[英]Python: What is the difference between a global variable, vs. a variable with the prefix “self.”, vs. a local variable?
[英]global vs. local namespace performance difference
为什么在函数中执行一组命令:
def main():
[do stuff]
return something
print(main())
在python中运行速度比在顶级执行命令要快1.5x
到3x
倍:
[do stuff]
print(something)
差异确实很大程度上取决于“做事”实际上做了什么, 主要取决于它访问定义/使用的名称的次数。 假设代码类似,这两种情况之间存在根本区别:
LOAD_FAST
/ STORE_FAST
完成加载/存储名称的字节代码。 LOAD_NAME
/ STORE_NAME
执行相同的命令,这些命令更加缓慢。 这可以在以下情况下查看, 我将使用for
循环来确保定义的变量的查找多次执行 。
功能和LOAD_FAST/STORE_FAST
:
我们定义了一个简单的函数来做一些非常愚蠢的事情:
def main():
b = 20
for i in range(1000000): z = 10 * b
return z
dis.dis
生成的输出:
dis.dis(main)
# [/snipped output/]
18 GET_ITER
>> 19 FOR_ITER 16 (to 38)
22 STORE_FAST 1 (i)
25 LOAD_CONST 3 (10)
28 LOAD_FAST 0 (b)
31 BINARY_MULTIPLY
32 STORE_FAST 2 (z)
35 JUMP_ABSOLUTE 19
>> 38 POP_BLOCK
# [/snipped output/]
这里要注意的是偏移28
和32
处的LOAD_FAST/STORE_FAST
命令,这些命令用于访问BINARY_MULTIPLY
操作中使用的b
名称并分别存储z
名称。 正如它们的字节代码名称所暗示的那样, 它们是 LOAD_*/STORE_*
系列的快速版本 。
模块和LOAD_NAME/STORE_NAME
:
现在,让我们看看上一个函数的模块版本的dis
输出:
# compile the module
m = compile(open('main.py', 'r').read(), "main", "exec")
dis.dis(m)
# [/snipped output/]
18 GET_ITER
>> 19 FOR_ITER 16 (to 38)
22 STORE_NAME 2 (i)
25 LOAD_NAME 3 (z)
28 LOAD_NAME 0 (b)
31 BINARY_MULTIPLY
32 STORE_NAME 3 (z)
35 JUMP_ABSOLUTE 19
>> 38 POP_BLOCK
# [/snipped output/]
在这里,我们有多个LOAD_NAME/STORE_NAME
调用, LOAD_NAME/STORE_NAME
, 这些调用执行起来比较迟钝 。
在这种情况下, 执行时间会有明显的差异 ,主要是因为Python必须多次评估LOAD_NAME/STORE_NAME
和LOAD_FAST/STORE_FAST
(由于我添加了for
循环),因此每次都会引入开销执行每个字节代码的代码将累积 。
将执行“定位为模块”:
start_time = time.time()
b = 20
for i in range(1000000): z = 10 *b
print(z)
print("Time: ", time.time() - start_time)
200
Time: 0.15162253379821777
将执行时间定位为函数:
start_time = time.time()
print(main())
print("Time: ", time.time() - start_time)
200
Time: 0.08665871620178223
如果你time
在一个较小的循环range
(例如for i in range(1000)
你会发现,“模块”的版本速度更快。 这是因为需要调用函数main()
引入的开销大于*_FAST
vs *_NAME
*_FAST
差异引入的*_FAST
。 所以它在很大程度上取决于完成的工作量。
所以,这里真正的罪魁祸首,以及这种差异显而易见的原因是使用了for
循环。 你通常有0
理由在脚本的顶层放置一个密集的循环。 在函数中移动它并避免使用全局变量 ,它被设计为更高效。
您可以查看为每个字节代码执行的代码。 我会在这里链接3.5
版Python的源代码,尽管我很确定2.7
没有太大差别。 字节码评估在Python/ceval.c
完成,特别是在函数PyEval_EvalFrameEx
:
正如您将看到的, *_FAST
字节码只是使用框架对象中包含的fastlocals
本地符号表来获取存储/加载的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.