scatter用法 - python直方图横坐标
为什么Python代码在函数中运行得更快? (2)
你可能会问为什么存储局部变量比全局变量更快。 这是一个CPython实现细节。
请记住,CPython被编译为解释器运行的字节码。 编译函数时,局部变量存储在一个固定大小的数组中( 而不是 dict
),变量名称被分配给索引。 这是可能的,因为你不能动态添加局部变量到一个函数。 然后检索一个局部变量实际上是一个指向查找列表的指针,并且PyObject
上的一个refcount增加是微不足道的。
将其与全局查找( LOAD_GLOBAL
)进行对比,该查询是包含哈希等的真正dict
搜索。 顺便说一句,这就是为什么你需要指定global i
如果你希望它是全局的:如果你曾经指定一个范围内的变量,编译器将发出STORE_FAST
s的访问,除非你不告诉它。
顺便说一下,全局查找仍然非常优化。 属性查找foo.bar
是非常慢的!
这里是关于局部变量效率的小illustration 。
def main():
for i in xrange(10**8):
pass
main()
Python中的这段代码运行在(注意:时序是在Linux的BASH中的时间函数中完成的。)
real 0m1.841s
user 0m1.828s
sys 0m0.012s
但是,如果for循环未放在函数中,
for i in xrange(10**8):
pass
那么它会运行更长的时间:
real 0m4.543s
user 0m4.524s
sys 0m0.012s
为什么是这样?
在函数内部,字节码是
2 0 SETUP_LOOP 20 (to 23)
3 LOAD_GLOBAL 0 (xrange)
6 LOAD_CONST 3 (100000000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 6 (to 22)
16 STORE_FAST 0 (i)
3 19 JUMP_ABSOLUTE 13
>> 22 POP_BLOCK
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE
在顶层,字节码是
1 0 SETUP_LOOP 20 (to 23)
3 LOAD_NAME 0 (xrange)
6 LOAD_CONST 3 (100000000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 6 (to 22)
16 STORE_NAME 1 (i)
2 19 JUMP_ABSOLUTE 13
>> 22 POP_BLOCK
>> 23 LOAD_CONST 2 (None)
26 RETURN_VALUE
不同之处在于STORE_FAST
比STORE_NAME
更快(!)。 这是因为在一个函数中, i
是一个本地的,但在顶层是一个全局的。