关于性能:为什么Python中的range()循环比使用while循环更快?

Why is looping over range() in Python faster than using a while loop?

前几天我做了一些Python基准测试,我发现了一些有趣的东西。 下面是两个或多或少相同的循环。 循环1大约需要循环2执行的两倍。

循环1:

1
2
3
int i = 0
while i < 100000000:
  i += 1

循环2:

1
2
for n in range(0,100000000):
  pass

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


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

使用while循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
2
3
4
5
6
7
8
9
10
11
12
13
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

循环体有3个操作

运行C代码的时间比intepretor短得多,可以忽略。


range()在C中实现,而i += 1被解释。

使用xrange()可以使大数字更快。从Python 3.0 range()开始与之前的xrange()相同。


必须要说的是,while循环中存在大量的对象创建和破坏。

1
i += 1

是相同的:

1
i = i + 1

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

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

垃圾收集器也将进行大量的清理工作。
"对象创建很昂贵"。


因为您在解释器中用C编写的代码中运行得更频繁。即i + = 1在Python中,如此慢(相对),而范围(0,...)是一个C调用,for循环也将主要在C中执行。


Python的大多数内置方法调用都是作为C代码运行的。必须解释的代码要慢得多。在内存效率和执行速度方面,差异是巨大的。 python内部结构已经过极端优化,最好利用这些优化。