[英]Why list comprehension can be faster than map() in Python?
I am looking in to the performance issues of the loop like structures in Python and found the following statements: 我正在研究Python中循环结构的性能问题,并发现以下语句:
Besides the syntactic benefit of list comprehensions, they are often as fast or faster than equivalent use of map.
除了列表推导的句法益处之外,它们通常比等效使用map更快或更快。 ( Performance Tips )
( 性能提示 )
List comprehensions run a bit faster than equivalent for-loops (unless you're just going to throw away the result).
列表推导比等效的for循环运行得快一点(除非你只是扔掉结果)。 ( Python Speed )
( Python速度 )
I am wondering what difference under the hood gives list comprehension this advantage. 我想知道引擎盖下有什么区别可以让列表理解这个优势。 Thanks.
谢谢。
Test one : throwing away the result. 测试一 :扔掉结果。
Here's our dummy function: 这是我们的虚拟功能:
def examplefunc(x):
pass
And here are our challengers: 以下是我们的挑战者:
def listcomp_throwaway():
[examplefunc(i) for i in range(100)]
def forloop_throwaway():
for i in range(100):
examplefunc(i)
I won't do an analysis of its raw speed, only why , per the OP's question. 根据OP的问题,我不会对其原始速度进行分析,只是为什么 。 Lets take a look at the diffs of the machine code.
让我们来看看机器代码的差异。
--- List comprehension
+++ For loop
@@ -1,15 +1,16 @@
- 55 0 BUILD_LIST 0
+ 59 0 SETUP_LOOP 30 (to 33)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (100)
9 CALL_FUNCTION 1
12 GET_ITER
- >> 13 FOR_ITER 18 (to 34)
+ >> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (i)
- 19 LOAD_GLOBAL 1 (examplefunc)
+
+ 60 19 LOAD_GLOBAL 1 (examplefunc)
22 LOAD_FAST 0 (i)
25 CALL_FUNCTION 1
- 28 LIST_APPEND 2
- 31 JUMP_ABSOLUTE 13
- >> 34 POP_TOP
- 35 LOAD_CONST 0 (None)
- 38 RETURN_VALUE
+ 28 POP_TOP
+ 29 JUMP_ABSOLUTE 13
+ >> 32 POP_BLOCK
+ >> 33 LOAD_CONST 0 (None)
+ 36 RETURN_VALUE
The race is on. 比赛开始了。 Listcomp's first move is to build an empty list, while for loop's is to setup a loop.
Listcomp的第一步是建立一个空列表,而for循环是建立一个循环。 Both of them then proceed to load global range(), the constant 100, and call the range function for a generator.
然后它们都继续加载全局范围(),常量100,并调用生成器的范围函数。 Then they both get the current iterator and get the next item, and store it into the variable i.
然后他们都获得当前的迭代器并获得下一个项目,并将其存储到变量i中。 Then they load examplefunc and i and call examplefunc.
然后他们加载examplefunc和i并调用examplefunc。 Listcomp appends it to the list and starts the loop over again.
Listcomp将它附加到列表并再次开始循环。 For loop does the same in three instructions instead of two.
For循环在三个指令中执行相同而不是两个。 Then they both load None and return it.
然后他们都加载None并返回它。
So who seems better in this analysis? 那么谁在这个分析中似乎更好? Here, list comprehension does some redundant operations such as building the list and appending to it, if you don't care about the result.
在这里,列表理解会执行一些冗余操作,例如构建列表并附加到它,如果您不关心结果。 For loop is pretty efficient too.
For循环也非常有效。
If you time them, using a for loop is about one-third faster than a list comprehension. 如果你计时,使用for循环比列表理解快三分之一。 (In this test, examplefunc divided its argument by five and threw it away instead of doing nothing at all.)
(在这个测试中,examplefunc将其参数除以5并将其抛弃而不是什么都不做。)
Test two : Keeping the result like normal. 测试二 :保持结果正常。
No dummy function this test. 这个测试没有虚函数。 So here are our challengers:
所以这是我们的挑战者:
def listcomp_normal():
l = [i*5 for i in range(100)]
def forloop_normal():
l = []
for i in range(100):
l.append(i*5)
The diff isn't any use to us today. 差异对我们今天没用。 It's just the two machine codes in two blocks.
它只是两个块中的两个机器码。
List comp's machine code: 列出comp的机器代码:
55 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (100)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (i)
19 LOAD_FAST 0 (i)
22 LOAD_CONST 2 (5)
25 BINARY_MULTIPLY
26 LIST_APPEND 2
29 JUMP_ABSOLUTE 13
>> 32 STORE_FAST 1 (l)
35 LOAD_CONST 0 (None)
38 RETURN_VALUE
For loop's machine code: 对于循环的机器代码:
59 0 BUILD_LIST 0
3 STORE_FAST 0 (l)
60 6 SETUP_LOOP 37 (to 46)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (100)
15 CALL_FUNCTION 1
18 GET_ITER
>> 19 FOR_ITER 23 (to 45)
22 STORE_FAST 1 (i)
61 25 LOAD_FAST 0 (l)
28 LOAD_ATTR 1 (append)
31 LOAD_FAST 1 (i)
34 LOAD_CONST 2 (5)
37 BINARY_MULTIPLY
38 CALL_FUNCTION 1
41 POP_TOP
42 JUMP_ABSOLUTE 19
>> 45 POP_BLOCK
>> 46 LOAD_CONST 0 (None)
49 RETURN_VALUE
As you can probably already tell, the list comprehension has fewer instructions than for loop does. 正如您可能已经知道的那样,列表理解的指令少于循环。
List comprehension's checklist: 列表理解的清单:
range
. range
。 100
. 100
。 range
. range
。 i
. i
。 i
. i
。 l
to the anonymous empty list. l
指向匿名空列表。 For loop's checklist: 对于循环的核对清单:
l
to the anonymous empty list. l
指向匿名空列表。 range
. range
。 100
. 100
。 range
. range
。 i
. i
。 l
. l
。 append
on that list. append
。 i
. i
。 append
. append
。 (Not including these steps: Load None
, return it.) (不包括以下步骤:加载
None
,返回。)
The list comprehension doesn't have to do these things: 列表理解不必执行以下操作:
i
twice per loop i
两次 In conclusion, listcomp is a lot faster if you are going to use the values, but if you don't it's pretty slow. 总之,如果要使用这些值,listcomp要快得多,但如果不这样做,那么速度非常慢。
Real speeds 真正的速度
Test one: for loop is faster by about one-third* 测试一:for循环速度快三分之一*
Test two: list comprehension is faster by about two-thirds* 测试二:列表理解速度提高了大约三分之二*
*About -> second decimal place acurrate *关于 - >小数点后第二位
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.