“else”语句是否有任何开销?

时间:2014-01-21 20:20:52

标签: python

这之间是否有任何性能差异:

for item in collection:
    if item == badItem:
        break
    else
        doFunction(item)

而且:

for item in collection:
    if item == badItem:
        break
    doFunction(item)

假设我这样做了几亿次,所以任何性能差异都会有所帮助。

编辑: 我实际上并没有根据这个问题的结果实现这个,我只是在理论上想知道什么是更快的。我只是好奇。

4 个答案:

答案 0 :(得分:5)

以下是两个版本的并排:

 0 SETUP_LOOP              40 (to 43)      |  0 SETUP_LOOP              40 (to 43)
 3 LOAD_GLOBAL              0 (collection) |  3 LOAD_GLOBAL              0 (collection)
 6 GET_ITER                                |  6 GET_ITER            
 7 FOR_ITER                32 (to 42)      |  7 FOR_ITER                32 (to 42)
10 STORE_FAST               0 (item)       | 10 STORE_FAST               0 (item)
                                           | 
13 LOAD_FAST                0 (item)       | 13 LOAD_FAST                0 (item)
16 LOAD_GLOBAL              1 (badItem)    | 16 LOAD_GLOBAL              1 (badItem)
19 COMPARE_OP               2 (==)         | 19 COMPARE_OP               2 (==)
22 POP_JUMP_IF_FALSE       29              | 22 POP_JUMP_IF_FALSE       29
                                           | 
25 BREAK_LOOP                              | 25 BREAK_LOOP          
26 JUMP_ABSOLUTE            7              | 26 JUMP_FORWARD             0 (to 29)
                                           | 
29 LOAD_GLOBAL              2 (doFunction) | 29 LOAD_GLOBAL              2 (doFunction)
32 LOAD_FAST                0 (item)       | 32 LOAD_FAST                0 (item)
35 CALL_FUNCTION            1              | 35 CALL_FUNCTION            1
38 POP_TOP                                 | 38 POP_TOP             
39 JUMP_ABSOLUTE            7              | 39 JUMP_ABSOLUTE            7
42 POP_BLOCK                               | 42 POP_BLOCK           
43 LOAD_CONST               0 (None)       | 43 LOAD_CONST               0 (None)
46 RETURN_VALUE                            | 46 RETURN_VALUE        

正如您所看到的,唯一的区别是JUMP_ABSOLUTEelse}与JUMP_FORWARD对比(没有它)。由于两个操作码都紧跟在BREAK_LOOP之后,因此它们不会在任何情况下运行,因此两个版本完全等效。

也就是说,在破坏声明(else)之后的break/continue/return通常被认为是代码气味(并且需要额外的无用线)。

如果您对最高性能感兴趣,可能值得考虑使用.indexitertools.takewhile而不是使用if的普通循环。

答案 1 :(得分:2)

这听起来像是预成熟的优化:不要这样做。

如果你不得不这样做,你应该在尝试优化程序之前使程序正常工作。

如果您的完成的应用程序比您需要的慢,那么测量,测量,测量。使用分析工具。缓慢的部分可能会让你感到惊讶。不要浪费时间固定那些速度不慢的部件。

但回到第一点:不要尝试优化功能不完整的程序。

答案 2 :(得分:1)

Grant Birchmeier说:衡量,衡量,衡量。

使用Python 3.3.1 (default, Apr 17 2013, 22:30:32) [GCC 4.7.3] on linux在我的方框中,我得到了以下结果:

testA 0.7911653139999544
testB 0.7868194140028208
testC 0.7771379340010753

使用:

collection = [random.randint (1, 10000000) for _ in range (10000000) ]
badItem = 0
collection [5000000] = 0

def doFunction (item): pass

def testA ():
    for item in collection:
        if item == badItem: break
        else: doFunction (item)

def testB ():
    for item in collection:
        if item == badItem: break
        doFunction (item)

def testC ():
    badIndex = collection.index (badItem)
    for item in collection [:badIndex]:
        doFunction (item)

YMMV。我只是在比较整数和没有真实世界的数据。我不知道你的__eq__doFunction做了什么等等。

答案 3 :(得分:1)

Grant's answer说得对:如果你关心性能,首先要运行代码,然后measure需要改进什么,最后改进那些东西。

对于后代,这是我的时间结果。简短的回答:即使经过数十亿次迭代,也没有真正的区别。

With Else:
min: 0.001327058799944325
max: 0.0037289344766406884
mean: 0.002665085947631951

Without Else:
min: 0.0013189987034252226
max: 0.003550914613782652
mean: 0.002147321588495288

代码:

C:\>python
Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:03:43) [MSC v.1600 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> else_version = timeit.repeat('for i in c:\n\tif i == 0: break\n\telse:i += 1','import random;c=[random.randint(0,1) for _ in range(1000000)]', number = 10000, repeat=10)
>>> nelse_version = timeit.repeat('for i in c:\n\tif i == 0: break\n\ti += 1','import random;c=[random.randint(0,1) for _ in range(1000000)]', number = 10000, repeat=10)
>>> min(else_version)
0.001327058799944325
>>> max(else_version)
0.0037289344766406884
>>> sum(else_version)/10
0.002665085947631951
>>>
>>> min(nelse_version)
0.0013189987034252226
>>> max(nelse_version)
0.003550914613782652
>>> sum(nelse_version)/10
0.002147321588495288
>>>

无论实际使用else声明的成本是多少,与您正在进行的任何实际操作(例如__eq__实施或实际doFunction实际操作相比,显然相形见绌)是,甚至只是你机器上发生的其他事情。)