Python:为啥弹出队列比 for-in 块更快?
Posted
技术标签:
【中文标题】Python:为啥弹出队列比 for-in 块更快?【英文标题】:Python: Why is popping off a queue faster than for-in block?Python:为什么弹出队列比 for-in 块更快? 【发布时间】:2015-10-03 12:37:44 【问题描述】:我一直在编写一个 Python 脚本来分析 CSV。其中一些文件相当大(1-2 百万条记录),脚本需要数小时才能完成。
我将处理记录的方式从for-in
循环更改为while
循环,速度提升非常显着。下面的演示:
>>> def for_list():
... for d in data:
... bunk = d**d
...
>>> def while_list():
... while data:
... d = data.pop(0)
... bunk = d**d
...
>>> data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> import timeit
>>> timeit.timeit(for_list)
1.0698931217193604
>>> timeit.timeit(while_list)
0.14515399932861328
几乎快了一个数量级。我从来没有看过 python 字节码,但我虽然它可能会说明问题,但事实证明 while_list
有更多说明。
那么这里发生了什么?这里有一个原则我可以应用到其他程序吗?是否存在for
比while
快十倍的情况?
编辑:正如@HappyLeapSecond 指出的那样,我不太明白timeit
内部到底发生了什么,以下差异消失了:
>>> def for_list():
... data = [x for x in range(1000)]
... for d in data:
... bunk = d**d
...
>>> def while_list():
... data = [x for x in range(1000)]
... while data:
... d = data.pop(0)
... bunk = d**d
>>> timeit.timeit(while_list, number=1000)
12.006330966949463
>>> timeit.timeit(for_list, number=1000)
11.847280025482178
奇怪的是,我的“真实”脚本通过如此简单的更改加速了这么多。我最好的猜测是迭代方法需要更多的交换?我有一个 40G 的交换分区,脚本填充了大约 15-20G。弹出会减少交换吗?
【问题讨论】:
我可以想象,为for
循环创建迭代器的成本可能相对较高。尝试使您的列表更长,例如100k 个条目,然后进行比较。
【参考方案1】:
while_list
正在改变全局 data
。 timeit.timeit
不会重置 data
的值。默认情况下,timeit.timeit
调用 for_list
和 while_list
各一百万次。第一次调用while_list
后,后续调用while_list
执行0 次循环后返回,因为data
已经为空。
您需要在每次调用 for_list
和 while_list
之前重置 data
的值,以执行公平的基准测试。
import timeit
def for_list(data):
for d in data:
bunk = d ** d
def while_list(data):
while data:
d = data.pop(0)
bunk = d ** d
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(timeit.timeit('data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; for_list(data)', 'from __main__ import for_list'))
# 0.959696054459
print(timeit.timeit('data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; while_list(data)', 'from __main__ import while_list'))
# 2.40107011795
pop(0)
是一个O(n)
操作。在长度为n
的循环内执行该操作使while_list
的整体时间复杂度为O(n**2)
,而O(n)
的复杂度为for_list
。所以正如预期的那样,for_list
更快,并且优势随着n
的增长而增长,data
的长度变大。
【讨论】:
上面的例子首先运行for_list
- 所以你的论点并不真正适用恕我直言。
@sebastian: timeit.timeit
默认重复调用while_list
一百万次。第一次调用while_list
后,data
为空。因此,对于 999,999 次运行,while_loop
完成得太快了。
@JoranBeasley:你如何重置data
?它必须在语句中完成,而不是设置,因为设置只运行一次。以上是关于Python:为啥弹出队列比 for-in 块更快?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 python itertools “消耗”配方比调用下 n 次更快?