简体   繁体   English

Python timeit.timeit在语句之间执行非定时操作

[英]Python timeit.timeit do untimed actions in between statements

I have a recursive function that has a functools.lru_cache() decorator on it. 我有一个具有functools.lru_cache()装饰器的递归函数。 I want to time the execution of it from an empty cache multiple times. 我想定时从一个空的缓存中多次执行它。 One way to do that is clear the cache every time, but this includes the cache clearing time. 一种方法是每次清除缓存,但这包括缓存清除时间。

This is what I currently have: 这是我目前拥有的:

@functools.lru_cache(maxsize=None)
def recursive_function(n):
    # ...
    return sum(recursive_function(n - i) * b for i, b in enumerate(other_list))

def time(number, n):
    return timeit.timeit(
        'f({}); f.cache_clear()'.format(n),
        'from {} import recursive_function as f'.format(__name__),
        number=number
    )

I still want to time it with a cache, as the cache reduces the time exponentially, but after it has been called once, calling it again takes ~0ms, as it just gets the cached value. 我还是想用一个缓存时间了,作为缓存成倍降低的时间,但在它已经被调用一次,再次调用它需要〜0毫秒,因为它只是获取缓存值。

Is there a way to do something between statements in timeit.timeit without it counting towards the time? 有没有一种方法可以在timeit.timeit语句之间timeit.timeit而不将其计入时间? Or to pause the timer before the cache_clear? 还是要在cache_clear之前暂停计时器?

You could produce number copies of the function, each with their independent lru_cache wrapper: 您可以产生该函数的number副本,每个副本都有其独立的 lru_cache包装器:

setup = '''\
    from {name} import recursive_function as f
    f = iter([
        functools.lru_cache(maxsize=None)(recursive_function.__wrapped__)
        for _ in range({number})])
    n = {n}
    next_ = next
'''.format(name=__name__, number=number, n=n)
test = '''\
    recursive_function = next_(f)
    recursive_funcion.__globals__['recursive_funcion'] = recursive_funcion
    recursive_function(n)
'''
return timeit.timeit(test, setup, number=number)

The setup creates number individually decorated function objects up front , each with a distinct LRU cache, and creates an iterator for this. 该安装程序在前面创建了number单独装饰的函数对象,每个对象都有一个不同的LRU缓存,并为此创建了一个迭代器。 The test then uses the next() function to obtain the next available function object, and uses that for the test. 然后,测试使用next()函数获取下一个可用的函数对象,并将其用于测试。

You do have to replace the current global name recursive_function each time however, as otherwise the recursive call won't find the new decorated version. 必须以取代目前的全球名称recursive_function每次然而,否则递归调用将找不到新装修的版本。 This is a bit of a downside, don't run the time trial and expect the cache to be empty afterwards (it'll contain the results of the very last test run instead). 这是一个缺点,不要运行时间试用版,并希望以后缓存为空(取而代之的是它将包含最后一次测试运行的结果)。

This works because: 之所以有效,是因为:

  1. the original un-cached function is still available as recursive_function.__wrapped__ 原始的未缓存函数仍然可以作为recursive_function.__wrapped__函数使用。
  2. decorator syntax is just syntactic sugar for a call to the decorator object to produce a new function object. 装饰器语法只是调用装饰器对象以产生新功能对象的语法糖。

This gives each individual test a clean cache, with minimal overhead (only a next() call, which has been bound to a local to avoid the global name lookup penalty). 这为每个测试提供了一个干净的缓存,并且开销最小(只有next()调用,该调用已绑定到本地以避免全局名称查找损失)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM