为何Python多进程读取二进制文件比单线程更慢?
你的多进程读取速度远慢于单线程,核心原因出在低效的IO操作模式和多进程的额外开销上,具体拆解如下:
1. 逐字节读取(read(1))是性能灾难
不管单线程还是多进程,逐字节调用read(1)都是极端低效的做法。每次read调用都会触发一次系统调用,而系统调用本身有固定开销。10MB的文件有超过1000万字节,意味着要执行1000多万次系统调用,单线程就已经慢得离谱;多进程下多个进程同时发起大量系统调用,会导致内核调度开销暴增,反而比单线程更慢。
2. 多进程的额外成本抵消了并行收益
多进程本身有创建、销毁的开销,加上你用了multiprocessing.Manager().dict做进程间通信,每次写入字典都会涉及进程间同步,这又增加了额外的开销。而你的任务本质是IO密集型(哪怕是低效IO),多进程并行不仅没法提升IO效率,反而会因为进程竞争内核资源(比如文件锁、IO调度队列)拖慢整体速度。
3. 循环逻辑错误导致无效读取
你的getBinaryData里循环条件写反了:
while data != b'' or pointer_position < pointer_to:
这里应该用and而不是or,否则哪怕已经读到了pointer_to的位置,只要data不为空就会继续读,直到文件结束。这导致每个进程实际读取的内容远超预期,进一步放大了IO开销。
4. 多进程文件操作的缓存颠簸
虽然用了NVMe SSD,但每个进程单独打开文件并逐字节读取,会导致操作系统的文件缓存无法高效利用——多个进程分散读取不同区域,缓存命中率极低,没法发挥NVMe的高带宽优势。
第一步:放弃逐字节读取,改用大块读取
把read(1)改成读取大块数据,比如read(4096)(系统页大小)或者更大的块,一次读取多字节后再处理,能大幅减少系统调用次数。修改后的getBinaryData示例:
def getBinaryData(procnum, filename, pointer_from, pointer_to): binary_values = [] chunk_size = 4096 # 可根据实际调整为更大值,比如65536 start = time.time() with open(filename, 'rb') as fileobject: fileobject.seek(pointer_from) remaining = pointer_to - pointer_from while remaining > 0: read_size = min(chunk_size, remaining) data = fileobject.read(read_size) if not data: break # 批量处理字节,比逐个append高效 binary_values.extend(ord(byte) for byte in data) remaining -= len(data) end = time.time() print(f"proc {procnum} finished in: {end - start:.2f}s") return binary_values
第二步:IO密集型任务优先用单线程/多线程,而非多进程
Python的多进程有额外的创建、调度开销,对于IO密集型任务,多线程(配合threading)或者甚至单线程(用大块读取)的性能通常更好——因为线程切换开销远低于进程,而且不需要进程间通信的成本。
第三步:修正循环条件
如果保留逐字节读取的逻辑,务必把or改成and,确保进程只读取分配给自己的块:
while data != b'' and pointer_position < pointer_to:
优化后,单线程读取10MB文件的耗时会从30秒降到毫秒级,多线程/多进程的性能也会和单线程接近(NVMe带宽足够的话,单线程大块读取已经能跑满带宽)。
内容的提问来源于stack exchange,提问作者AndriiTalksTech

