0001 .. include:: ../disclaimer-zh_CN.rst
0002
0003 :Original: Documentation/scheduler/sched-bwc.rst
0004
0005 :翻译:
0006
0007 司延腾 Yanteng Si <siyanteng@loongson.cn>
0008
0009 :校译:
0010
0011
0012
0013 ============
0014 CFS 带宽控制
0015 ============
0016
0017 .. note::
0018 本文只讨论了SCHED_NORMAL的CPU带宽控制。
0019 SCHED_RT的情况在Documentation/scheduler/sched-rt-group.rst中有涉及。
0020
0021 CFS带宽控制是一个CONFIG_FAIR_GROUP_SCHED扩展,它允许指定一个组或层次的最大CPU带宽。
0022
0023 一个组允许的带宽是用配额和周期指定的。在每个给定的”周期“(微秒)内,一个任务组被分配多
0024 达“配额”微秒的CPU时间。当cgroup中的线程可运行时,该配额以时间片段的方式被分配到每个cpu
0025 运行队列中。一旦所有的配额被分配,任何额外的配额请求将导致这些线程被限流。被限流的线程将不
0026 能再次运行,直到下一个时期的配额得到补充。
0027
0028 一个组的未分配配额是全局跟踪的,在每个周期边界被刷新为cfs_quota单元。当线程消耗这个带宽时,
0029 它以需求为基础被转移到cpu-local“筒仓”,在每次更新中转移的数量是可调整的,被描述为“片“(时
0030 间片)。
0031
0032 突发特性
0033 --------
0034 现在这个功能借来的时间是用于防范我们对未来的低估,代价是对其他系统用户的干扰增加。所有这些都
0035 有很好的限制。
0036
0037 传统的(UP-EDF)带宽控制是这样的:
0038
0039 (U = \Sum u_i) <= 1
0040
0041 这既保证了每个最后期限的实现,也保证了系统的稳定。毕竟,如果U>1,那么每一秒钟的壁钟时间,我
0042 们就必须运行超过一秒钟的程序时间,显然会错过我们的最后期限,但下一个最后期限会更远,永远没有
0043 时间赶上,无边无界的失败。
0044
0045 突发特性观察到工作负载并不总是执行全部配额;这使得人们可以将u_i描述为一个统计分布。
0046
0047 例如,让u_i = {x,e}_i,其中x是p(95)和x+e p(100)(传统的WCET)。这实际上允许u更小,提
0048 高了效率(我们可以在系统中打包更多的任务),但代价是当所有的概率都一致时,会错过最后期限。然
0049 而,它确实保持了稳定性,因为只要我们的x高于平均水平,每一次超限都必须与低估相匹配。
0050
0051 也就是说,假设我们有两个任务,都指定了一个p(95)值,那么我们有一个p(95)*p(95)=90.25%的机
0052 会,两个任务都在他们的配额内,一切都很好。同时,我们有一个p(5)p(5)=0.25%的机会,两个任务同
0053 时超过他们的配额(保证最后期限失败)。在这两者之间有一个阈值,其中一个超过了,而另一个没有不足,
0054 无法补偿;这取决于具体的CDFs。
0055
0056 同时,我们可以说,最坏的情况下的截止日期失败,将是Sum e_i;也就是说,有一个有界的迟延(在假
0057 设x+e确实是WCET的情况下)。
0058
0059 使用突发时的干扰是由错过最后期限的可能性和平均WCET来评价的。测试结果表明,当有许多cgroup或
0060 CPU未被充分利用时,干扰是有限的。更多的细节显示在:
0061 https://lore.kernel.org/lkml/5371BD36-55AE-4F71-B9D7-B86DC32E3D2B@linux.alibaba.com/
0062
0063 管理
0064 ----
0065 配额、周期和突发是在cpu子系统内通过cgroupfs管理的。
0066
0067 .. note::
0068 本节描述的cgroupfs文件只适用于cgroup v1.对于cgroup v2,请参阅Control Group v2。
0069 :ref:`Documentation/admin-guide/cgroup-v2.rst <cgroup-v2-cpu>`.
0070
0071 - cpu.cfs_quota_us:在一个时期内补充的运行时间(微秒)。
0072 - cpu.cfs_period_us:一个周期的长度(微秒)。
0073 - cpu.stat: 输出节流统计数据[下面进一步解释]
0074 - cpu.cfs_burst_us:最大累积运行时间(微秒)。
0075
0076 默认值是::
0077
0078 cpu.cfs_period_us=100ms
0079 cpu.cfs_quota_us=-1
0080 cpu.cfs_burst_us=0
0081
0082 cpu.cfs_quota_us的值为-1表示该组没有任何带宽限制,这样的组被描述为无限制的带宽组。这代表
0083 了CFS的传统工作保护行为。
0084
0085 写入不小于cpu.cfs_burst_us的任何(有效的)正值将配发指定的带宽限制。该配额或周期允许的最
0086 小配额是1ms。周期长度也有一个1s的上限。当带宽限制以分层方式使用时,存在额外的限制,这些在下
0087 面有更详细的解释。
0088
0089 向cpu.cfs_quota_us写入任何负值都会移除带宽限制,并使组再次回到无限制的状态。
0090
0091 cpu.cfs_burst_us的值为0表示该组不能积累任何未使用的带宽。它使得CFS的传统带宽控制行为没有
0092 改变。将不大于 cpu.cfs_quota_us 的任何(有效的)正值写入 cpu.cfs_burst_us 将配发未使用
0093 带宽累积的上限。
0094
0095 如果一个组处于受限状态,对该组带宽规格的任何更新都将导致其成为无限流状态。
0096
0097 系统范围设置
0098 ------------
0099 为了提高效率,运行时间在全局池和CPU本地“筒仓”之间以批处理方式转移。这大大减少了大型系统的全
0100 局核算压力。每次需要进行这种更新时,传输的数量被描述为 "片"。
0101
0102 这是可以通过procfs调整的::
0103
0104 /proc/sys/kernel/sched_cfs_bandwidth_slice_us (default=5ms)
0105
0106 较大的时间片段值将减少传输开销,而较小的值则允许更精细的消费。
0107
0108 统计
0109 ----
0110 一个组的带宽统计数据通过cpu.stat的5个字段导出。
0111
0112 cpu.stat:
0113
0114 - nr_periods:已经过去的执行间隔的数量。
0115 - nr_throttled: 该组已被节流/限制的次数。
0116 - throttled_time: 该组的实体被限流的总时间长度(纳秒)。
0117 - nr_bursts:突发发生的周期数。
0118 - burst_time: 任何CPU在各个时期使用超过配额的累计壁钟时间(纳秒)。
0119
0120 这个接口是只读的。
0121
0122 分层考虑
0123 --------
0124 该接口强制要求单个实体的带宽总是可以达到的,即:max(c_i) <= C。然而,在总体情况下,是明确
0125 允许过度订阅的,以便在一个层次结构中实现工作保护语义:
0126
0127 例如,Sum (c_i)可能超过C
0128
0129 [ 其中C是父方的带宽,c_i是其子方的带宽。 ]
0130
0131 .. note::
0132 译文中的父亲/孩子指的是cgroup parent, cgroup children。
0133
0134 有两种方式可以使一个组变得限流:
0135
0136 a. 它在一段时期内完全消耗自己的配额
0137 b. 父方的配额在其期间内全部用完
0138
0139 在上述b)情况下,即使孩子可能有剩余的运行时间,它也不会被允许,直到父亲的运行时间被刷新。
0140
0141 CFS带宽配额的注意事项
0142 ---------------------
0143 一旦一个片断被分配给一个cpu,它就不会过期。然而,如果该cpu上的所有线程都无法运行,那么除了
0144 1ms以外的所有时间片都可以返回到全局池中。这是在编译时由min_cfs_rq_runtime变量配置的。这
0145 是一个性能调整,有助于防止对全局锁的额外争夺。
0146
0147 cpu-local分片不会过期的事实导致了一些有趣的罕见案例,应该被理解。
0148
0149 对于cgroup cpu限制的应用程序来说,这是一个相对有意义的问题,因为他们自然会消耗他们的全部配
0150 额,以及每个cpu-本地片在每个时期的全部。因此,预计nr_periods大致等于nr_throttled,并且
0151 cpuacct.用量的增加大致等于cfs_quota_us在每个周期的增加。
0152
0153 对于高线程、非cpu绑定的应用程序,这种非过期的细微差别允许应用程序短暂地突破他们的配额限制,
0154 即任务组正在运行的每个cpu上未使用的片断量(通常每个cpu最多1ms或由min_cfs_rq_runtime定
0155 义)。这种轻微的突发只适用于配额已经分配给cpu,然后没有完全使用或在以前的时期返回。这个突发
0156 量不会在核心之间转移。因此,这种机制仍然严格限制任务组的配额平均使用量,尽管是在比单一时期更
0157 长的时间窗口。这也限制了突发能力,每个cpu不超过1ms。这为在高核数机器上有小配额限制的高线程
0158 应用提供了更好的更可预测的用户体验。它还消除了在使用低于配额的cpu时对这些应用进行节流的倾向。
0159 另一种说法是,通过允许一个片断的未使用部分在不同时期保持有效,我们减少了在不需要整个片断的cpu
0160 时间的cpu-local 筒仓上浪费配额的可能性。
0161
0162 绑定cpu和非绑定cpu的交互式应用之间的互动也应该被考虑,特别是当单核使用率达到100%时。如果你
0163 给了这些应用程序一半的cpu-core,并且它们都被安排在同一个CPU上,理论上非cpu绑定的应用程序有
0164 可能在某些时期使用多达1ms的额外配额,从而阻止cpu绑定的应用程序完全使用其配额,这也是同样的数
0165 量。在这些情况下,将由CFS算法(见CFS调度器)来决定选择哪个应用程序来运行,因为它们都是可运行
0166 的,并且有剩余的配额。这个运行时间的差异将在接下来的交互式应用程序空闲期间得到弥补。
0167
0168 例子
0169 ----
0170 1. 限制一个组的运行时间为1个CPU的价值::
0171
0172 如果周期是250ms,配额也是250ms,那么该组将每250ms获得价值1个CPU的运行时间。
0173
0174 # echo 250000 > cpu.cfs_quota_us /* quota = 250ms */
0175 # echo 250000 > cpu.cfs_period_us /* period = 250ms */
0176
0177 2. 在多CPU机器上,将一个组的运行时间限制为2个CPU的价值
0178
0179 在500ms周期和1000ms配额的情况下,该组每500ms可以获得2个CPU的运行时间::
0180
0181 # echo 1000000 > cpu.cfs_quota_us /* quota = 1000ms */
0182 # echo 500000 > cpu.cfs_period_us /* period = 500ms */
0183
0184 这里较大的周期允许增加突发能力。
0185
0186 3. 将一个组限制在1个CPU的20%。
0187
0188 在50ms周期内,10ms配额将相当于1个CPU的20%。::
0189
0190 # echo 10000 > cpu.cfs_quota_us /* quota = 10ms */
0191 # echo 50000 > cpu.cfs_period_us /* period = 50ms */
0192
0193 通过在这里使用一个小的周期,我们以牺牲突发容量为代价来确保稳定的延迟响应。
0194
0195 4. 将一个组限制在1个CPU的40%,并允许累积到1个CPU的20%,如果已经累积了的话。
0196
0197 在50ms周期内,20ms配额将相当于1个CPU的40%。而10毫秒的突发将相当于1个
0198 CPU的20%::
0199
0200 # echo 20000 > cpu.cfs_quota_us /* quota = 20ms */
0201 # echo 50000 > cpu.cfs_period_us /* period = 50ms */
0202 # echo 10000 > cpu.cfs_burst_us /* burst = 10ms */
0203
0204 较大的缓冲区设置(不大于配额)允许更大的突发容量。