【问题标题】:Memoization decorator runs slower than original function记忆装饰器运行速度比原始功能慢
【发布时间】:2018-02-01 15:30:46
【问题描述】:

我正在使用斐波那契函数进行记忆装饰器练习。当输入变大时,memoized 函数应该会更快,因为它从字典中返回结果,而不是再次计算结果。

我正在使用timeit.timeit() 来测量函数的执行时间,其中memoized on vs off。 我得到的结果与我的预期完全相反。没有装饰器的执行运行得更快。

# memorized decorator for fibonacci series
def mem_fib(f):
    def wrapper(n):
        wrapper.d = {}  # create the attr member of THIS wrapper
        if n in wrapper.d:
            return wrapper.d[n]
        wrapper.d[n] = f(n)  # save f() return in a dict
        return wrapper.d[n]
    return wrapper

@mem_fib
def fibonacci(n):
    assert n >= 0
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

我正在 PyCharm python 的控制台上运行命令。

@with 装饰器

>>> print(timeit.timeit('decorators.fibonacci(7)', setup='import decorators'))
19.6940833939
>>> print(timeit.timeit('decorators.fibonacci(10)', setup='import decorators'))
85.7157191166

没有装饰器

>>> print(timeit.timeit('decorators.fibonacci(7)', setup='import decorators'))
5.10131571594
>>> print(timeit.timeit('decorators.fibonacci(10)', setup='import decorators'))
21.9784012801

我多次运行timeit,我只是放一个输出来总结它。我错过了什么?

谢谢

更新:感谢丹尼尔的回答,我发现了我的错误。我将字典创建移到了包装器之外,结果要好得多。

>>> print(timeit.timeit('decorators.fibonacci(10)', setup='import decorators'))
0.248986574759

【问题讨论】:

  • 您是否已经厌倦了追求更大的价值?我的猜测是,对于 7 和 10,memoization 引入的少量开销大于实际计算的成本。我想对于 fibbonacci(100) 它可能会产生预期的结果。
  • 会不会是因为你的断言声明?作为旁注,您不需要最后的 pass 语句。您可以检查负 n 并引发错误,然后检查基本情况并返回。如果两个检查都失败了,一定是递归的情况,因此不需要使用else。
  • 麦这个小改动让代码更容易阅读,感谢注释

标签: python decorator


【解决方案1】:

您每次调用该函数时都会创建一个新字典,因为您的 wrapper 函数总是执行 wrapper.d = {}。因此缓存永远不会被填充,并且您的代码每次都会产生额外的创建字典的开销。

在您从 mem_fib 返回之前,该行应该超出该函数。

【讨论】:

  • 我不知道我是怎么错过的,谢谢!当我将字典创建移到包装器之外(在 mem_fib 内)时,结果与我预期的一样
  • print(timeit.timeit('decorators.fibonacci(10)', setup='import decorators')) 0.248986574759
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-15
  • 1970-01-01
  • 2012-12-25
  • 2020-08-06
  • 2015-05-25
  • 2018-06-14
  • 2019-04-23
相关资源
最近更新 更多