Python中用multiprocessing做素数计算,为何没用到全部CPU核心?
核心问题根源
导致性能完全没有提升的最核心原因,是代码导入的根本不是真的多进程实现:
- 代码中写的导入语句是
from multiprocessing.dummy import Pool,这个dummy模块是Python为了统一多进程/多线程API做的线程封装层,本质是基于threading实现的多线程池,全程没有创建独立的操作系统进程,仍然受Python GIL的严格约束。 - 这种实现下,哪怕设置了12个线程,在CPU密集型的素数计算场景中,同一时刻永远只有一个线程能获得Python解释器的执行权,根本没法实现真的并行计算。你观测到的“多进程运行时CPU占用12%、单线程运行时CPU占用10%”的现象,正好匹配12逻辑线程的CPU只跑满1个线程算力的特征——单线程纯计算的CPU占用大概在8%~10%,和实测数值完全吻合。
需要澄清一个常见认知误区:multiprocessing确实可以绕过GIL实现多核并行,但前提是使用原生的
multiprocessing进程池,而不是带dummy标识的线程兼容版本。
代码中影响并行效率的其他细节问题
哪怕修正导入语句,当前代码还有几个会拉低并行收益的问题:
- 无分块的
pool.map(prime, loop)模式任务粒度过小:每个任务仅需判断一个数是不是素数,进程间通信需要对每个任务做序列化、传递、反序列化,当计算收益远低于通信开销时,并行带来的提升会被完全抵消。 - 进程池大小硬编码为12:对于CPU密集型任务,过多的进程会带来不必要的上下文切换开销,一般进程数设置为物理核心数时效率最高。6核CPU对应开6个工作进程就足够,超线程的逻辑核心对纯计算任务的提升非常有限。
- 短时间单次测试的误差:Python多进程在Windows系统下默认用spawn模式启动,首次创建进程池时需要全量序列化主进程代码、创建独立内存空间,这个固定开销会被100万次素数计算的短测试放大,多次重复取平均的测试结果才会更准确。
正确的多核并行实现步骤
要获得预期的并行性能提升,按照以下方式调整即可:
- 替换导入语句,去掉
dummy标识:将from multiprocessing.dummy import Pool改为from multiprocessing import Pool,这样创建的才是拥有独立内存空间、完全绕开GIL的真进程池。 - 保留已实现的分块逻辑:把连续的一段待计算数字打包为一个任务传给子进程,减少进程间通信的次数。当单个任务包含几千到上万次素数判断时,通信开销的占比会降到可忽略的水平。
- 动态设置进程池大小:用
cpu_count()获取系统核心数,根据物理核心数调整工作进程数量,避免无意义的资源浪费。
按照上述调整后,在6核CPU上跑素数计算,能获得接近6倍的单线程性能提升,任务管理器也会观测到接近100%的整体CPU占用。
内容的提问来源于stack exchange,提问作者matth。
相关产品推荐
相关产品推荐

