car*_*995 3 python function python-3.x python-internals
函数调用总会产生一些开销.但为什么下面的代码显示非函数调用较慢.
import time
def s():
for i in range(1000000000):
1 + 1
t = time.time()
s()
print("Function call: " + str(time.time() - t))
t = time.time()
for i in range(1000000000):
1 + 1
print("Non function call: " + str(time.time() - t))
Run Code Online (Sandbox Code Playgroud)
Function call: 38.39736223220825
Non function call: 60.33238506317139
Run Code Online (Sandbox Code Playgroud)
Jim*_*ard 11
你可能会认为,因为循环只是这样1 + 1,所以应该没有太大区别.但是,这里有一个"隐藏"的赋值,它通常被遗忘:循环i中的循环变量for.这是经济放缓的原因.
在函数中,这是完成的STORE_FAST.在顶层,完成了STORE_NAME.第一个比另一个快,并且在一个运行1000000000时间的循环中,这个差异非常清楚地显示出来.
请记住,函数调用只发生一次.因此,它的开销在这种特定情况下并没有真正贡献.
除此之外,所有其他步骤只发生一次并且几乎相同.创建一个范围并抓取其迭代器,并2为每次迭代加载常量.
您始终可以使用该dis模块检查为每个生成的CPython字节码,如@Moses在注释中指示的那样.对于该功能s,您有:
dis.dis(s)
# snipped for brevity
>> 10 FOR_ITER 8 (to 20)
12 STORE_FAST 0 (i)
3 14 LOAD_CONST 3 (2)
16 POP_TOP
18 JUMP_ABSOLUTE 10
Run Code Online (Sandbox Code Playgroud)
而对于循环的顶级版本:
dis('for i in range(1000000000): 1+1')
# snipped for brevity
>> 10 FOR_ITER 8 (to 20)
12 STORE_NAME 1 (i)
14 LOAD_CONST 3 (2)
16 POP_TOP
18 JUMP_ABSOLUTE 10
Run Code Online (Sandbox Code Playgroud)
它们之间的主要区别在于存储迭代值i.在功能方面,它更有效率.
为了解决@Reblochon Masque(现已删除)的答案,这似乎表明这两者timeit在IPython单元格中时没有差异.
timeit通过创建一个小函数(命名inner)来计算事物,该函数存储您传递的语句并为给定数量的执行执行它们.你可以看到这个,如果你创建一个Timer对象并查看它的src属性(这没有记录,所以不要指望它总是在那里:-):
from timeit import Timer
t = Timer('for i in range(10000): 1 + 1')
print(t.src)
Run Code Online (Sandbox Code Playgroud)
这包含基本上定时的小功能.上一个print电话打印:
def inner(_it, _timer):
pass
_t0 = _timer()
for _i in _it:
for i in range(10000): 1 + 1
_t1 = _timer()
return _t1 - _t0
Run Code Online (Sandbox Code Playgroud)
所以,实际上,通过使用timeit你已经改变了执行查找的方式i,因为它在函数内部也是如此STORE_FAST.容易陷入困境!
(如果你不相信我,请看dis.dis(compile(t.src, '', 'exec').co_consts[0]))