大矩阵Numpy多进程性能无提升原因咨询(Ryzen 9 3900X)
大矩阵多进程场景下性能无提升的原因解析
实验背景与现象
我用Python测试多进程对随机矩阵乘法的性能加速比,任务完全独立无对象共享。原本预期不同矩阵尺寸的性能曲线一致,但结果显示矩阵尺寸较大(80、160)时,增加进程数几乎无法提升性能。
我的CPU为AMD Ryzen™ 9 3900X,规格如下:
Product Family: AMD Ryzen™ Processors Product Line: AMD Ryzen™ 9 Desktop Processors # of CPU Cores: 12 # of Threads: 24 Max. Boost Clock: Up to 4.6GHz Base Clock: 3.8GHz L1 Cache: 768KB L2 Cache: 6MB L3 Cache: 64MB
实验结果表现为:小矩阵(5、10、20、40)随进程数增加加速比明显提升,大矩阵(80、160)加速比几乎维持在1左右。
实验代码
主测试脚本
import numpy as np import pickle from dataclasses import dataclass import time import multiprocessing import os import subprocess import numpy as np def split_number(n_total, n_split): return [n_total // n_split + (1 if x < n_total % n_split else 0) for x in range(n_split)] def task(args): n_iter, idx, matrix_size = args #cores = "{},{}".format(2 * idx, 2 * idx+1) #os.system("taskset -p -c {} {}".format(cores, os.getpid())) for _ in range(n_iter): A = np.random.randn(matrix_size, matrix_size) for _ in range(100): A = A.dot(A) def measure_time(n_process: int, matrix_size: int) -> float: n_total = 100 assigne_list = split_number(n_total, n_process) pool = multiprocessing.Pool(n_process) ts = time.time() pool.map(task, zip(assigne_list, range(n_process), [matrix_size] * n_process)) elapsed = time.time() - ts return elapsed if __name__ == "__main__": n_experiment_sample = 5 n_logical = os.cpu_count() n_physical = int(0.5 * n_logical) result = {} for mat_size in [5, 10, 20, 40, 80, 160]: subresult = {} result[mat_size] = subresult for n_process in range(1, n_physical + 1): elapsed = np.mean([measure_time(n_process, mat_size) for _ in range(n_experiment_sample)]) subresult[n_process] = elapsed print("{}, {}, {}".format(mat_size, n_process, elapsed)) with open("result.pkl", "wb") as f: pickle.dump(result, f)
绘图脚本
import numpy as np import matplotlib.pyplot as plt import pickle with open("result.pkl", "rb") as f: result = pickle.load(f) fig, ax = plt.subplots() for matrix_size in result.keys(): subresult = result[matrix_size] n_process_list = list(subresult.keys()) elapsed_time_list = np.array(list(subresult.values())) speedups = elapsed_time_list[0] / elapsed_time_list ax.plot(n_process_list, speedups, label=matrix_size) ax.set_xlabel("number of process") ax.set_ylabel("speed up compared to single process") ax.legend(loc="upper left", borderaxespad=0, fontsize=10, framealpha=1.0) plt.show()
原因分析
1. 内存带宽瓶颈
大矩阵运算对内存带宽的需求远高于小矩阵:
- 以160x160的float64矩阵为例,单个矩阵占用
160*160*8 = 204800字节 ≈ 200KB,但每次矩阵乘法需要频繁读写内存中的矩阵数据,加上循环100次的迭代,会产生海量的内存访问请求。 - 当多进程同时运行时,所有进程都会争抢CPU的内存带宽资源。Ryzen 9 3900X的内存带宽是固定的(DDR4-3200约51GB/s),当多个进程同时发起内存访问时,带宽会被占满,此时CPU核心即使空闲也无法获取足够的数据,导致计算等待内存数据,性能无法随进程数提升。
2. CPU缓存利用率下降
- 小矩阵可以完全放入L2或L3缓存中,运算时无需频繁从主存读取数据,CPU核心能高效利用缓存进行计算,多进程并行时每个核心的缓存都能发挥作用,加速比明显。
- 大矩阵(比如160x160)的尺寸超过了单核心L2缓存(每个核心512KB L2,3900X共12核心,总L2 6MB),甚至接近L3缓存总容量(64MB)。当多进程同时处理大矩阵时,缓存会被多个进程的矩阵数据填满,频繁出现缓存失效,不得不从主存加载数据,进一步加剧内存带宽的压力,导致并行效率骤降。
3. 浮点运算单元(FPU)与内存带宽的平衡
- 小矩阵运算时,CPU的FPU是瓶颈:计算量小,内存访问少,多进程可以充分利用多个FPU核心,提升性能。
- 大矩阵运算时,内存带宽成为瓶颈:FPU的计算能力远超过内存数据供给速度,即使增加进程数,FPU大部分时间处于等待数据的状态,无法发挥并行计算的优势。
验证建议
- 可以尝试绑定进程到物理核心(取消代码中
taskset相关注释),减少线程调度开销,但本质上无法解决内存带宽瓶颈。 - 测试更大的内存带宽配置(比如升级到DDR4-3600或更高),观察大矩阵的并行性能是否有提升。
- 减少每个进程的矩阵运算循环次数,降低内存访问压力,看并行加速比是否恢复。
内容的提问来源于stack exchange,提问作者HiroIshida
相关产品推荐
相关产品推荐

