Timeout on a Python function call inside timeit

被刻印的时光 ゝ 提交于 2019-12-24 10:46:30

问题


I have a function, let's call it my_function(*args, **kwargs), that depending on the arguments passed to it takes anywhere from 30 seconds to many many hours (days). I have a list of different arguments and I want to know how long the functions will take given the arguments.

I am still very new to using timeit, but I have learn enough to do this part; however, this wouldn't be the very efficient for my purposes. Any set of arguments that take longer than 4 hours to complete are consider intractable, despite all of them being able to be solved in "finite" time. As some (possibly most) of the arguments will results in run times that will take upwards of 20+ hours, I am looking for a way to terminate a test after 4 hours so that I don't have to waste time after figuring out that it is intractable.

I have looked at Timeout on a Python function call and Stop code after time period, and this might be close enough of a question to be a duplicate, but I am having trouble integrating those answers with timeit so that times less that 4 hours are recorded like they should be while long runs return some valid time greater than 4 hours.

What would be the best way to go about doing this?

EDIT : One of the problems that I have had with it is that the answers that I've seen have take in func(*args,**kwargs) while the timeit functions looks like this:

timeit.Timer('my_function(*args, **kwargs)', setup=setup).timeit(1)

I don't know how to handle this form.

EDIT : The original answer that I provided below using threads doesn't actually terminate the threads. This can easily be shown by running it with the following function.

def foo(x):
    for i in xrange(1, x + 1):
        sleep(1)
        print i
    return x

Using a code that involves multiprocessing.Pool, which actually has a terminate(), allow for this.


回答1:


Based on the answer found in Timeout function using threading in python does not work. If you try it out on foo(x) it does indeed stop counting unlike the my previous answer.

import multiprocessing as mp
import timeit

def timeout(func, args=(), kwargs=None, TIMEOUT=10, default=None, err=.05):

    if hasattr(args, "__iter__") and not isinstance(args, basestring):
        args = args
    else:
        args = [args]    
    kwargs = {} if kwargs is None else kwargs

    pool = mp.Pool(processes = 1)

    try:
        result = pool.apply_async(func, args=args, kwds=kwargs)
        val = result.get(timeout = TIMEOUT * (1 + err))
    except mp.TimeoutError:
        pool.terminate()
        return default
    else:
        pool.close()
        pool.join()
        return val

def Timeit(command, setup=''):
    return timeit.Timer(command, setup=setup).timeit(1)

def timeit_timeout(command, setup='', TIMEOUT=10, default=None, err=.05):
    return timeout(Timeit, args=command, kwargs={'setup':setup},
                   TIMEOUT=TIMEOUT, default=default, err=err) 



回答2:


After some more fiddling I have a initial answer based on Timeout function using threading. I would still love to hear from anyone who has better ideas as I am still new to this.

def timeout(func, args=None, kwargs=None, TIMEOUT=10, default=None, err=.05):
    if args is None:
        args = []
    elif hasattr(args, "__iter__") and not isinstance(args, basestring):
        args = args
    else:
        args = [args]

    kwargs = {} if kwargs is None else kwargs

    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = None

        def run(self):
            try:
                self.result = func(*args, **kwargs)
            except:
                self.result = default

    it = InterruptableThread()
    it.start()
    it.join(TIMEOUT* (1 + err))
    if it.isAlive():
        return default
    else:
        return it.result

def timeit_timeout(command, setup='', TIMEOUT=10, default=None, err=.05):
    import timeit
    f = lambda: timeit.Timer(command, setup=setup).timeit(1)
    return timeout(f,TIMEOUT=TIMEOUT, default=default, err=err) 

if __name__ == '__main__':    
    from time import sleep
    setup = 'gc.enable()\nfrom time import sleep'
    for n in range(1,15):
        command = 'sleep({})'.format(n)
        print timeit_timeout(command, setup=setup, default=float('Inf'))


来源:https://stackoverflow.com/questions/20360795/timeout-on-a-python-function-call-inside-timeit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!