Python 为什么一种记忆策略比另一种慢?
所以这本关于回忆录的书让我很好奇。我运行自己的基准测试 1) 可变默认字典: 输出: 2) 同样的想法,但遵循“请求原谅比允许更容易”的原则: 输出: 我的问题Python 为什么一种记忆策略比另一种慢?,python,fibonacci,memoization,python-decorators,Python,Fibonacci,Memoization,Python Decorators,所以这本关于回忆录的书让我很好奇。我运行自己的基准测试 1) 可变默认字典: 输出: 2) 同样的想法,但遵循“请求原谅比允许更容易”的原则: 输出: 我的问题 为什么2)比1)慢 编辑 正如@kevin在评论中所暗示的,我把装饰师搞错了,所以我把它拆了。剩余部分仍然有效!(我希望)捕获异常意味着堆栈跟踪可能非常昂贵: 例外情况在两种情况下非常有效: 试试看。。。最后 试试看。。。除非,前提是不引发异常 但是,当发生异常并捕获所需的堆栈跟踪时 增加了巨大的开销 方法一总共进行三次查找(n不
- 为什么2)比1)慢
试试看。。。最后
试试看。。。除非
,前提是不引发异常增加了巨大的开销 方法一总共进行三次查找(
n不在dic中:
,插入dic[n]=
并返回dic[n]
)。第二种方法在最坏情况下也进行三次查找(检索尝试dic[n]=
、插入dic[n]=
和返回dic[n]
),此外还涉及异常处理
如果一种方法与另一种方法做相同的工作,并且添加了一些东西,那么它显然不会更快,而且很可能会更慢
当记忆有更多有用的机会时,考虑比较场景中的效率——即多次运行函数,以比较分摊的复杂性。这样,第二种方法的最坏情况发生的频率就会降低,并且您可以从一次查找中获得更少的收益
版本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--“导入版本1”1000000个循环,最好3个:每个循环1.64 usec C:\Users\Bartek\Documents\Python>Python-m timeit--“导入版本2”
1000000个循环,最好3个:每个循环1.6 usec
当函数被更频繁地使用时,缓存会被更多的值填充,从而降低异常的可能性。这是一个非常不寻常的装饰器。通常,当您装饰一个函数时,在装饰器中的某个地方,您会在完成一些工作后调用该函数。但是你的装饰者从不调用
f
。当你修饰fibo
时,你实际上在做“扔掉这个函数的定义,用示例1中的函数替换它”捕获异常(这意味着堆栈跟踪)可能非常昂贵:@DmitryBychenko我想你应该把它作为一个答案发布。原则“请求原谅比允许更容易”在哪里来自哪里0在编程上下文中听起来很不合理。@BartoszKP这是真的。大多数时候,用户可能会给你正确的东西。这就是为什么最好假设他们给了你正确的东西,如果异常是错误的,就处理它。这样就避免了hasattr
或如果输入d:
的开销。这个原则经常使用的另一个领域是在尝试使用键/属性之前检查它们是否存在,这(有时)更可能被原谅。
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
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)
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)