Python concurrent.futures多进程池无法随处理器数量扩展问题
问题分析:多进程执行耗时反而增加的原因与优化
核心原因拆解
你的测试结果不符合预期,本质是三个关键因素共同作用的结果:
1. 任务量随进程数线性增长
你的process函数中,iterobj = range(num_jobs)同时搭配max_workers=num_jobs,这意味着每增加一个进程,就会多执行一次test_multi函数。比如1进程执行1次列表构建,10进程执行10次完整的列表构建——总工作量是原来的10倍,耗时自然会大幅上升,这并非多进程本身的问题,而是任务逻辑设计错误。
2. 列表拼接的低效操作
test = test + [i]是性能杀手:每次拼接都会创建新列表并复制所有已有元素,时间复杂度为O(n²)。单进程下这已经导致52秒的耗时,多进程下每个进程都在重复这个高开销操作,进一步放大了总耗时。
3. Windows进程创建的额外开销
Windows下ProcessPoolExecutor使用spawn方式创建进程,每个子进程都需要重新加载Python解释器、导入模块。当任务数量多且单个任务耗时不够长时,这部分初始化开销会显著拉高总耗时。
优化方案
1. 修正任务逻辑:并行拆分单个任务
如果目标是用多进程加速单个列表的构建,需要将任务拆分为多个子区间,让不同进程协作完成,而非每个进程单独构建完整列表:
from datetime import datetime import concurrent.futures def test_multi(start, end): starttime = datetime.utcnow() # 用内置list(range)替代低效循环拼接,直接生成子区间列表 test = list(range(start, end)) return (datetime.utcnow()-starttime) def process(num_workers): total_elements = 200000 # 拆分任务为多个子区间 chunk_size = total_elements // num_workers chunks = [(i*chunk_size, (i+1)*chunk_size) for i in range(num_workers)] # 处理最后一个区间的剩余元素 if total_elements % num_workers != 0: chunks[-1] = (chunks[-1][0], total_elements) with concurrent.futures.ProcessPoolExecutor(max_workers=num_workers) as ex: results = list(ex.map(lambda x: test_multi(*x), chunks)) return results if __name__ == '__main__' : max_processors = 10 for i in range(max_processors): num_workers = i+1 starttime = datetime.utcnow() result = process(num_workers) finishtime = datetime.utcnow()-starttime if i == 0: chng = 0 total = 0 firsttime = finishtime else: chng = finishtime/lasttime*100 - 100 total = finishtime/firsttime*100 - 100 lasttime = finishtime print(f'Multi took {finishtime} for {num_workers} processes changed by {round(chng,2)}%, total change {round(total,2)}%')
2. 优化列表构建方式
无论是否用多进程,都要替换低效的列表拼接:
- 用
test.append(i)替代test = test + [i],时间复杂度降为O(n) - 直接用
list(range(200000))生成列表,这是Python内部优化的C实现,速度会提升几个数量级(单进程耗时从52秒降到毫秒级)
3. 合理控制进程数
即使是48核机器,进程数也无需开到48——进程调度、上下文切换会产生额外开销,通常设置为CPU核心数或核心数+1即可,避免过度调度。
内容的提问来源于stack exchange,提问作者WGee
相关产品推荐
相关产品推荐

