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


79

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

循环1:

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

循环2:

for n in range(0,100000000):
  pass

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

Answers:


156

看到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

循环体有3个操作

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


2
实际上,第一次拆卸中的回路体有10次操作(从位置32跳到9)。在当前的CPython实现中,对每个字节码的解释具有很高的概率,这会导致CPU中代价高昂的间接分支错误预测(跳转到下一个字节码的实现)。这是当前CPython实现的结果,空载吞咽,PyPy和其他实现的JIT很可能会损失这些开销。他们中最好的人也将能够进行类型专业化,以获得一个数量级的加速。
蚂蚁阿斯玛09年

5
使用“ dis”模块。定义你的代码中的函数,然后调用dis.disco(FUNC .__ code__)
kcwu

那么可以准确地说,在更高级别上,while循环必须在每次迭代中进行比较吗?
davidhood2

34

range()用C实现,而用Ci += 1解释。

使用xrange()它可以使批量处理甚至更快。从Python 3.0开始与range()以前相同xrange()


15

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

i += 1

是相同的:

i = i + 1

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

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

垃圾收集器还将有大量清理工作。“对象创建很昂贵”。


4

因为您使用解释器中用C编写的代码的运行频率更高。即i + = 1在Python中,所以比较慢(而range(0,...)是一个C调用),for循环也将大部分在C中执行。


1

Python的大多数内置方法调用均作为C代码运行。必须解释的代码要慢得多。就内存效率和执行速度而言,差异是巨大的。python内部已经过优化,因此最好利用这些优化。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.