我运行2个功能。它们都有for循环来执行指令。这两个功能完成相同的任务,但需要更长的时间。
函数1执行并且是自包含的,执行TaskA。
f1:
For x in X:
do task a
功能2执行并调用功能3.功能3执行TaskA
f2:
For x in X:
call function 3
f3:
do task a
为什么函数2的执行时间通常是函数1的10倍?
编辑:以前的措辞让人感到困惑。答案 0 :(得分:1)
另一个因素可能是"准备" / setup在调用internal
之前完成。可能在TaskA
您已经在f1
循环之前完成了一次,然后在for
中完成了它,因此每个f3
都会调用它x in X
而不是一开始就是一次。没有任何真实的代码,很难说。
至于为每个f2
调用f3
的潜在复杂性,这不太可能导致10x缓慢。
只有在使用x
的过于简单的示例中,我们才会看到此行为。让我们来看看pass
,f1
和f2
的这3个不良版本:
f3
使用>>> def f1():
... for x in X:
... pass
...
>>> def f2():
... for x in X:
... f3()
...
>>> def f3():
... pass
...
,这里是dis
的字节码:
f1
... vs >>> dis.dis(f1)
2 0 SETUP_LOOP 14 (to 17)
3 LOAD_GLOBAL 0 (X)
6 GET_ITER
>> 7 FOR_ITER 6 (to 16)
10 STORE_FAST 0 (x)
3 13 JUMP_ABSOLUTE 7
>> 16 POP_BLOCK
>> 17 LOAD_CONST 0 (None)
20 RETURN_VALUE
:
f2
除了>>> dis.dis(f2)
2 0 SETUP_LOOP 21 (to 24)
3 LOAD_GLOBAL 0 (X)
6 GET_ITER
>> 7 FOR_ITER 13 (to 23)
10 STORE_FAST 0 (x)
3 13 LOAD_GLOBAL 1 (f3)
16 CALL_FUNCTION 0
19 POP_TOP
20 JUMP_ABSOLUTE 7
>> 23 POP_BLOCK
>> 24 LOAD_CONST 0 (None)
27 RETURN_VALUE
和CALL_FUNCTION
之外,它们看起来几乎相同。但是,它们与POP_TOP
非常不同:
timeit
现在已经8倍了,但不是因为调用函数慢,而是因为在>>> X = range(1000) # [0, 1, 2, ...999]
>>>
>>> import timeit
>>> timeit.timeit(f1)
10.290941975496747
>>> timeit.timeit(f2)
81.18860785875617
>>>
的{{1}}中只做了pass
for循环是非常快,尤其是每次调用函数然后什么都不做的时候。所以希望你不使用这些作为例子来找出/想知道为什么。
现在,如果您在任务中实际做某事,比如说f1
,那么您会看到两者之间的时间/性能差异变得更小:
x * x
现在只有 2.9x 的时间。它不是导致缓慢的函数调用(是的,有一些开销),而是你在该函数中所做的事情与>>> def f1():
... for x in X:
... _ = x*x
...
>>> def f2():
... for x in X:
... _ = f3(x) # didn't pass in `x` to `f3` in the previous example
...
>>> def f3(x):
... return x*x
...
>>> timeit.timeit(f1)
38.76545268807092
>>> timeit.timeit(f2)
113.72242594670047
>>>
相比,这会对总体时间产生影响。
如果您在两个地方都将pass
替换为_ = x * x
,这是相当"慢",只有print x * x
:
X = range(5)
现在他们的表现差别要小得多。
所以用实际代码进行实际检查,而不仅仅是简单的伪代码分析。空调用可能看起来更快,但与函数中的代码较慢的东西相比,开销非常小。