Python joblib 在 n_jobs 大于 8 后几乎没有性能增益 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Leon6868
V2EX    Python

Python joblib 在 n_jobs 大于 8 后几乎没有性能增益

  •  
  •   Leon6868 353 天前 2096 次点击
    这是一个创建于 353 天前的主题,其中的信息可能已经有所发展或是发生改变。

    电脑 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 

    请问为什么会出现这种情况呢?有哪些办法能进一步提升性能呢?

    18 条回复    2024-10-22 12:31:15 +08:00
    BingoXuan
        1
    BingoXuan  
       353 天前   1
    因为你只有 8 核
    Leon6868
        2
    Leon6868  
    OP
       353 天前
    @BingoXuan #1 请问为什么用上 8 核后相比 1 程也只有 50% 的性能提升呢?
    vicalloy
        3
    vicalloy  
       353 天前
    看一下每个核的 CPU 占用率,如果负载满了就是到性能瓶颈了。
    BingoXuan
        4
    BingoXuan  
       353 天前
    t2 才是你真正计算部分吧,其他都是处理数据。感觉就是处理数据拖后腿了。
    Donaldo
        5
    Donaldo  
       353 天前
    @Leon6868 #2 上下文切换,缓存同步,核间通信也有开销的,实际上很难(甚至可以说不可能)做到 N 个核的效率是 1 个核的 N 倍。
    Leon6868
        6
    Leon6868  
    OP
       353 天前
    @BingoXuan #4 是的, `n_jobs` 小于 8 时 `t2` 几乎没影响,但是为什么这些数据处理代码会随着 `n_jobs` 增大而线性变慢呢,不同进程之间理论上应该不会相互影响?
    vicalloy
        7
    vicalloy  
       353 天前
    进程多变慢是正常的,线程切换是有代价的。
    NoOneNoBody
        8
    NoOneNoBody  
       353 天前   1
    windows
    python 多进程还有很多消耗,基本上达不到 total/n 这么完美的效果
    然后,你需要一些特殊的包,控制 CPU 亲和度,把闲置的 CPU 核分给进程
    另外,我的经验是外部跑一些消耗的软件,如播放器、浏览器,python 多进程的效率会大幅降低,只有保留 CPU 专用,才能保持一个相对较高效率
    还有内存,当内存用满,也是会大幅效率降低

    如果数据不是十份庞大,多进程提升不大,数据庞大且内存足够,建议想办法跑 numba ,如果实在难以跑 numba ,也要尽量用 np/pd 的向量函数

    你这里用了大量 append ,考虑一下换成一次生成的思路
    或者改写方式,就是预置长度,所有元素为默认值,然后定位再赋值计算结果
    yzongyue
        9
    yzongyue  
       353 天前
    瓶颈不一定在 cpu , 跑代码的时候,把任务管理器打开, 看看是不是内存/磁盘 io 满了
    Leon6868
        10
    Leon6868  
    OP
       353 天前
    @yzongyue #9 CPU 满的, 内存和 io 没满,数据是全部加载到内存中然后再处理的
    Riyue
        11
    Riyue  
       353 天前
    numpy 底层的 mkl openblas 自动利用多核,不知道会不会是原因之一
    Clouder1
        12
    Clouder1  
       353 天前   1
    一个原因是你只有 8cores ,另一部分原因是:多进程本身就有开销,比如启动进程的开销、进程通信拷贝数据的开销,以及如果你使用的某些库本身就会使用多线程、向量化之类的手段优化性能,开太多进程其实对并行计算也没有多少帮助。提名 numpy/polars 等。
    如果想要逐行 profile ,可以参考 https://stackoverflow.com/questions/3927628/how-can-i-profile-python-code-line-by-line
    timethinker
        13
    timethinker  
       353 天前   1
    详见:阿姆达尔定律
    JacHammer
        14
    JacHammer  
       353 天前
    更像是 CPU 撞功耗/温度墙了。在占用 CPU 核心数不多时,每个核都会以较高频率和功率运行;等每个核心逐渐被占用时,所有的核心也会逐渐降低频率和功率,自然此时的单核性能会下降;如果在笔记本电脑这种对功耗/温度限制大的设备上运行则尤其如此。
    当然还有楼上提到的各种非硬件开销等等,但我并不认为非硬件原因为主要因素。你可以试试在桌面或者服务器 CPU 等没有太大功率与温度限制的环境下进行相同测试,总用时统计曲线应该会更加线性。
    hertzry
        15
    hertzry  
       353 天前 via iPhone
    试试 Multiprocessing 吧。
    BingoXuan
        16
    BingoXuan  
       353 天前
    @Leon6868
    我在怀疑是缓存击中率太低了。处理数据过程需要不断交换内存数据。进程越多,每个进程需要读写数据页面过于分散,导致缓存不断刷新。单进程和 fft 处理不存在这个问题。

    我建议你可以用 mmap 共享一段内存,然后流水线处理各个步骤。你可以让 gpt 修改一下测试效果如何。
    volvo007
        17
    volvo007  
       353 天前 via iPhone
    主要是核心数就是 8 ,想提速的话用 numpy 或者 polars 改写。初看似乎你这个循环可以完全干掉写成向量计算
    deplives
        18
    deplives  
       352 天前
    AMD Ryzen 7 6800H
    一共就 8 核,再多开之后,核间通讯,数据同步,进程的上下文切换,就成了速度的主要原因。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5818 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 02:43 PVG 10:43 LAX 19:43 JFK 22:43
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86