代码之家  ›  专栏  ›  技术社区  ›  A. Dorton

为什么在Python中循环range()比使用while循环更快?

  •  96
  • A. Dorton  · 技术社区  · 16 年前

    前几天我在做一些Python基准测试,我遇到了一些有趣的事情。下面是两个或多或少做同样事情的循环。循环1的执行时间大约是循环2的两倍。

    i = 0
    while i < 100000000:
        i += 1
    

    for n in range(0,100000000):
        pass
    

    为什么第一个循环要慢得多?我知道这是一个微不足道的例子,但它激起了我的兴趣。range()函数是否有什么特殊之处,使其比以相同的方式递增变量更高效?

    6 回复  |  直到 2 年前
        1
  •  182
  •   Nope    16 年前

    看看python字节码的反汇编,你可能会得到一个更具体的想法

    while循环使用:

    1           0 LOAD_CONST               0 (0)
                3 STORE_NAME               0 (i)
    
    2           6 SETUP_LOOP              28 (to 37)
          >>    9 LOAD_NAME                0 (i)              # <-
               12 LOAD_CONST               1 (100000000)      # <-
               15 COMPARE_OP               0 (<)              # <-
               18 JUMP_IF_FALSE           14 (to 35)          # <-
               21 POP_TOP                                     # <-
    
    3          22 LOAD_NAME                0 (i)              # <-
               25 LOAD_CONST               2 (1)              # <-
               28 INPLACE_ADD                                 # <-
               29 STORE_NAME               0 (i)              # <-
               32 JUMP_ABSOLUTE            9                  # <-
          >>   35 POP_TOP
               36 POP_BLOCK
    

    循环体有10个操作

    使用范围:

    1           0 SETUP_LOOP              23 (to 26)
                3 LOAD_NAME                0 (range)
                6 LOAD_CONST               0 (0)
                9 LOAD_CONST               1 (100000000)
               12 CALL_FUNCTION            2
               15 GET_ITER
          >>   16 FOR_ITER                 6 (to 25)        # <-
               19 STORE_NAME               1 (n)            # <-
    
    2          22 JUMP_ABSOLUTE           16                # <-
          >>   25 POP_BLOCK
          >>   26 LOAD_CONST               2 (None)
               29 RETURN_VALUE
    

        2
  •  41
  •   Georg Schölly Crazy Developer    16 年前

    range() i += 1

    使用 xrange() 对于大量用户来说,这可能会更快。从Python 3.0开始 range() 与之前相同 .

        3
  •  21
  •   Peter    2 年前

    必须说,while循环中有很多对象的创建和销毁。

    i += 1
    

    i = i + 1
    

    但是因为Python int是不可变的,所以它不会修改现有的对象;相反,它创造了一个具有新价值的全新对象。它基本上是:

    i = new int(i + 1)   # Using C++ or Java-ish syntax
    

    更新: 有趣。现在使用3.11, for 实际上比 while 对于低迭代次数:

    迭代 while循环 for 循环
    55
    5. 26
    10 18
    1_000 18 9.2
    1_000_000 18 11

    两者都是非常稳定的ns/迭代超过100。

        4
  •  11
  •   mCoding    4 年前

    我认为这里的答案比其他答案所暗示的要微妙一些,尽管它的要点是正确的: for循环更快,因为更多的操作发生在C中,而Python中的操作更少 .

    1. i < 100000000 range(100000000) ,它在C中内部进行迭代(并因此进行边界检查)。

    2. i += 1 范围(100000000) 用C写的 i+=1 (或 ++i ).

    我们可以看到,正是这两件事的结合,通过手动将它们加回来查看差异,使for循环更快。

    import timeit
    
    N = 100000000
    
    
    def while_loop():
        i = 0
        while i < N:
            i += 1
    
    
    def for_loop_pure():
        for i in range(N):
            pass
    
    
    def for_loop_with_increment():
        for i in range(N):
            i += 1
    
    
    def for_loop_with_test():
        for i in range(N):
            if i < N: pass
    
    
    def for_loop_with_increment_and_test():
        for i in range(N):
            if i < N: pass
            i += 1
    
    
    def main():
        print('while loop\t\t', timeit.timeit(while_loop, number=1))
        print('for pure\t\t', timeit.timeit(for_loop_pure, number=1))
        print('for inc\t\t\t', timeit.timeit(for_loop_with_increment, number=1))
        print('for test\t\t', timeit.timeit(for_loop_with_test, number=1))
        print('for inc+test\t', timeit.timeit(for_loop_with_increment_and_test, number=1))
    
    
    if __name__ == '__main__':
        main()
    

    我尝试了将数字100000000作为一个字面常量,并将其作为一个变量 N 这将是更典型的。

    # inline constant N
    while loop      3.5131139
    for pure        1.3211338000000001
    for inc         3.5477727000000003
    for test        2.5209639
    for inc+test    4.697028999999999
    
    # variable N
    while loop      4.1298240999999996
    for pure        1.3526357999999998
    for inc         3.6060175
    for test        3.1093069
    for inc+test    5.4753364
    

    正如你所看到的,在这两种情况下 while for inc+test for pure 请注意,在我们使用 N ,但是 for

    3倍代码加速

        5
  •  4
  •   John Montgomery    16 年前

        6
  •  0
  •   ohdeargod    16 年前