电脑 CPU 为 AMD Ryzen 7 6800H
,8 核 16 进程
系统为 Windows 11
任务为对一组数据做分段 FFT ,因为每段 FFT 相互无关,所以将整段数据分为 n_jobs
块后每块并行计算,试图加速(具体代码不能公开,正在整理一份能复现的代码)。但是发现了奇怪的情况,保持其他参数不变,使用 parallel = Parallel(n_jobs=int(n_jobs))
,当 n_jobs
大于 4 后,总体用时不会下降。
深入进程测试后发现每个进程内部的 for
循环内的代码拖慢了速度,代码如下(正常来说怎么测试每行代码的性能呢……望大佬指路!):
tCost = [] for i in batchTask: tCost.append([time.time()]) startTime = sampleDot[0] + i * step endTime = startTime + step splitSampleDot = sampleDot[ np.where((sampleDot >= startTime) & (sampleDot < endTime)) ] tCost[-1].append(time.time() - tCost[-1][0]) # ckpt t0 splitData = np.array(list(zip(splitSampleDot, linearData(splitSampleDot)))) tCost[-1].append(time.time() - tCost[-1][0]) # ckpt t1 signal, powerDensity = getFftResult( splitData, splitSampleDot, float(sampleRate), 0.0, 0.8, float(minFreq), ) tCost[-1].append(time.time() - tCost[-1][0]) # ckpt t2 powerDensity[powerDensity < displayThreshold] = np.nan tCost[-1].append(time.time() - tCost[-1][0]) # ckpt t3 fftDataList.append(powerDensity) fftFreqList.append(signal) fftStartTimeList.append(datetime.fromtimestamp(startTime)) tCost[-1].append(time.time() - tCost[-1][0]) # ckpt t4 realTimeDateObjList.append( mdates.date2num(np.vectorize(datetime.fromtimestamp)(splitSampleDot)) ) tCost[-1].append(time.time() - tCost[-1][0]) # ckpt t5
请问为什么会出现这种情况呢?有哪些办法能进一步提升性能呢?
![]() | 1 BingoXuan 353 天前 ![]() 因为你只有 8 核 |
![]() | 3 vicalloy 353 天前 看一下每个核的 CPU 占用率,如果负载满了就是到性能瓶颈了。 |
![]() | 4 BingoXuan 353 天前 t2 才是你真正计算部分吧,其他都是处理数据。感觉就是处理数据拖后腿了。 |
![]() | 6 Leon6868 OP @BingoXuan #4 是的, `n_jobs` 小于 8 时 `t2` 几乎没影响,但是为什么这些数据处理代码会随着 `n_jobs` 增大而线性变慢呢,不同进程之间理论上应该不会相互影响? |
![]() | 7 vicalloy 353 天前 进程多变慢是正常的,线程切换是有代价的。 |
8 NoOneNoBody 353 天前 ![]() windows python 多进程还有很多消耗,基本上达不到 total/n 这么完美的效果 然后,你需要一些特殊的包,控制 CPU 亲和度,把闲置的 CPU 核分给进程 另外,我的经验是外部跑一些消耗的软件,如播放器、浏览器,python 多进程的效率会大幅降低,只有保留 CPU 专用,才能保持一个相对较高效率 还有内存,当内存用满,也是会大幅效率降低 如果数据不是十份庞大,多进程提升不大,数据庞大且内存足够,建议想办法跑 numba ,如果实在难以跑 numba ,也要尽量用 np/pd 的向量函数 你这里用了大量 append ,考虑一下换成一次生成的思路 或者改写方式,就是预置长度,所有元素为默认值,然后定位再赋值计算结果 |
![]() | 9 yzongyue 353 天前 瓶颈不一定在 cpu , 跑代码的时候,把任务管理器打开, 看看是不是内存/磁盘 io 满了 |
![]() | 11 Riyue 353 天前 numpy 底层的 mkl openblas 自动利用多核,不知道会不会是原因之一 |
12 Clouder1 353 天前 ![]() 一个原因是你只有 8cores ,另一部分原因是:多进程本身就有开销,比如启动进程的开销、进程通信拷贝数据的开销,以及如果你使用的某些库本身就会使用多线程、向量化之类的手段优化性能,开太多进程其实对并行计算也没有多少帮助。提名 numpy/polars 等。 如果想要逐行 profile ,可以参考 https://stackoverflow.com/questions/3927628/how-can-i-profile-python-code-line-by-line |
![]() | 13 timethinker 353 天前 ![]() 详见:阿姆达尔定律 |
14 JacHammer 353 天前 更像是 CPU 撞功耗/温度墙了。在占用 CPU 核心数不多时,每个核都会以较高频率和功率运行;等每个核心逐渐被占用时,所有的核心也会逐渐降低频率和功率,自然此时的单核性能会下降;如果在笔记本电脑这种对功耗/温度限制大的设备上运行则尤其如此。 当然还有楼上提到的各种非硬件开销等等,但我并不认为非硬件原因为主要因素。你可以试试在桌面或者服务器 CPU 等没有太大功率与温度限制的环境下进行相同测试,总用时统计曲线应该会更加线性。 |
15 hertzry 353 天前 via iPhone 试试 Multiprocessing 吧。 |
![]() | 16 BingoXuan 353 天前 @Leon6868 我在怀疑是缓存击中率太低了。处理数据过程需要不断交换内存数据。进程越多,每个进程需要读写数据页面过于分散,导致缓存不断刷新。单进程和 fft 处理不存在这个问题。 我建议你可以用 mmap 共享一段内存,然后流水线处理各个步骤。你可以让 gpt 修改一下测试效果如何。 |
17 volvo007 353 天前 via iPhone 主要是核心数就是 8 ,想提速的话用 numpy 或者 polars 改写。初看似乎你这个循环可以完全干掉写成向量计算 |
18 deplives 352 天前 AMD Ryzen 7 6800H 一共就 8 核,再多开之后,核间通讯,数据同步,进程的上下文切换,就成了速度的主要原因。 |