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

...这将消除continues,但正如dis 模块所展示的那样,这样做并没有性能优势。至于break,据我所知,这是算法正常运行所必需的。

【讨论】:

您可以将break 替换为return sublist 很好的答案。这正是我正在寻找的 @ppperry 我很确定这也会打破不希望的外部 for 循环。

以上是关于Pythonic / Python中继续的性能[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

浅谈python 字典对象的 Pythonic 用法

python 检查列表中所有项目是否相等的Pythonic方法

Python 的 sum():Pythonic 的求和方法

#13 让代码变得Pythonic

用Pythonic方式来思考

python -- Pythonic