我该如何优化这个文件系统I/O限制的程序?
我有一个Python程序,大致是这样工作的:
- 从一个csv文件中读取一行数据。
- 对这行数据进行一些处理。
- 把处理后的数据分解成实际要写入数据库的行。
- 把这些行写入单独的csv文件。
- 如果文件还没完全读取,就回到第一步。
- 运行SQL*Loader,把这些文件加载到数据库中。
第六步其实花的时间不多。看起来第四步才是最耗时的。总体来说,我想优化这个程序,以便能处理几百万条记录,并且运行在一个四核的服务器上,配有某种RAID存储。
我有几个想法来解决这个问题:
- 从第一步开始读取整个文件(或者至少分成很大的块来读取),然后把文件整体或分成大块写入磁盘。这样可以减少硬盘在文件之间来回移动的时间。这样做会有什么好处吗?比起缓冲区来说,效果会更好吗?
- 把第1、2、3和4步并行处理,分成不同的进程。这样第1、2、3步就不需要等第4步完成。
- 把加载文件分成不同的块,并行处理它们。行数据不需要按顺序处理。这可能需要和第二步结合起来。
当然,解决这个问题的正确答案是“通过测试找到最快的方法”。不过,我主要是想知道我应该先把时间花在哪里。有没有经验丰富的人能给点建议?
7 个回答
如果你的程序在输入输出方面比较慢,优化的最好方法就是一次性把整个文件读到内存里,或者把整个文件从内存写出去,然后后续的操作都在内存中进行。
经过大量测试,我发现程序的运行速度并不是由我从硬盘读取或写入的数据量决定的,而是由我进行的输入输出操作的次数决定的。所以,你需要优化的就是这个部分。
我不太懂Python,但如果有办法让它一次性把整个文件从内存写出去,而不是每次处理一个字节都要进行一次输入输出操作,那你就应该这么做。
当然,这样做的一个缺点是,文件可能会比可用的内存大得多。处理这个问题的方法有很多,但那是另一个话题,以后再说。
Python本身就有输入输出的缓冲机制,而操作系统应该会处理输入文件的预读取和延迟写入,直到它需要用到内存或者对内存中有脏数据感到不安。除非你强制操作系统立即写入,比如在每次写入后关闭文件,或者以O_SYNC模式打开文件。
如果操作系统没有按预期工作,你可以尝试增加缓冲区的大小(在open()
函数中的第三个参数)。比如,如果你的IO系统是100MB/s,延迟是10毫秒,那么1MB的IO大小会导致大约50%的延迟开销,而10MB的IO大小则会使开销降到9%。如果还是受IO限制,那可能就是带宽不够。你可以使用操作系统特定的工具来检查你从硬盘读写数据的带宽。
另外,检查一下第4步是否花费了很多时间在执行或等待IO上也很有用。如果是在执行,你需要花更多时间找出哪个部分是问题所在并进行优化,或者把工作分配到不同的进程中去。