Python多进程starmap提速效果不明显,该使用方式是否正确?
核心结论
你对starmap的语法使用没有错误,当前提速效果差属于正常现象,问题出在任务设计不符合多进程加速的适用场景。
问题原因分析
- 任务粒度过细:你定义的
add_one仅包含一次判断和加法运算,单步执行耗时不到1微秒,但你向进程池提交了1000万个独立任务。每一个任务都需要经过pickle序列化→跨进程传输→子进程执行→结果序列化→回传主进程的完整流程,累计的进程间通信(IPC)开销已经远大于计算本身的开销,多进程带来的计算提速被通信成本完全抵消。 - MacOS平台的进程启动开销:MacOS下Python的
multiprocessing默认使用spawn模式创建子进程,子进程启动时会拷贝父进程的全部资源,你提前生成的1000万元素的列表本身占用内存接近80MB,子进程初始化的额外开销也会拉低整体效率。 - 资源竞争:1000万任务的调度、队列读写本身也会产生CPU资源竞争,进一步抵消多核收益。
优化方案
核心思路是放大单任务的计算粒度,减少跨进程通信次数:
将全量列表拆分为和进程数匹配的大块,每个子进程一次性处理一整个块的数据,仅在块处理完成后再回传结果,把IPC次数从1000万次降到和进程数一致的个位数。
优化后的示例代码:
from multiprocessing import Pool from itertools import repeat import timeit import multiprocessing def add_one_batch(numbers_batch, flag): # 单进程一次性处理一个批次的所有数据 res = [] if flag == "a": for num in numbers_batch: res.append((num, num+1)) else: for num in numbers_batch: res.append((num, None)) return res if __name__ == "__main__": numbers = list(range(10000000)) # 拆分批次:按CPU核心数拆分,也可以根据实际情况调整批次大小 cpu_cnt = multiprocessing.cpu_count() batch_size = len(numbers) // cpu_cnt batches = [numbers[i*batch_size : (i+1)*batch_size] for i in range(cpu_cnt)] # 处理最后一个不足batch_size的批次 if len(numbers) % cpu_cnt != 0: batches.append(numbers[cpu_cnt*batch_size:]) pool = Pool(cpu_cnt) for i in range(3): print(i) start_time = timeit.default_timer() flag = "a" # 提交批次任务,合并返回结果 batch_results = pool.starmap(add_one_batch, zip(batches, repeat(flag))) new_numbers = [item for batch in batch_results for item in batch] print('time taken: ', timeit.default_timer() - start_time)
调整后你可以看到明显的多核提速效果,理论上8核设备可以拿到5-7倍左右的提速。
另外补充两个优化注意点:
- 不要在全局作用域创建大对象和进程池,放到
if __name__ == "__main__":块内,避免spawn模式下子进程重复执行全局代码,额外浪费资源。 - 如果是数值计算场景,优先用numpy等向量化运算库处理,单进程效率就远高于纯Python循环的多进程实现。
内容的提问来源于stack exchange,提问作者marlon
相关产品推荐
相关产品推荐

