我一直在研究一个
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有更多的指示.
那么这里发生了什么?这里有原则可以申请到其他程序吗?有没有场景比现在快十倍?
编辑:正如@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的.会弹出减少交换?
解决方法
while_list正在突变全局数据. timeit.timeit不会重置数据的值. timeit.timeit默认调用for_list和while_list每个百万次.在首次调用while_list之后,随后调用while_list返回执行0循环,因为数据已经为空.
您需要在每次调用for_list和while_list之前重置数据的值,以执行公平的基准.
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,10] print(timeit.timeit('data = [1,10]; for_list(data)','from __main__ import for_list')) # 0.959696054459 print(timeit.timeit('data = [1,10]; while_list(data)','from __main__ import while_list')) # 2.40107011795
pop(0)是一个O(n)操作.在长度为n的循环中执行,使得while_list与for_list的O(n)复杂度相比,具有总体时间复杂度O(n ** 2).如预期的那样,for_list更快,随着n的长度,数据的增长越来越大.