想到一个控制子进程的 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 ,子进程再开孙子进程的情况还没考虑。
实测能达到预想效果。
各位还有其他的方案可以借鉴下吗?
1
wevsty 2022-01-19 23:41:20 +08:00
windows 下面直接设置 job 就可以限制 cpu 使用了。
|
2
junnplus 2022-01-19 23:53:01 +08:00
cgroups 不行么
|
3
Buges 2022-01-19 23:57:01 +08:00 via Android
win 不清楚,Linux 下直接 cgroup
|
4
ClericPy 2022-01-20 00:00:19 +08:00
cgroups 基本版本答案一样地在各种环境里存在
虽然我平时用 nice 设置个低优先级就够使了, 但是我把内核占满时间一长会被 kill 不知道咋回事, 所以似乎也需要限制一下 cpu 了(否则就得 sleep 一会了) |
5
firejoke OP |
6
wevsty 2022-01-20 09:37:40 +08:00
@firejoke
只是为了系统空闲的时候能使用 CPU 资源,忙的时候不要占用的话,Windows 下面直接设置进程优先级就行了。 设置进程优先级为最低,这样 Windows 会尽可能的调度其他进程,只有空闲的时候才会把 CPU 时间交给低优先级进程。 |
7
ClericPy 2022-01-20 10:13:29 +08:00
@firejoke
我也是这个场景啊, 所以我用的 nice 把优先级调低凑合用着, 实际效果没发现多好, 想做自动伸缩资源占用的场景很多, 比如 Hadoop 上想用空闲资源计算不重要的离线, 那里提供的优先级队列在队列已经启动的时候依然没法让出 CPU 来... 同关注一下看看有没有更好方案 |
8
firejoke OP |
9
2i2Re2PLMaDnghL 2022-01-20 11:02:02 +08:00
@ClericPy dmesg 看下 kill 的原因? nice 值好像会影响到 OOM kill 的顺序。
(不如用竞价实例(误 |
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 时间限制还满足不了你的需求,那你可能需要的是自己定制一个系统内核。 |
11
ClericPy 2022-01-20 12:42:54 +08:00
@2i2Re2PLMaDnghL
问题就是不是 OOM Kill 的, python 里多进程里每个进程高并发协程跑满 CPU, 内存只用了 200 多 MB, 任务主要就是流式下载图片并流式上传到 S3... 连个 SIGABRT 都没发, 看 journal 也没看出啥东西 过段时间看看整 Serverless 上了, 竞价实例早就想用了, 但是运维跑路了申请不下来 |
12
firejoke OP @wevsty #10 我晚上回去试试,不过 psutil 库只能设置指定进程的优先级,在 Windows 是用 SetPriorityClass 实现的,只有几个固定优先级和后台模式。
|
13
firejoke OP @wevsty #10 在 Windows 上测了。
测试环境: 测试中把所有进程包括子进程都调整到同一 cpu 核上; 运行中通过任务管理器确认了程序内设置的优先级是生效的; 子进程用 multiprocessing.Process 生成的,在 Windows 上是 spawn 模式,会启动一个全新的解释器运行子进程。 测试结果: 两个独立进程之间,可以通过设置优先级,让优先级高的占用更多 cpu 时间,而相同优先级的会竞争。 同一父进程的两个子进程之间,优先级没起到作用; 不同父进程的两个子进程之间,不论是设置父进程的优先级还是子进程的优先级或者全都配置,两个子进程仍然会竞争。 |
14
woodpenker 2022-01-24 21:55:23 +08:00
kill STOP/CONT + sleep 就搞定了
|