【问题标题】:Python for loop executionPython for 循环执行
【发布时间】:2013-01-25 13:15:36
【问题描述】:
for a in sorted(arr):
    doSomething()

sArr = sorted(arr)
for a in sArr:
    doSomething()

这两个是否完全相同,或者一个会比另一个消耗更多的 cpu 周期?

意思是,在第一个例子中,sorted(arr) 会在每个循环中被调用吗?

谢谢

【问题讨论】:

    标签: python for-loop python-2.7


    【解决方案1】:

    它们几乎相同。

    The documentation 说:

    表达式列表被计算一次;它应该产生一个可迭代的对象。

    因此,在上述代码中,sorted() 调用不会发生多次。

    第一种形式仍然更好,因为它更短更简洁,并且可以更好地优化(因为解释器可以确定循环结束后排序列表完全超出范围)。

    【讨论】:

      【解决方案2】:

      两者并不完全相同,但差异很小。如有疑问,请使用dis 模块查找:

      >>> import dis
      >>> def f():
      ...     for a in sorted(arr):
      ...         doSomething()
      ... 
      >>> def g():
      ...     sArr = sorted(arr)
      ...     for a in sArr:
      ...         doSomething()
      ... 
      >>> dis.dis(f)
        2           0 SETUP_LOOP              27 (to 30)
                    3 LOAD_GLOBAL              0 (sorted)
                    6 LOAD_GLOBAL              1 (arr)
                    9 CALL_FUNCTION            1
                   12 GET_ITER            
              >>   13 FOR_ITER                13 (to 29)
                   16 STORE_FAST               0 (a)
      
        3          19 LOAD_GLOBAL              2 (doSomething)
                   22 CALL_FUNCTION            0
                   25 POP_TOP             
                   26 JUMP_ABSOLUTE           13
              >>   29 POP_BLOCK           
              >>   30 LOAD_CONST               0 (None)
                   33 RETURN_VALUE        
      >>> dis.dis(g)
        2           0 LOAD_GLOBAL              0 (sorted)
                    3 LOAD_GLOBAL              1 (arr)
                    6 CALL_FUNCTION            1
                    9 STORE_FAST               0 (sArr)
      
        3          12 SETUP_LOOP              21 (to 36)
                   15 LOAD_FAST                0 (sArr)
                   18 GET_ITER            
              >>   19 FOR_ITER                13 (to 35)
                   22 STORE_FAST               1 (a)
      
        4          25 LOAD_GLOBAL              2 (doSomething)
                   28 CALL_FUNCTION            0
                   31 POP_TOP             
                   32 JUMP_ABSOLUTE           19
              >>   35 POP_BLOCK           
              >>   36 LOAD_CONST               0 (None)
                   39 RETURN_VALUE        
      

      如您所见,g() 函数添加了STORE_FASTLOAD_FAST 操作。您还使用了更多内存,因为排序结果会一直保留,直到清理 sArr 变量,而在 f() 中,排序结果可以在循环结束后立即清理。

      CALL_FUNCTION 执行sorted() 函数;它只执行一次。

      【讨论】:

      • +1:天哪,在 29 秒内击败我。甚至是“有疑问时”的措辞
      • 因此改变了我的答案,以击败你的心灵感应
      【解决方案3】:

      sorted() 返回一个随后被迭代的对象。它只被调用一次。

      【讨论】:

        【解决方案4】:

        和其他人说的一样。请记住,当有疑问时(尽管在这种情况下微不足道且显而易见),请始终在源代码上运行反汇编。或者,您可以通过 cProfile 运行它,即使反汇编不同,cProfile 也会对性能给出一个公平的概念

        >>> def foo():
            total = 0
            arr = range(10000000,1,-1)
            for a in sorted(arr):
                total += a
        
        
        >>> def bar():
            total = 0
            for a in sorted(range(10000000,1,-1)):
                total += a
        
        
        >>> cProfile.run("bar()")
                 5 function calls in 2.614 seconds
        
           Ordered by: standard name
        
           ncalls  tottime  percall  cumtime  percall filename:lineno(function)
                1    1.914    1.914    2.614    2.614 <pyshell#88>:1(bar)
                1    0.000    0.000    2.614    2.614 <string>:1(<module>)
                1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
                1    0.266    0.266    0.266    0.266 {range}
                1    0.434    0.434    0.434    0.434 {sorted}
        
        
        >>> cProfile.run("foo()")
                 5 function calls in 2.477 seconds
        
           Ordered by: standard name
        
           ncalls  tottime  percall  cumtime  percall filename:lineno(function)
                1    1.779    1.779    2.346    2.346 <pyshell#86>:1(foo)
                1    0.130    0.130    2.477    2.477 <string>:1(<module>)
                1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
                1    0.152    0.152    0.152    0.152 {range}
                1    0.415    0.415    0.415    0.415 {sorted}
        
        
        >>> 
        

        正如您将在此处观察到的那样

        1. Sorted 在两种情况下都只调用一次
        2. 每种情况下有 5 个函数调用
        3. 它们的性能几乎相同。

        【讨论】:

        • 对于这么小的事情,我会改用timeit 来消除操作系统调度的变化。
        • @MartijnPieters:我使用cProfile 的主要目的是展示functions callsncalls
        • 啊,好的。您想展示调用结构,而不是性能差异。
        猜你喜欢
        • 2016-11-22
        • 2011-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-10
        • 2012-08-04
        相关资源
        最近更新 更多