为什么一个记忆策略比另一个慢?

时间:2014-08-25 12:14:10

标签: python fibonacci memoization python-decorators

所以关于回忆的这个page让我很好奇。我运行了自己的基准测试。

1)可变默认字典:

%%timeit
def fibo(n, dic={}) :
    if n not in dic :
        if n in (0,1) :
            dic[n] = 1
        else :
            dic[n] = fibo(n-1)+fibo(n-2)
    return dic[ n ]
fibo(30)

输出:

100000 loops, best of 3: 18.3 µs per loop

2)同样的想法,但遵循“更容易请求宽恕而不是许可”的原则:

In [21]:

%%timeit
def fibo(n, dic={}) :
    try :
        return dic[n]
    except :
        if n in (0,1) :
            dic[n] = 1
        else :
            dic[n] = fibo(n-1)+fibo(n-2)
        return dic[ n ]
fibo(30)

输出:

10000 loops, best of 3: 46.8 µs per loop

我的问题

  • 为什么2)与1)相比这么慢?

修改

正如@kevin在评论中所说,我让装饰者完全错了所以我删除了它。其余的仍然有效! (我希望)

2 个答案:

答案 0 :(得分:6)

捕获异常意味着堆栈跟踪可能非常昂贵

https://docs.python.org/2/faq/design.html#how-fast-are-exceptions

在两种情况下,例外非常有效:

  1. try ... finally
  2. try ... except,前提是不会抛出任何异常
  3. 但是,当异常发生并捕获所需的堆栈跟踪时 增加了很多开销。

答案 1 :(得分:0)

方法一共进行三次查找(n not in dic:,插入dic[n] =并返回dic[n])。第二种方法还在最坏情况(检索尝试dic[n] =,插入dic[n] =和返回dic[n])中进行了三次查找,并且还涉及异常处理。 / p>

如果一种方法与另一种方法完成相同的工作,并添加了一些东西,它显然不会更快,而且很可能会更慢。

考虑比较备忘录有更多场合有用的场景中的效率 - 即多次运行该函数,以比较摊销的复杂性。这样,第二种方法的最坏情况将不常发生,并且您可以从一次查找中获得更少的东西。

版本1:

def fibo(n, dic={}) :
    if n not in dic :
        if n in (0,1) :
            dic[n] = 1
        else :
            dic[n] = fibo(n-1)+fibo(n-2)
    return dic[ n ]

for i in range(10000):
    fibo(i)

版本2:

def fibo(n, dic={}) :
    try :
        return dic[n]
    except :
        if n in (0,1) :
            dic[n] = 1
        else :
            dic[n] = fibo(n-1)+fibo(n-2)
        return dic[ n ]

for i in range(10000):
    fibo(i)

测试:

  

C:\ Users \ Bartek \ Documents \ Python> python -m timeit - “import version1”
  1000000循环,最佳3:1.64每循环使用

     

C:\ Users \ Bartek \ Documents \ Python> python -m timeit - “import version2”
  1000000循环,最佳3:1.6每循环usec

当更频繁地使用该函数时,缓存会填充更多值,这反过来会降低异常的可能性。