Python multiprocessing.Process为何未充分利用全部CPU核心?
核心未充分利用的可能原因
1. Intel大小核架构的调度特性
你的i5-12600K是大小核混合架构:6个性能核(P-Core)+4个能效核(E-Core),超线程后显示16个逻辑核心。Windows调度器默认会把CPU密集型任务优先分配给P核,但如果你的任务存在启动开销小、优先级低等特性,系统可能反而将它们集中分配到E核上——而E核恰好是4个,这就会出现4个核心满负载、其余核心闲置的情况。
2. 子进程内部的多线程抢占
如果save_betas_over_window函数中用到了numpy、pandas这类依赖BLAS/LAPACK库的计算,这类库默认会启用多线程加速(比如MKL、OpenBLAS)。假设每个子进程启动后占用4个线程,4个子进程就会占满16个逻辑核心,但资源监视器显示的是物理核心负载,4个物理核满负载(每个核跑2个线程),其余物理核闲置,看起来就像只有4个核心在工作。
3. 任务计算量不均衡
你生成的windows列表是[30, 60, ..., 480],窗口越大,save_betas_over_window的计算量可能呈线性甚至非线性增长。窗口小的任务会很快执行完毕,对应的核心会闲置;只有窗口大的任务还在运行,导致后期只有少数核心处于高负载状态,看起来全程只有部分核心在工作。
4. Windows进程调度的随机性
虽然multiprocessing会启动指定数量的进程,但Windows调度器不会强制把进程均匀分配到所有核心上。如果调度器倾向于把多个进程放在同一组核心,就会出现部分核心过载、其余核心闲置的情况。
解决方法
1. 限制子进程内部的线程数
如果是BLAS库的多线程导致的问题,在代码开头添加环境变量设置,强制每个子进程只用1个线程:
import os os.environ["OMP_NUM_THREADS"] = "1" os.environ["MKL_NUM_THREADS"] = "1" os.environ["OPENBLAS_NUM_THREADS"] = "1"
这样每个子进程只会占用1个逻辑核心,16个进程就能均匀利用所有逻辑核心。
2. 调整进程数量为物理核心数
multiprocessing.cpu_count()返回的是逻辑核心数(16),但物理核心只有10个。试试把进程数量改成物理核心数,避免超线程带来的调度冲突:
from multiprocessing import cpu_count import psutil # 获取物理核心数 physical_cores = psutil.cpu_count(logical=False) windows = np.linspace(30, physical_cores*30, physical_cores).astype(int)
3. 手动设置进程的CPU亲和性
使用psutil库手动把每个子进程绑定到不同的核心上,强制调度器分配资源:
import psutil # ... 你的原有代码 ... for idx, process in enumerate(processes): # 将进程绑定到第idx个逻辑核心 psutil.Process(process.pid).cpu_affinity([idx])
4. 均衡任务计算量
如果是窗口大小导致的计算量不均衡,可以调整windows的生成方式,让每个任务的计算量尽量接近。比如采用对数间隔生成窗口,或者把大窗口拆分成多个小任务,避免部分进程长期占用核心。
内容的提问来源于stack exchange,提问作者Abdullah Bahi

