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