Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 用于循环项目解包_Python_Performance_For Loop_Iterable Unpacking - Fatal编程技术网

Python 用于循环项目解包

Python 用于循环项目解包,python,performance,for-loop,iterable-unpacking,Python,Performance,For Loop,Iterable Unpacking,有一次,在看了Mike Muller的性能优化教程(我想)后,我的脑海中开始浮现出一个想法:如果性能很重要,那么通过索引最小化对循环中项目的访问,例如。G如果您需要在循环中多次访问x[1],对于l中的x,请为x[1]分配一个变量,并在循环中重复使用它 现在我有一个合成的例子: import timeit SEQUENCE = zip(range(1000), range(1, 1001)) def no_unpacking(): return [item[0] + item[1] f

有一次,在看了Mike Muller的性能优化教程(我想)后,我的脑海中开始浮现出一个想法:如果性能很重要,那么通过索引最小化对循环中项目的访问,例如。G如果您需要在循环中多次访问
x[1]
,对于l中的x,请为
x[1]
分配一个变量,并在循环中重复使用它

现在我有一个合成的例子:

import timeit

SEQUENCE = zip(range(1000), range(1, 1001))

def no_unpacking():
    return [item[0] + item[1] for item in SEQUENCE]


def unpacking():    
    return [a + b for a, b in SEQUENCE]


print timeit.Timer('no_unpacking()', 'from __main__ import no_unpacking').timeit(10000)
print timeit.Timer('unpacking()', 'from __main__ import unpacking').timeit(10000)
unpacking()
no_unpacking()
函数返回相同的结果。实现是不同的:
unpacking()
将项目解包到循环中的
a
b
no_unpacking()
通过索引获取值

对于python27,它显示:

1.25280499458
0.946601867676
换句话说,
unpacking()
的性能比
no\u unpacking()
高出约25%

问题是:

  • 为什么通过索引访问会显著降低速度(即使在这种简单的情况下)
奖金问题:

  • 我也在
    pypy
    上尝试过这一点-从性能上看,这两个函数几乎没有区别。为什么呢

感谢您的帮助。

要回答您的问题,我们可以使用
dis
模块检查这两个函数生成的字节码:

In [5]: def no_unpacking():
   ...:     s = []
   ...:     for item in SEQUENCE:
   ...:         s.append(item[0] + item[1])
   ...:     return s
   ...: 
   ...: 
   ...: def unpacking():  
   ...:     s = []
   ...:     for a,b in SEQUENCE:
   ...:         s.append(a+b)  
   ...:     return s
我扩展了对列表的理解,因为在python3中,检查有趣的字节码会更麻烦。代码是等效的,因此它对我们的目的并不重要

第一个函数的字节码为:

In [6]: dis.dis(no_unpacking)
  2           0 BUILD_LIST               0 
              3 STORE_FAST               0 (s) 

  3           6 SETUP_LOOP              39 (to 48) 
              9 LOAD_GLOBAL              0 (SEQUENCE) 
             12 GET_ITER             
        >>   13 FOR_ITER                31 (to 47) 
             16 STORE_FAST               1 (item) 

  4          19 LOAD_FAST                0 (s) 
             22 LOAD_ATTR                1 (append) 
             25 LOAD_FAST                1 (item) 
             28 LOAD_CONST               1 (0) 
             31 BINARY_SUBSCR        
             32 LOAD_FAST                1 (item) 
             35 LOAD_CONST               2 (1) 
             38 BINARY_SUBSCR        
             39 BINARY_ADD           
             40 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             43 POP_TOP              
             44 JUMP_ABSOLUTE           13 
        >>   47 POP_BLOCK            

  5     >>   48 LOAD_FAST                0 (s) 
             51 RETURN_VALUE      
In [7]: dis.dis(unpacking)
  9           0 BUILD_LIST               0 
              3 STORE_FAST               0 (s) 

 10           6 SETUP_LOOP              37 (to 46) 
              9 LOAD_GLOBAL              0 (SEQUENCE) 
             12 GET_ITER             
        >>   13 FOR_ITER                29 (to 45) 
             16 UNPACK_SEQUENCE          2 
             19 STORE_FAST               1 (a) 
             22 STORE_FAST               2 (b) 

 11          25 LOAD_FAST                0 (s) 
             28 LOAD_ATTR                1 (append) 
             31 LOAD_FAST                1 (a) 
             34 LOAD_FAST                2 (b) 
             37 BINARY_ADD           
             38 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             41 POP_TOP              
             42 JUMP_ABSOLUTE           13 
        >>   45 POP_BLOCK            

 12     >>   46 LOAD_FAST                0 (s) 
             49 RETURN_VALUE 
注意,循环必须调用
BINARY\u SUBSCR
两次才能访问元组的两个元素

第二个函数的字节码是:

In [6]: dis.dis(no_unpacking)
  2           0 BUILD_LIST               0 
              3 STORE_FAST               0 (s) 

  3           6 SETUP_LOOP              39 (to 48) 
              9 LOAD_GLOBAL              0 (SEQUENCE) 
             12 GET_ITER             
        >>   13 FOR_ITER                31 (to 47) 
             16 STORE_FAST               1 (item) 

  4          19 LOAD_FAST                0 (s) 
             22 LOAD_ATTR                1 (append) 
             25 LOAD_FAST                1 (item) 
             28 LOAD_CONST               1 (0) 
             31 BINARY_SUBSCR        
             32 LOAD_FAST                1 (item) 
             35 LOAD_CONST               2 (1) 
             38 BINARY_SUBSCR        
             39 BINARY_ADD           
             40 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             43 POP_TOP              
             44 JUMP_ABSOLUTE           13 
        >>   47 POP_BLOCK            

  5     >>   48 LOAD_FAST                0 (s) 
             51 RETURN_VALUE      
In [7]: dis.dis(unpacking)
  9           0 BUILD_LIST               0 
              3 STORE_FAST               0 (s) 

 10           6 SETUP_LOOP              37 (to 46) 
              9 LOAD_GLOBAL              0 (SEQUENCE) 
             12 GET_ITER             
        >>   13 FOR_ITER                29 (to 45) 
             16 UNPACK_SEQUENCE          2 
             19 STORE_FAST               1 (a) 
             22 STORE_FAST               2 (b) 

 11          25 LOAD_FAST                0 (s) 
             28 LOAD_ATTR                1 (append) 
             31 LOAD_FAST                1 (a) 
             34 LOAD_FAST                2 (b) 
             37 BINARY_ADD           
             38 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             41 POP_TOP              
             42 JUMP_ABSOLUTE           13 
        >>   45 POP_BLOCK            

 12     >>   46 LOAD_FAST                0 (s) 
             49 RETURN_VALUE 
请注意,没有要执行的
二进制\u SUBSCR

因此,
UNPACK\u SEQUENCE
加上一个
STORE\u FAST
(这是解包添加的额外操作)似乎比执行两个
BINARY\u SUBSCR
更快。 这是合理的,因为
BINARY\u SUBSCR
是一个完整的方法调用,而
UNPACK\u SEQUENCE
STORE\u FAST
是更简单的操作

即使在更简单的情况下,您也可以看到差异:

In [1]: def iter_with_index(s):
   ...:     for i in range(len(s)):
   ...:         s[i]
   ...:         

In [2]: def iter_without_index(s):
   ...:     for el in s:el
   ...:     

In [3]: %%timeit s = 'a' * 10000
   ...: iter_with_index(s)
   ...: 
1000 loops, best of 3: 583 us per loop

In [4]: %%timeit s = 'a' * 10000
   ...: iter_without_index(s)
   ...: 
1000 loops, best of 3: 206 us per loop
正如您所看到的,使用显式索引对字符串进行迭代大约要慢3倍。 这是由于调用
BINARY\u SUBSCR
而产生的所有开销

关于第二个问题:pypy具有JIT,它能够分析代码并生成优化版本,从而避免索引操作的开销。
当它意识到订阅是在元组上完成的时,它可能能够生成不调用元组方法但直接访问元素的代码,从而完全删除
二进制\u SUBSCR
操作。

很好的解释,很高兴知道我没有无缘无故地担心。回答很好。很彻底,解释得很好。然而,只要将
range
更改为
xrange
,我笔记本上的速度就从4.0us下降到了3.43us