如何从/proc/schedstat计算CFS调度器节流百分比并排查计算异常
问题原因拆解
- 单位换算错误:纳秒转秒的系数是
1e9,不是你使用的1e7,错误的系数会直接导致计算结果放大100倍,是数值异常的核心原因之一。 - 总CPU基准时间计算错误:
/proc/schedstat的统计是单CPU维度的,如果你把所有CPU的runq_time差值做了累加,对应的总可用CPU时间应该是时间间隔(秒)* CPU核心数,而非单倍时间间隔。比如16核CPU,间隔1秒的总可用CPU时间是16秒,若累加16个核的runq_time得到20秒,直接除以1秒就会出现2000%的异常值,和你遇到的情况完全吻合。 - 统计逻辑偏差:CFS节流百分比对应CPU资源受限的时间占比,直接用换算后的runqTime作为百分比值的逻辑不严谨,需要和对应周期内的总可用CPU时间做比值计算。
正确计算步骤
- 第一次读取
/proc/schedstat,记录每个CPU的cpu_time(对应cpu行第8个字段)、runq_time(对应cpu行第9个字段),同时记录当前时间戳t1 - 等待指定间隔后,第二次读取
/proc/schedstat,记录每个CPU的对应字段,同时记录当前时间戳t2 - 计算时间差
delta_t = t2 - t1,单位为秒 - 计算全核的
delta_runq_time总和 = 所有CPU第二次runq_time - 第一次runq_time - 把
delta_runq_time从纳秒转换为秒:delta_runq_time_sec = delta_runq_time / 1e9 - 计算周期内总可用CPU时间:
total_cpu_time = delta_t * CPU核心数 - 最终节流百分比:
percent = (delta_runq_time_sec / total_cpu_time) * 100
如果仅统计单核心的节流率,不需要做核数累加,直接用单核心的delta_runq_time/1e9 / delta_t * 100即可,最大值不会超过100%。
修正后的Go代码示例
// delta.RunqTime单位:纳秒,delta.TimeDelta单位:秒,cpuCoreCount为节点CPU核心数 runqTimeSec := float64(delta.RunqTime) / 1e9 totalAvailableCpuTime := float64(delta.TimeDelta) * float64(cpuCoreCount) percent := (runqTimeSec / totalAvailableCpuTime) * 100
内容的提问来源于stack exchange,提问作者xakepp35
相关产品推荐
相关产品推荐

