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
firejoke
V2EX  ›  Python

控制子进程的 cpu 使用率

  •  
  •   firejoke · 2022-01-19 23:37:07 +08:00 · 3496 次点击
    这是一个创建于 1067 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想到一个控制子进程的 cpu 使用率的简单方法。

    import platform
    import time
    from multiprocessing import Process, JoinableQueue
    
    import psutil
    
    from settings import CPU_FREQ, MIN_SLEEP_TIME_OF_SUBPROCESS, testLog
    # CPU_FREQ = psutil.cpu_freq().max * pow(10, 9)
    # MIN_SLEEP_TIME_OF_SUBPROCESS = 1 / CPU_FREQ
    
    
    def subp(queue: JoinableQueue):
        while 1:
            if queue.empty():
                _st = 0
            else:
                _st = queue.get()
                queue.task_done()
            time.sleep(_st)
    
    
    if __name__ == '__main__':
        sysv = platform.system().lower()
        expect = 90
        queue = JoinableQueue()
        queue.put(MIN_SLEEP_TIME_OF_SUBPROCESS)
        sp = Process(target=subp, args=(queue,))
        sp.start()
        spi = psutil.Process(sp.pid)
        if sysv == "windows":
            spi.nice(psutil.HIGH_PRIORITY_CLASS)
        else:
            spi.nice(28)
        if sysv != "windows":
            spi.ionice(psutil.IOPRIO_CLASS_RT, value=7)
        testLog.info(f"pid: {spi.pid}, ppid: {spi.ppid()}")
    
        time.sleep(5)
        while 1:
            scpu = spi.cpu_percent(MIN_SLEEP_TIME_OF_SUBPROCESS)
            if scpu >= expect:
                testLog.info(f"sub-process cpu: {scpu}")
                if sysv != "windows":
                    testLog.info(f"sub-process cpu_num: {spi.cpu_num()}")
                st = 1 / (((scpu - expect) / 100) * CPU_FREQ)
                testLog.info(f"st: {st}")
            else:
                # st = MIN_SLEEP_TIME_OF_SUBPROCESS
                st = 0
            queue.put(st)
    
    

    原理如下:
    if 子进程的 cpu 占用率 > 预期:
        1 / ((当前的占用率- 预期)/ 满载值 * cpu 频率。)
    这个值就是子进程需要休眠的时间。
    子进程只使用单核时,满载值就是 100 ,子进程再开孙子进程的情况还没考虑。
    实测能达到预想效果。
    各位还有其他的方案可以借鉴下吗?

    14 条回复    2022-01-24 21:55:23 +08:00
    wevsty
        1
    wevsty  
       2022-01-19 23:41:20 +08:00
    windows 下面直接设置 job 就可以限制 cpu 使用了。
    junnplus
        2
    junnplus  
       2022-01-19 23:53:01 +08:00
    cgroups 不行么
    Buges
        3
    Buges  
       2022-01-19 23:57:01 +08:00 via Android
    win 不清楚,Linux 下直接 cgroup
    ClericPy
        4
    ClericPy  
       2022-01-20 00:00:19 +08:00
    cgroups 基本版本答案一样地在各种环境里存在

    虽然我平时用 nice 设置个低优先级就够使了, 但是我把内核占满时间一长会被 kill 不知道咋回事, 所以似乎也需要限制一下 cpu 了(否则就得 sleep 一会了)
    firejoke
        5
    firejoke  
    OP
       2022-01-20 09:32:13 +08:00
    @wevsty #1
    @junnplus #2
    @Buges #3
    @ClericPy #4
    我需要应用的场景,是在资源空闲的时候,用尽预期的所有资源,而在有其他进程也需要占用资源时,能动态的让出资源。
    也可以用 cgroups 设定进程组占用 cpu 时间比其他进程组要少,但这样我就得为每一个可能启用的其他任务规划资源占比,并且每次都要手动修改已有的 cgroup 参数。
    所以就想着,如果能自动动态的在程序成面规划资源占用的话,就能少动手了😋
    wevsty
        6
    wevsty  
       2022-01-20 09:37:40 +08:00
    @firejoke
    只是为了系统空闲的时候能使用 CPU 资源,忙的时候不要占用的话,Windows 下面直接设置进程优先级就行了。

    设置进程优先级为最低,这样 Windows 会尽可能的调度其他进程,只有空闲的时候才会把 CPU 时间交给低优先级进程。
    ClericPy
        7
    ClericPy  
       2022-01-20 10:13:29 +08:00
    @firejoke

    我也是这个场景啊, 所以我用的 nice 把优先级调低凑合用着, 实际效果没发现多好, 想做自动伸缩资源占用的场景很多, 比如 Hadoop 上想用空闲资源计算不重要的离线, 那里提供的优先级队列在队列已经启动的时候依然没法让出 CPU 来...

    同关注一下看看有没有更好方案
    firejoke
        8
    firejoke  
    OP
       2022-01-20 10:38:14 +08:00
    @wevsty #6
    在系统成面设置进程优先级来控制资源调度确实要方便很多,但就像 @ClericPy #7 描述的,操作系统内的资源调度对我们来说约等于是黑盒,不能达到想要的效果,所以如果能在程序成面做到控制的话,可控度就高多了,希望能找到更合适的方案。
    2i2Re2PLMaDnghL
        9
    2i2Re2PLMaDnghL  
       2022-01-20 11:02:02 +08:00
    @ClericPy dmesg 看下 kill 的原因? nice 值好像会影响到 OOM kill 的顺序。
    (不如用竞价实例(误
    wevsty
        10
    wevsty  
       2022-01-20 11:33:47 +08:00
    @firejoke
    常见的系统内核都被人研究的比较透彻了,所以这些内核提供的资源调度方式都是比较好预期的。

    以调度的眼光来看,一个耗时任务所需要的 CPU 时间是无穷大的(因为无法预知具体需要多少时间来完成任务),而 CPU 时间显然是有限的。
    对 Windows 来说,Windows 会优先保证在调度周期内每个线程都能得到一定时间的执行的机会,在此基础上,剩余的时间按照优先级分配给各个线程。也就是说,进程优先级实际上保证的也是任务在系统中所占用的时间比例。
    我不知道所谓低优先级任务启动时不能让出 CPU 时间是一个什么概念,但是我十分确定,在高优先级线程需要 CPU 时间时,对比低优先级线程高优先级线程会获得更多的 CPU 时间。
    在 Windows 中,还可以给进程设置 JOBOBJECT_CPU_RATE_CONTROL_INFORMATION ,可以设定 JOB 中进程使用的 CPU 占比或者时间,可以设定为动态比值,也可以设定绝对周期。这样可以比较好的控制 CPU 时间的分配。

    最后资源调度本来就是内核的活,用户态根本拿不到准确的数据来进行调度。
    如果内核提供的优先级+CPU 时间限制还满足不了你的需求,那你可能需要的是自己定制一个系统内核。
    ClericPy
        11
    ClericPy  
       2022-01-20 12:42:54 +08:00
    @2i2Re2PLMaDnghL
    问题就是不是 OOM Kill 的, python 里多进程里每个进程高并发协程跑满 CPU, 内存只用了 200 多 MB, 任务主要就是流式下载图片并流式上传到 S3... 连个 SIGABRT 都没发, 看 journal 也没看出啥东西

    过段时间看看整 Serverless 上了, 竞价实例早就想用了, 但是运维跑路了申请不下来
    firejoke
        12
    firejoke  
    OP
       2022-01-20 17:41:36 +08:00
    @wevsty #10 我晚上回去试试,不过 psutil 库只能设置指定进程的优先级,在 Windows 是用 SetPriorityClass 实现的,只有几个固定优先级和后台模式。
    firejoke
        13
    firejoke  
    OP
       2022-01-20 20:25:32 +08:00
    @wevsty #10 在 Windows 上测了。

    测试环境:
    测试中把所有进程包括子进程都调整到同一 cpu 核上;
    运行中通过任务管理器确认了程序内设置的优先级是生效的;
    子进程用 multiprocessing.Process 生成的,在 Windows 上是 spawn 模式,会启动一个全新的解释器运行子进程。

    测试结果:
    两个独立进程之间,可以通过设置优先级,让优先级高的占用更多 cpu 时间,而相同优先级的会竞争。
    同一父进程的两个子进程之间,优先级没起到作用;
    不同父进程的两个子进程之间,不论是设置父进程的优先级还是子进程的优先级或者全都配置,两个子进程仍然会竞争。
    woodpenker
        14
    woodpenker  
       2022-01-24 21:55:23 +08:00
    kill STOP/CONT + sleep 就搞定了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2505 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 03:42 · PVG 11:42 · LAX 19:42 · JFK 22:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.