python - 函数调用执行速度比非函数调用快

标签 python python-3.x function 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))

输出:

Function call: 38.39736223220825
Non function call: 60.33238506317139

最佳答案

您可能会想,既然循环只执行 1 + 1,那么应该没有太大区别。 但是,有一个“隐藏的”assignment这里通常被遗忘的是:for 循环中的循环变量 i。这是放缓的原因。

在函数中,这是通过 STORE_FAST 完成的.在顶层,它是用 STORE_NAME 完成的.第一个比另一个更快,并且在运行 1000000000 次的循环中,这种差异非常明显。

请记住,函数调用仅发生一次。所以它的开销在这个特定场景中并没有真正贡献。

除此之外,所有其他步骤只发生一次并且几乎相同。创建一个范围并获取其迭代器,并为每次迭代加载常量 2


您始终可以使用 dis module检查为其中每一个生成的 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

虽然对于循环的顶层版本:

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

它们之间的主要区别在于迭代值 i 的存储。在函数中,它的效率更高。


解决@Reblochon Masque(现已删除)的答案,当在 IPython 单元格中使用 timeit 计时时,这两者之间似乎没有差异。

timeit 通过创建一个小函数 ( named inner ) 对事物进行计时,该函数存储您传递的语句并按给定的执行次数执行它们。如果您创建一个 Timer 对象并查看它的 src 属性,您可以看到这一点(这没有记录,所以不要期望它总是存在 :-):

from timeit import Timer

t = Timer('for i in range(10000): 1 + 1')
print(t.src)

这包含了本质上是定时的小函数。之前的 print 调用打印:

def inner(_it, _timer):
    pass
    _t0 = _timer()
    for _i in _it:
        for i in range(10000): 1 + 1
    _t1 = _timer()
    return _t1 - _t0

因此,实际上,通过使用 timeit,您已经改变了执行查找 i 的方式,因为它在函数内部,所以它也是通过 STORE_FAST。容易犯错!

(如果您不相信我,请参阅 dis.dis(compile(t.src, '', 'exec').co_consts[0]) )

关于python - 函数调用执行速度比非函数调用快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47596122/

相关文章:

python - 对象列表功能不起作用

mysql - PHP PDO 简洁的 mySQL SELECT 对象

Swift 初学者需要详细说明语法 - 传递和返回函数

python - 如何将 numpy 数组中的特定元素转换为整数?

python - 如何确保报告管道中的错误

python - Pip 使用 virtualenv 在错误的目录中安装包

python-3.x - 使用 ALSA 在 RaspBerry Pi 4+ 上使用 PyGame 的音频 cdrom 没有声音

python - 新的 sympy 交叉点用法

python - 按另一个列表中项目的存在对列表进行排序

python - 过滤直方图边缘和计数