测Queue传输1000万个float64、Pipe做2000次ping-pong、shared_memory零拷贝共享数组,看三种跨进程通信方式的代价差在哪。
进程之间默认内存不共享,得靠的IPC机制传数据。
测试的三种方式1. multiprocessing.Queue:子进程put一个1000万元素的numpy数组,父进程get 2. multiprocessing.Pipe:两个进程做2000次一来一回的ping-pong 3. multiprocessing.shared_memory:子进程直接原地修改共享的numpy数组,父进程直接读
Queue底层是把对象pickle序列化后通过管道传输。
结果Queue传输1000万个float64(约80MB): 总耗时 0.1924s 接收方收到并sum: 49999995000000.0(数据完整无误)
Queue可以传任何可pickle的Python对象,很通用,但代价是:发送方要把整个对象序列化成字节流,接收方要再反序列化回来——80MB的数组花了0.19秒,这个耗时会随数据量线性增长。
Pipe是两个进程之间一根直连的双向管道,延迟远低于Queue。
结果2000次ping-pong往返: 总耗时 0.0337s 平均单次往返: 16.9 微秒
对于小消息的高频往返,Pipe的单次延迟只有约17微秒——这类场景如果用Queue也能做,但Pipe是两个进程间专用的直连通道,没有Queue内部锁和缓冲队列那一层额外开销,更适合频繁的小数据交互(比如心跳、控制信号)。
如果数据是numpy这类支持buffer协议的类型,共享内存能让两个进程"看见"同一块物理内存。
结果子进程原地把共享数组每个元素乘以2,耗时0.1034s(主要是进程启动开销,不是数据传输开销) 父进程直接读取子进程写入的结果(不需要任何显式发送): shm_arr[5] = 10.0 (期望值 5×2=10,吻合)
父进程完全没有执行任何"接收"操作——子进程原地改了共享内存里的数据,父进程直接读同一块内存就看到了变化。这是三种方式里唯一做到"零拷贝"的——不需要序列化、不需要通过管道搬运数据,两个进程共享的是同一块物理内存。代价是:只能用于支持这种内存布局的数据类型(定长的数值数组等),不像Queue那样能传任意Python对象。
每一步都是本文上面用multiprocessing跑出来的原始结果。
multiprocessing.Queue / Pipe / shared_memory.SharedMemory:对比三种IPC方式