前几天我做了一些Python基准测试,我发现了一些有趣的东西。下面是两个或多或少相同的循环。循环1大约需要循环2执行的两倍。
循环1:
int i = 0
while i < 100000000:
i += 1
循环2:
for n in range(0,100000000):
pass
为什么第一个循环这么慢?我知道这是一个微不足道的例子,但它引起了我的兴趣。是否有一些关于range()函数的特殊内容使得它比以相同的方式递增变量更有效?
答案 0 :(得分:139)
看到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 op
使用范围:
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个op
运行C代码的时间比intepretor短得多,可以忽略。
答案 1 :(得分:31)
range()
在C中实现,而i += 1
则被解释。
使用xrange()
可以使大数字更快。从Python 3.0开始range()
与之前的xrange()
相同。
答案 2 :(得分:13)
必须要说的是,while循环中存在大量的对象创建和破坏。
i += 1
与:
相同i = i + 1
但是因为Python int是不可变的,所以它不会修改现有的对象;相反,它创造了一个具有新价值的全新物体。基本上是这样的:
i = new int(i + 1) # Using C++ or Java-ish syntax
垃圾收集器也需要做大量的清理工作。 “对象创建很昂贵”。
答案 3 :(得分:3)
因为您在解释器中用C编写的代码中运行得更频繁。即,i + = 1在Python中,如此缓慢(相对),而范围(0,...)是一个C调用,for循环也将主要在C中执行。
答案 4 :(得分:2)
Python的大多数内置方法调用都是作为C代码运行的。必须解释的代码要慢得多。在内存效率和执行速度方面,差异是巨大的。 python内部结构已经过极端优化,最好利用这些优化。
答案 5 :(得分:0)
我认为这里的答案比其他答案建议的要微妙得多,尽管要旨是正确的: for循环更快,因为更多的操作发生在C中,而更少的发生在Python中。
更具体地说,在for循环的情况下,C中发生了两件事,而while循环是在Python中处理的:
在while循环中,比较i < 100000000
在Python中执行,而在for循环中,作业被传递到range(100000000)
的迭代器,迭代器在内部进行迭代(并且因此边界检查)。
在while循环中,循环更新i += 1
在Python中发生,而在for循环中,再次以C编写的range(100000000)
的迭代器执行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
变量的情况下,while
的附加速度会降低,以重复查找N
的值,而for
不会。< / p>
如此微不足道的修改会导致超过3倍的代码加速,这真是太疯狂了,但这是适合您的Python。当您完全可以在循环中使用内置函数时,甚至都不要让我开始...。