如何正确统计Python multiprocessing并行计算的总CPU时间?
当用process_time()包裹multiprocessing的并行计算代码时,得到的CPU时间仅统计了父进程的耗时,完全没包含子进程(以及Pool的后台管理进程)的CPU使用,导致结果严重偏低。比如你给出的示例中,并行计算的CPU时间只有0.04秒左右,远低于实际计算消耗的CPU资源。
问题根源
process_time()是进程级别的统计函数,它只记录当前调用进程的用户态+内核态CPU时间,不会追踪子进程或其他相关进程的时间。而multiprocessing.Pool会创建独立的子进程执行任务,还可能有后台进程处理任务分发、数据序列化/反序列化等操作,这些进程的CPU时间都不会被父进程的process_time()统计到。
解决方案
方案1:子进程自行统计CPU时间并汇总
如果主要关注计算任务本身的CPU消耗,可以在每个worker函数内统计自身的CPU时间,将计算结果和耗时一起返回,最后在父进程中求和所有子进程的耗时,再加上父进程自身的耗时(如果需要)。
修改后的示例代码:
from time import perf_counter as pc, process_time as pt from multiprocessing import Pool n_workers = 2 def worker(k): worker_pt_start = pt() total = 0 for i in range(2*10**7): total += i worker_cpu_time = pt() - worker_pt_start return total, worker_cpu_time if __name__ == '__main__': print('Serial computation') pc_start = pc() pt_start = pt() results = n_workers * [0] total_serial_cpu = 0 for k in range(n_workers): res, cpu_time = worker(k) results[k] = res total_serial_cpu += cpu_time print(f' Total run time: {pc() - pc_start} seconds') print(f' Total CPU time: {total_serial_cpu} seconds') print('Parallel computation') pc_start = pc() parent_pt_start = pt() with Pool() as pool: results = pool.map(worker, range(n_workers)) parent_cpu_time = pt() - parent_pt_start # 汇总所有子进程的CPU时间 total_parallel_cpu = sum(cpu_time for _, cpu_time in results) + parent_cpu_time print(f' Total run time: {pc() - pc_start} seconds') print(f' Total CPU time (子进程+父进程): {total_parallel_cpu} seconds')
这个方案能准确统计所有worker子进程的计算CPU时间,加上父进程的耗时。但要注意:Pool的后台管理进程(比如负责任务调度、数据序列化的进程)的CPU时间不会被统计到,如果你的场景中这些后台操作的CPU消耗不可忽略,这个方案会有误差。
方案2:使用操作系统层面的工具统计(最准确)
如果需要统计所有相关进程(包括Pool的后台进程、子进程、父进程)的总CPU时间,直接用操作系统自带的工具是最可靠的方式:
- Linux/macOS:在终端用
time命令运行脚本,比如:
输出中的time python your_script.pyuser字段是所有进程的用户态CPU时间总和,sys是内核态总和,两者相加就是总CPU时间。 - Windows:可以用PowerShell的
Measure-Command结合进程树分析,或者使用wmic命令追踪进程的CPU使用,更简单的方式是用Windows Terminal的time命令(或者第三方工具如busybox time),也能得到类似的总CPU时间统计。
操作系统层面的工具会追踪整个进程树的CPU消耗,不管是子进程还是后台管理进程,都能被准确统计,这是最全面的统计方式。
内容的提问来源于stack exchange,提问作者SapereAude

