使用未使用的变量可以提高 20% 的速度?为什么?

Pyc*_*ath 5 python performance cpython python-internals

我正在做很多基准测试。我从来没有见过这样的事情。我很困惑。创建一个根本不使用的额外全局变量可以使我的部分代码速度提高约 20%。为什么?

\n

我正在对一个生成可迭代对象的函数进行基准测试,测量消耗(迭代)它们所需的时间。我有两种食用方式。我获得高 CPU 份额的典型时间:

\n
       | Without the variable | With the variable \n-------+----------------------+-------------------\nOutput |   0.74 s  consume_1  |  0.72 s  consume_1 \n       |   0.96 s  consume_2  |  0.77 s  consume_2 \n       |                      |                    \n       |   0.74 s  consume_1  |  0.75 s  consume_1 \n       |   0.96 s  consume_2  |  0.78 s  consume_2 \n       |                      |                    \n       |   0.73 s  consume_1  |  0.73 s  consume_1 \n       |   0.95 s  consume_2  |  0.78 s  consume_2 \n-------+----------------------+-------------------\nDebug  |  Real time: 5.110 s  | Real time: 4.560 s\n       |  User time: 4.546 s  | User time: 4.386 s\n       |  Sys. time: 0.535 s  | Sys. time: 0.150 s\n       |  CPU share: 99.43 %  | CPU share: 99.47 %\n
Run Code Online (Sandbox Code Playgroud)\n

创建无意义的变量使消耗速度consume_2加快了约 0.2 秒(从 0.97 到 0.77)。此外,“调试”统计数据也存在显着差异。最剧烈的是“系统时间”:对于“无”,它始终在 0.5 秒左右,对于“有”,它始终在 0.14 秒左右。

\n

我正在 TIO 上执行此操作,您可以自己在那里重现它:
\n不带变量/带变量

\n

这是代码,我称之为额外变量foobar。另请注意,consume_1加载全局变量deque10000 次,而consume_2仅加载少量全局变量,因此,如果有的话,我认为这consume_1将是受影响的。

\n
from timeit import timeit\nfrom operator import itemgetter\nfrom itertools import repeat\nfrom collections import deque\n\ndef each_with_others_1(iterable):\n    xs = tuple(iterable)\n    for i, x in enumerate(xs):\n        yield x, xs[:i] + xs[i+1:]\n\nconsume_0 = None\n\ndef consume_1(each_with_others):\n    for each, others in each_with_others:\n        deque(others, 0)\n\ndef consume_2(each_with_others):\n    otherss = map(itemgetter(1), each_with_others)\n    deque(map(deque, otherss, repeat(0)), 0)\n\nlst = list(range(10000))\nfoobar = None\nfor solver in [each_with_others_1] * 3:\n    for consume in consume_1, consume_2:\n        t = timeit(lambda: consume(solver(lst)), number=1)\n        print(\'%.2f s \' % t, consume.__name__)\n    print()\n
Run Code Online (Sandbox Code Playgroud)\n

更新:安装 Python 3.8.2 后,也在 Google Compute Engine 实例上重现,创建变量 madeconsume_2约 15%:

\n
       | Without the variable | With the variable \n-------+----------------------+-------------------\nOutput |   0.64 s  consume_1  |  0.65 s  consume_1 \n       |   0.80 s  consume_2  |  0.68 s  consume_2 \n       |                      |                    \n       |   0.64 s  consume_1  |  0.65 s  consume_1 \n       |   0.80 s  consume_2  |  0.68 s  consume_2 \n       |                      |                    \n       |   0.64 s  consume_1  |  0.64 s  consume_1 \n       |   0.78 s  consume_2  |  0.68 s  consume_2 \n-------+----------------------+-------------------\nDebug  |   real   0m 4.327s   |  real   0m 3.987s\n       |   user   0m 3.987s   |  user   0m 3.902s\n       |   sys    0m 0.340s   |  sys    0m 0.084s\n
Run Code Online (Sandbox Code Playgroud)\n

“调试”来自于将其称为time python test.py. 对于“无”,sys始终约为 0.32 秒。对于“with”,它始终在 0.09 秒左右。

\n