我正在编写一个程序,它生成指定数量的句子,每个句子都写入一个文件。我一直在尝试为1000万句或更多句子的案例优化代码。
我最近在打开调用中将缓冲区参数指定为512MB以提高写入性能,但是我的代码实际上慢了3秒。罪魁祸首是{method 'close' of '_io.TextIOWrapper' objects}
。我认为这与文件关闭方法有关,但这是我第一次比较配置文件输出。
这是我的写入速度有多慢:
10000000 49.057 0.000 49.057 0.000 {method 'write' of '_io.TextIOWrapper' objects}
现在就是这样:
10000000 3.184 0.000 3.184 0.000 {method 'write' of '_io.TextIOWrapper' objects}
相当大的改进。
这是我以前的近距离方法:
3 4.003 1.334 4.003 1.334 {method 'close' of '_io.TextIOWrapper' objects}
这是我的新人:
1 62.668 62.668 62.668 62.668 {method 'close' of '_io.TextIOWrapper' objects}
这是我的代码:
def sentence_maker(nouns, verbs, number_of_sentences, file_name):
writer = open(file_name, "w", 536870912)
for num in range(number_of_sentences):
string = (choice(nouns) + " " + choice(verbs) + " " + choice(nouns))
writer.write(string + "\n")
writer.close()
为什么close()
这么慢?
注意:在程序早期的某个地方,我曾经有一些close()
语句,因此在我的旧ncalls = 3
示例中为close()
。我已经确定这些对性能没有明显的影响。
答案 0 :(得分:1)
写入磁盘的速度很慢,因此许多程序将写入存储到大块中,这些块一次性写入。这称为缓冲,Python在您打开文件时会自动执行此操作。当您写入文件时,您实际上正在写入"缓冲区"在记忆中。当它填满时,Python会自动将其写入磁盘或调用close()。
答案 1 :(得分:1)
你明确选择使用一个巨大的缓冲区(536870912是在刷新缓冲区之前缓冲的字节数,大约半GB的内存)。 close
包含隐含在缓冲区中的任何内容flush
,并假设您正在写很多内容,这意味着它需要全部写出来。
您必须在某个时候支付实际的I / O;一个大的缓冲区使得write
便宜(因为它实际上并没有执行任何I / O),但是一个大的缓冲区只是推迟了痛苦,而不是避免它。我怀疑任何超过1 MB的缓冲区大小实际上会节省有意义的工作(并且限制可能更低);如果你经常这样做,执行系统调用的成本很高,但是当每次调用完成的工作(实际的物理I / O)超过它们时,每MB一次调用和每512 MB一次调用之间的差异是没有意义的。一个数量级或更多。
为了进行比较,缓冲的原因是系统调用与常规函数调用(a few hundred clock ticks, vs. a dozen or less for most function calls)相比具有较高的开销。 CPython在I / O中有一些额外的系统调用(释放和恢复GIL),因此系统调用write
与memcpy
的增量成本相差100-1000x。但即使在现代CPU的开销上,即使2000个滴答仍然在微秒范围内。但是I / O本身要贵得多;写10 MB的数据可能需要十分之一秒左右。 使用较大的缓冲区大小在系统调用上保存一些毫秒并不重要,当I / O本身的成本在秒中测量时。一个缓冲区,大的将开始引入缓存未命中(以及可能的页面错误),较小的缓冲区将避免。