Pythonic / Python中继续的性能[关闭]
Posted
技术标签:
【中文标题】Pythonic / Python中继续的性能[关闭]【英文标题】:Pythonic/Performance of continue in Python [closed] 【发布时间】:2016-10-20 21:43:43 【问题描述】:我不确定这是否应该进入 cs 或程序员堆栈交换,所以如果应该请告诉我。我知道在其他语言中,continue
可能会出现问题,因为它本质上执行无条件分支,这会导致流水线机器上的性能问题,因为为了有效地流水线,我们需要能够知道接下来是什么指令,哪个无条件分支可以预防。这可以通过使用更可靠的逻辑来防止。话虽如此,我知道在 Python 中,continue
可以提高循环内的性能,但是,我希望不仅仅是轶事证据来支持这一点。谁能详细说明 为什么 在 Python 中会出现这种情况?此外,在 Python 中使用 continue
是否被认为是“正确的”逻辑(即 Pythonic)?谢谢!
更新
我也对性能影响感到好奇。如上所述,在其他语言中,continue
基本上是一个无条件分支。显然,这可能会导致流水线机器上的性能问题。所以我很好奇,在 Python 中使用 continue
时是否也存在固有的性能开销,即使在循环中使用时会有性能提升?
在示例代码中(尝试对插入排序进行返工,忽略整体逻辑,这源于一些尝试获得优于 O(n2) 性能的实验),如果我们有在我们可以确定下一个元素应该去哪里(在头部或尾部)的情况下,我们可以轻松地将它放在那里并使用 continue
语句忽略循环的其余部分
def insertion_sort(array):
sublist = [array[0]]
for i in range(1, len(array)):
if array[i] >= sublist[i-1]:
sublist.append(array[i])
continue #possible performance loss here
if array[i] <= sublist[0]:
sublist.insert(0, array[i])
continue #possible performance loss here
for j in reversed(range(0, i)):
if array[i] >= sublist[j-1] and array [i] <=sublist[j]:
sublist.insert(j, array[i])
break #possible performance loss here
return sublist
【问题讨论】:
看看Are `break` and `continue` bad programming practices?。使用continue
没有任何问题,它可以使某些代码更具可读性。
我想这确实回答了“正确”逻辑的问题。但我仍然对性能的影响感到好奇。更新问题,见上文!
取决于具体的用例。无论如何,当到达块的末尾时,流程将返回到循环的开头,因此如果您使用continue
来短路特定情况的逻辑,它最终仍会执行相同数量的迭代,除非你明确地break
退出循环。从优化的 POV 来看,最常见的情况在循环代码中首先处理可能更重要。发布一些具体的例子会有所帮助。
添加示例代码
这也值得一试:cs.stackexchange.com/questions/35537/…
【参考方案1】:
在这种情况下,您使用continue
作为在互斥条件下的“选择”手段。考虑以下更简单的示例...
def f1():
for i in range(10):
if i < 5:
print 'i is less than 5'
if i > 5:
print 'i is greater than 5'
def f2():
for i in range(10):
if i < 5:
print 'i is less than 5'
continue
if i > 5:
print 'i is greater than 5'
这两个函数将产生相同的输出,但在第一种情况下,鉴于我们知道i
不能同时大于和小于 5,第一个函数执行了不必要的比较,并且很可能执行时间更长。
第二个例子更常见的表示为...
def f3():
for i in range(10):
if i < 5:
print 'i is less than 5'
elif i > 5:
print 'i is greater than 5'
至于哪个性能更好,你可以看看 Python 的dis
模块,对于f2()
产生...
0 SETUP_LOOP 63 (to 66)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (10)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 49 (to 65)
16 STORE_FAST 0 (i)
19 LOAD_FAST 0 (i)
22 LOAD_CONST 2 (5)
25 COMPARE_OP 0 (<)
28 POP_JUMP_IF_FALSE 42
31 LOAD_CONST 3 ('i is less than 5')
34 PRINT_ITEM
35 PRINT_NEWLINE
36 JUMP_ABSOLUTE 13
39 JUMP_FORWARD 0 (to 42)
>> 42 LOAD_FAST 0 (i)
45 LOAD_CONST 2 (5)
48 COMPARE_OP 4 (>)
51 POP_JUMP_IF_FALSE 13
54 LOAD_CONST 4 ('i is greater than 5')
57 PRINT_ITEM
58 PRINT_NEWLINE
59 JUMP_ABSOLUTE 13
62 JUMP_ABSOLUTE 13
>> 65 POP_BLOCK
>> 66 LOAD_CONST 0 (None)
69 RETURN_VALUE
...对于f3()
...
0 SETUP_LOOP 60 (to 63)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (10)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 46 (to 62)
16 STORE_FAST 0 (i)
19 LOAD_FAST 0 (i)
22 LOAD_CONST 2 (5)
25 COMPARE_OP 0 (<)
28 POP_JUMP_IF_FALSE 39
31 LOAD_CONST 3 ('i is less than 5')
34 PRINT_ITEM
35 PRINT_NEWLINE
36 JUMP_ABSOLUTE 13
>> 39 LOAD_FAST 0 (i)
42 LOAD_CONST 2 (5)
45 COMPARE_OP 4 (>)
48 POP_JUMP_IF_FALSE 13
51 LOAD_CONST 4 ('i is greater than 5')
54 PRINT_ITEM
55 PRINT_NEWLINE
56 JUMP_ABSOLUTE 13
59 JUMP_ABSOLUTE 13
>> 62 POP_BLOCK
>> 63 LOAD_CONST 0 (None)
66 RETURN_VALUE
...唯一显着的区别是第一个示例中的39 JUMP_FORWARD
,由于之前的无条件跳转,它永远无法真正执行,但使用它来决定哪个“更好”正在将优化提升到一个荒谬的水平。
f2()
和f3()
这两个例子只是在风格上真正不同。考虑...
def f4():
while 1:
if expr1:
do_something()
else:
if expr2:
do_something_else()
if expr3:
do_a_different_thing()
def f5():
while 1:
if expr1:
do_something()
continue
if expr2:
do_something_else()
if expr3:
do_a_different_thing()
...它们在功能上是相同的,但f5()
最小化了嵌套块的缩进级别,这对于较窄的固定宽度显示更可取。至于哪个更“可读”,那纯粹是主观的。
回到您的代码,您也可以将其表达为...
def insertion_sort(array):
sublist = [array[0]]
for i in range(1, len(array)):
if array[i] >= sublist[i-1]:
sublist.append(array[i])
elif array[i] <= sublist[0]:
sublist.insert(0, array[i])
else:
for j in reversed(range(0, i)):
if array[i] >= sublist[j-1] and array [i] <=sublist[j]:
sublist.insert(j, array[i])
break #possible performance loss here
return sublist
...这将消除continue
s,但正如dis
模块所展示的那样,这样做并没有性能优势。至于break
,据我所知,这是算法正常运行所必需的。
【讨论】:
您可以将break
替换为return sublist
。
很好的答案。这正是我正在寻找的
@ppperry 我很确定这也会打破不希望的外部 for 循环。以上是关于Pythonic / Python中继续的性能[关闭]的主要内容,如果未能解决你的问题,请参考以下文章