列表并行求和场景下ThreadPoolExecutor为何更高效?如何优化ProcessPoolExecutor?
ThreadPoolExecutor 优于 ProcessPoolExecutor 的原因及 ProcessPool 优化方案
为什么 ThreadPoolExecutor 效率更高?
- 进程的固有开销远大于线程:Python中每个进程拥有独立的内存空间,创建、销毁进程以及进程间通信(IPC)的成本极高。你的任务是简单的整数加法,计算量极小,进程的启动/通信开销完全盖过了并行计算的收益,反而拖慢了整体速度。而线程共享父进程内存,创建、切换、通信的开销都可以忽略不计。
- 循环内重复创建 Executor 放大了进程开销:代码在每次循环中都新建
ProcessPoolExecutor,意味着每次循环都要重新创建一组子进程,这个初始化过程的成本非常高。相比之下,ThreadPoolExecutor创建线程的成本极低,重复创建的影响微乎其微。 - 任务粒度太细:单个加法任务的执行时间几乎可以忽略,进程调度和IPC的时间远超过任务本身的执行时间。ProcessPool适合处理CPU密集型且任务粒度较大的工作,这种细粒度任务完全发挥不出它的优势。
如何优化 ProcessPoolExecutor 的性能?
1. 复用进程池,避免重复创建
把ProcessPoolExecutor的创建移到循环外部,全程复用同一组进程,避免每次循环都重新初始化进程的开销。
2. 避免跨进程访问外部变量
当前代码中group_cont访问外部的add_list,ProcessPool会把这个列表序列化后传到子进程,每次循环都要传递数据,增加了额外开销。应该直接把需要相加的数值作为参数传给函数。
3. 增大任务粒度
将多个加法任务打包成一个批次,减少进程调度和IPC的次数。比如一次处理多个数对的加法,而不是单个。
4. 合理设置进程池大小
默认进程池大小是CPU核心数(你的8核),过多的进程会增加调度开销,对于细粒度任务,可以适当减少进程数量,比如设置为4。
优化后的代码示例
import concurrent.futures import time add_list = list(range(100)) temp_len = len(add_list) loop_start = time.time() # 复用进程池,放在循环外面 with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor: while temp_len > 1: is_odd = temp_len % 2 group_len = temp_len // 2 + is_odd # 直接生成需要相加的数值对,而不是索引,避免子进程访问外部列表 groups = [(add_list[2*i], add_list[2*i+1]) for i in range(group_len - is_odd)] last = add_list[-1] if is_odd else None def add_pair(pair): return pair[0] + pair[1] results = executor.map(add_pair, groups) add_list = list(results) if is_odd: add_list.append(last) temp_len = group_len loop_end = time.time() print(f'Time: {round(loop_end - loop_start, 3)}')
内容的提问来源于stack exchange,提问作者Wei-jia Huang
相关产品推荐
相关产品推荐

