← Documents Documentation/scheduler/sched-util-clamp.rst GitHub 원문 ↗

Linux 6.18.37 · Scheduler

Utilization clamping

uclamp_min/max의 task, runqueue, cgroup 집계 방식과 DVFS·task placement에 미치는 효과 및 한계를 설명합니다.

Source pathDocumentation/scheduler/sched-util-clamp.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

utilization signal에 범위를 씌우는 이유

sched-util-clamp.rst:1-183

PELT utilization은 최근 실행 이력을 반영하므로 갑자기 깨어난 latency-sensitive task의 필요 성능을 늦게 따라갈 수 있습니다. uclamp_min은 scheduler가 보는 최소 utilization을 올려 빠른 CPU 또는 높은 주파수를 요청하고, uclamp_max는 최대 utilization을 제한해 background workload의 성능과 전력을 억제합니다.

clamp는 task의 실제 실행 시간을 줄이거나 보장하지 않습니다. CPU selection과 schedutil frequency decision에 전달되는 utilization 표현을 제한합니다. 따라서 CPU가 이미 포화되었거나 thermal/power limit가 개입하면 요청한 성능이 실현되지 않을 수 있습니다.

Runqueue bucket 집계

sched-util-clamp.rst:184-263
uclamp bucket과 active task 분포
Bucket 0Bucket 1Bucket 2Bucket N
0-ΔΔ-2Δ2Δ-3Δ상한 구간
active countactive countactive countactive count
검색 방향enqueue/dequeue 시 해당 bucket count만 갱신하고, 필요한 clamp 값은 비어 있지 않은 가장 높은 bucket에서 얻습니다.

원문의 긴 ASCII bucket 배열을 동일한 구간과 count 관계가 보이도록 다시 구성했습니다.

모든 runnable task의 clamp를 매번 순회하면 enqueue/dequeue hot path 비용이 커집니다. 커널은 clamp 범위를 일정 수의 bucket으로 양자화하고 각 bucket의 active count를 유지하여 상한 집계를 일정한 비용으로 갱신합니다.

Max와 계층 집계

sched-util-clamp.rst:264-336
task 요청에서 effective clamp까지
task uclamp requestcgroup restrictionsystem defaulteffective task clamprunqueue max aggregation

task 값은 cgroup과 시스템 범위에 의해 제한된 뒤 effective 값이 되고, runqueue는 runnable task의 effective 값 중 필요한 최댓값을 유지합니다.

cgroup 계층에서는 자식이 부모가 허용한 범위를 벗어나지 못합니다. task가 요청한 min/max, cgroup 제한과 시스템 기본값을 결합해 effective clamp를 만든 뒤 runqueue aggregation에 반영합니다.

Task, cgroup, system 인터페이스

sched-util-clamp.rst:337-521
범위인터페이스사용 목적
Tasksched_setattr()의 sched_util_min/max개별 thread의 boost 또는 cap
cgroupcpu.uclamp.min / cpu.uclamp.max서비스·앱 단위 정책
Systemsched_util_clamp_min/max 관련 설정전체 허용 범위와 기본값
RT defaultsched_util_clamp_min_rt_defaultRT task의 기본 최소 성능 요청

값은 capacity scale인 0..1024 범위로 해석합니다. min이 max보다 커지는 조합, cgroup parent 범위와 충돌하는 조합은 effective 값 계산에서 제한됩니다. 값을 쓴 뒤에는 task와 cgroup 양쪽에서 실제 적용값을 다시 읽어야 합니다.

사용 사례

sched-util-clamp.rst:522-593
  • UI, audio와 control thread의 uclamp_min을 올려 wake-up 직후 DVFS ramp 지연을 줄입니다.
  • background task의 uclamp_max를 낮춰 big CPU 배치와 높은 OPP 선택을 억제합니다.
  • powersave mode에서 app cgroup의 최대값을 일괄 제한합니다.
  • 앱별 성능 정책을 cgroup 계층으로 적용하되 latency-critical helper thread는 별도 group으로 분리합니다.

알려진 한계와 검증 항목

sched-util-clamp.rst:594-742

uclamp_max가 항상 물리 주파수의 완전한 상한이 되는 것은 아닙니다. 같은 performance domain의 다른 CPU가 높은 주파수를 요구하거나 RT/DL/IRQ 부하가 개입하면 domain frequency가 올라갈 수 있습니다.

낮은 uclamp_max로 실행이 지연되면 PELT util_avg 자체가 workload demand를 충분히 표현하지 못하는 feedback 문제가 생길 수 있습니다. schedutil rate limit과 driver transition latency도 반응 시간을 늘리므로 trace_cpu_frequency, sched_switch, sched_util_est 계열 trace를 함께 확인해야 합니다.

UCLAMP_MAX 해제 순간의 PELT 기반 주파수 spike
Task p0Task p1RunqueueSchedutil
01 util_avg 1024 / max 300sleepingeffective max 300frequency capped
02 util_avg 1024wake: util_avg 200 / max 1024util_avg 1024 / max 1024frequency spike
03 ideal util_avg 300util_avg 200ideal aggregate 500expected moderate OPP

오랫동안 cap된 p0의 util_avg가 1024까지 누적된 상태에서 제한 없는 p1이 깨어나면 runqueue max가 1024로 풀리며 과도한 frequency 요청이 발생할 수 있습니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ====================
4 Utilization Clamping
5 ====================
6
7 1. Introduction
8 ===============
9
10 Utilization clamping, also known as util clamp or uclamp, is a scheduler
11 feature that allows user space to help in managing the performance requirement
12 of tasks. It was introduced in v5.3 release. The CGroup support was merged in
13 v5.4.
14
15 Uclamp is a hinting mechanism that allows the scheduler to understand the
16 performance requirements and restrictions of the tasks, thus it helps the
17 scheduler to make a better decision. And when schedutil cpufreq governor is
18 used, util clamp will influence the CPU frequency selection as well.
19
20 Since the scheduler and schedutil are both driven by PELT (util_avg) signals,
21 util clamp acts on that to achieve its goal by clamping the signal to a certain
22 point; hence the name. That is, by clamping utilization we are making the
23 system run at a certain performance point.
24
25 The right way to view util clamp is as a mechanism to make request or hint on
26 performance constraints. It consists of two tunables:
27
28 * UCLAMP_MIN, which sets the lower bound.
29 * UCLAMP_MAX, which sets the upper bound.
30
31 These two bounds will ensure a task will operate within this performance range
32 of the system. UCLAMP_MIN implies boosting a task, while UCLAMP_MAX implies
33 capping a task.
34
35 One can tell the system (scheduler) that some tasks require a minimum
36 performance point to operate at to deliver the desired user experience. Or one
37 can tell the system that some tasks should be restricted from consuming too
38 much resources and should not go above a specific performance point. Viewing
39 the uclamp values as performance points rather than utilization is a better
40 abstraction from user space point of view.
41
42 As an example, a game can use util clamp to form a feedback loop with its
43 perceived Frames Per Second (FPS). It can dynamically increase the minimum
44 performance point required by its display pipeline to ensure no frame is
45 dropped. It can also dynamically 'prime' up these tasks if it knows in the
46 coming few hundred milliseconds a computationally intensive scene is about to
47 happen.
48
49 On mobile hardware where the capability of the devices varies a lot, this
50 dynamic feedback loop offers a great flexibility to ensure best user experience
51 given the capabilities of any system.
52
53 Of course a static configuration is possible too. The exact usage will depend
54 on the system, application and the desired outcome.
55
56 Another example is in Android where tasks are classified as background,
57 foreground, top-app, etc. Util clamp can be used to constrain how much
58 resources background tasks are consuming by capping the performance point they
59 can run at. This constraint helps reserve resources for important tasks, like
60 the ones belonging to the currently active app (top-app group). Beside this
61 helps in limiting how much power they consume. This can be more obvious in
62 heterogeneous systems (e.g. Arm big.LITTLE); the constraint will help bias the
63 background tasks to stay on the little cores which will ensure that:
64
65 1. The big cores are free to run top-app tasks immediately. top-app
66 tasks are the tasks the user is currently interacting with, hence
67 the most important tasks in the system.
68 2. They don't run on a power hungry core and drain battery even if they
69 are CPU intensive tasks.
70
71 .. note::
72 **little cores**:
73 CPUs with capacity < 1024
74
75 **big cores**:
76 CPUs with capacity = 1024
77
78 By making these uclamp performance requests, or rather hints, user space can
79 ensure system resources are used optimally to deliver the best possible user
80 experience.
81
82 Another use case is to help with **overcoming the ramp up latency inherit in
83 how scheduler utilization signal is calculated**.
84
85 On the other hand, a busy task for instance that requires to run at maximum
86 performance point will suffer a delay of ~200ms (PELT HALFIFE = 32ms) for the
87 scheduler to realize that. This is known to affect workloads like gaming on
88 mobile devices where frames will drop due to slow response time to select the
89 higher frequency required for the tasks to finish their work in time. Setting
90 UCLAMP_MIN=1024 will ensure such tasks will always see the highest performance
91 level when they start running.
92
93 The overall visible effect goes beyond better perceived user
94 experience/performance and stretches to help achieve a better overall
95 performance/watt if used effectively.
96
97 User space can form a feedback loop with the thermal subsystem too to ensure
98 the device doesn't heat up to the point where it will throttle.
99
100 Both SCHED_NORMAL/OTHER and SCHED_FIFO/RR honour uclamp requests/hints.
101
102 In the SCHED_FIFO/RR case, uclamp gives the option to run RT tasks at any
103 performance point rather than being tied to MAX frequency all the time. Which
104 can be useful on general purpose systems that run on battery powered devices.
105
106 Note that by design RT tasks don't have per-task PELT signal and must always
107 run at a constant frequency to combat undeterministic DVFS rampup delays.
108
109 Note that using schedutil always implies a single delay to modify the frequency
110 when an RT task wakes up. This cost is unchanged by using uclamp. Uclamp only
111 helps picking what frequency to request instead of schedutil always requesting
112 MAX for all RT tasks.
113
114 See :ref:`section 3.4 <uclamp-default-values>` for default values and
115 :ref:`3.4.1 <sched-util-clamp-min-rt-default>` on how to change RT tasks
116 default value.
117
118 2. Design
119 =========
120
121 Util clamp is a property of every task in the system. It sets the boundaries of
122 its utilization signal; acting as a bias mechanism that influences certain
123 decisions within the scheduler.
124
125 The actual utilization signal of a task is never clamped in reality. If you
126 inspect PELT signals at any point of time you should continue to see them as
127 they are intact. Clamping happens only when needed, e.g: when a task wakes up
128 and the scheduler needs to select a suitable CPU for it to run on.
129
130 Since the goal of util clamp is to allow requesting a minimum and maximum
131 performance point for a task to run on, it must be able to influence the
132 frequency selection as well as task placement to be most effective. Both of
133 which have implications on the utilization value at CPU runqueue (rq for short)
134 level, which brings us to the main design challenge.
135
136 When a task wakes up on an rq, the utilization signal of the rq will be
137 affected by the uclamp settings of all the tasks enqueued on it. For example if
138 a task requests to run at UTIL_MIN = 512, then the util signal of the rq needs
139 to respect to this request as well as all other requests from all of the
140 enqueued tasks.
141
142 To be able to aggregate the util clamp value of all the tasks attached to the
143 rq, uclamp must do some housekeeping at every enqueue/dequeue, which is the
144 scheduler hot path. Hence care must be taken since any slow down will have
145 significant impact on a lot of use cases and could hinder its usability in
146 practice.
147
148 The way this is handled is by dividing the utilization range into buckets
149 (struct uclamp_bucket) which allows us to reduce the search space from every
150 task on the rq to only a subset of tasks on the top-most bucket.
151
152 When a task is enqueued, the counter in the matching bucket is incremented,
153 and on dequeue it is decremented. This makes keeping track of the effective
154 uclamp value at rq level a lot easier.
155
156 As tasks are enqueued and dequeued, we keep track of the current effective
157 uclamp value of the rq. See :ref:`section 2.1 <uclamp-buckets>` for details on
158 how this works.
159
160 Later at any path that wants to identify the effective uclamp value of the rq,
161 it will simply need to read this effective uclamp value of the rq at that exact
162 moment of time it needs to take a decision.
163
164 For task placement case, only Energy Aware and Capacity Aware Scheduling
165 (EAS/CAS) make use of uclamp for now, which implies that it is applied on
166 heterogeneous systems only.
167 When a task wakes up, the scheduler will look at the current effective uclamp
168 value of every rq and compare it with the potential new value if the task were
169 to be enqueued there. Favoring the rq that will end up with the most energy
170 efficient combination.
171
172 Similarly in schedutil, when it needs to make a frequency update it will look
173 at the current effective uclamp value of the rq which is influenced by the set
174 of tasks currently enqueued there and select the appropriate frequency that
175 will satisfy constraints from requests.
176
177 Other paths like setting overutilization state (which effectively disables EAS)
178 make use of uclamp as well. Such cases are considered necessary housekeeping to
179 allow the 2 main use cases above and will not be covered in detail here as they
180 could change with implementation details.
181
182 .. _uclamp-buckets:
183
184 2.1. Buckets
185 ------------
186
187 ::
188
189 [struct rq]
190
191 (bottom) (top)
192
193 0 1024
194 | |
195 +-----------+-----------+-----------+---- ----+-----------+
196 | Bucket 0 | Bucket 1 | Bucket 2 | ... | Bucket N |
197 +-----------+-----------+-----------+---- ----+-----------+
198 : : :
199 +- p0 +- p3 +- p4
200 : :
201 +- p1 +- p5
202 :
203 +- p2
204
205
206 .. note::
207 The diagram above is an illustration rather than a true depiction of the
208 internal data structure.
209
210 To reduce the search space when trying to decide the effective uclamp value of
211 an rq as tasks are enqueued/dequeued, the whole utilization range is divided
212 into N buckets where N is configured at compile time by setting
213 CONFIG_UCLAMP_BUCKETS_COUNT. By default it is set to 5.
214
215 The rq has a bucket for each uclamp_id tunables: [UCLAMP_MIN, UCLAMP_MAX].
216
217 The range of each bucket is 1024/N. For example, for the default value of
218 5 there will be 5 buckets, each of which will cover the following range:
219
220 ::
221
222 DELTA = round_closest(1024/5) = 204.8 = 205
223
224 Bucket 0: [0:204]
225 Bucket 1: [205:409]
226 Bucket 2: [410:614]
227 Bucket 3: [615:819]
228 Bucket 4: [820:1024]
229
230 When a task p with following tunable parameters
231
232 ::
233
234 p->uclamp[UCLAMP_MIN] = 300
235 p->uclamp[UCLAMP_MAX] = 1024
236
237 is enqueued into the rq, bucket 1 will be incremented for UCLAMP_MIN and bucket
238 4 will be incremented for UCLAMP_MAX to reflect the fact the rq has a task in
239 this range.
240
241 The rq then keeps track of its current effective uclamp value for each
242 uclamp_id.
243
244 When a task p is enqueued, the rq value changes to:
245
246 ::
247
248 // update bucket logic goes here
249 rq->uclamp[UCLAMP_MIN] = max(rq->uclamp[UCLAMP_MIN], p->uclamp[UCLAMP_MIN])
250 // repeat for UCLAMP_MAX
251
252 Similarly, when p is dequeued the rq value changes to:
253
254 ::
255
256 // update bucket logic goes here
257 rq->uclamp[UCLAMP_MIN] = search_top_bucket_for_highest_value()
258 // repeat for UCLAMP_MAX
259
260 When all buckets are empty, the rq uclamp values are reset to system defaults.
261 See :ref:`section 3.4 <uclamp-default-values>` for details on default values.
262
263
264 2.2. Max aggregation
265 --------------------
266
267 Util clamp is tuned to honour the request for the task that requires the
268 highest performance point.
269
270 When multiple tasks are attached to the same rq, then util clamp must make sure
271 the task that needs the highest performance point gets it even if there's
272 another task that doesn't need it or is disallowed from reaching this point.
273
274 For example, if there are multiple tasks attached to an rq with the following
275 values:
276
277 ::
278
279 p0->uclamp[UCLAMP_MIN] = 300
280 p0->uclamp[UCLAMP_MAX] = 900
281
282 p1->uclamp[UCLAMP_MIN] = 500
283 p1->uclamp[UCLAMP_MAX] = 500
284
285 then assuming both p0 and p1 are enqueued to the same rq, both UCLAMP_MIN
286 and UCLAMP_MAX become:
287
288 ::
289
290 rq->uclamp[UCLAMP_MIN] = max(300, 500) = 500
291 rq->uclamp[UCLAMP_MAX] = max(900, 500) = 900
292
293 As we shall see in :ref:`section 5.1 <uclamp-capping-fail>`, this max
294 aggregation is the cause of one of limitations when using util clamp, in
295 particular for UCLAMP_MAX hint when user space would like to save power.
296
297 2.3. Hierarchical aggregation
298 -----------------------------
299
300 As stated earlier, util clamp is a property of every task in the system. But
301 the actual applied (effective) value can be influenced by more than just the
302 request made by the task or another actor on its behalf (middleware library).
303
304 The effective util clamp value of any task is restricted as follows:
305
306 1. By the uclamp settings defined by the cgroup CPU controller it is attached
307 to, if any.
308 2. The restricted value in (1) is then further restricted by the system wide
309 uclamp settings.
310
311 :ref:`Section 3 <uclamp-interfaces>` discusses the interfaces and will expand
312 further on that.
313
314 For now suffice to say that if a task makes a request, its actual effective
315 value will have to adhere to some restrictions imposed by cgroup and system
316 wide settings.
317
318 The system will still accept the request even if effectively will be beyond the
319 constraints, but as soon as the task moves to a different cgroup or a sysadmin
320 modifies the system settings, the request will be satisfied only if it is
321 within new constraints.
322
323 In other words, this aggregation will not cause an error when a task changes
324 its uclamp values, but rather the system may not be able to satisfy requests
325 based on those factors.
326
327 2.4. Range
328 ----------
329
330 Uclamp performance request has the range of 0 to 1024 inclusive.
331
332 For cgroup interface percentage is used (that is 0 to 100 inclusive).
333 Just like other cgroup interfaces, you can use 'max' instead of 100.
334
335 .. _uclamp-interfaces:
336
337 3. Interfaces
338 =============
339
340 3.1. Per task interface
341 -----------------------
342
343 sched_setattr() syscall was extended to accept two new fields:
344
345 * sched_util_min: requests the minimum performance point the system should run
346 at when this task is running. Or lower performance bound.
347 * sched_util_max: requests the maximum performance point the system should run
348 at when this task is running. Or upper performance bound.
349
350 For example, the following scenario have 40% to 80% utilization constraints:
351
352 ::
353
354 attr->sched_util_min = 40% * 1024;
355 attr->sched_util_max = 80% * 1024;
356
357 When task @p is running, **the scheduler should try its best to ensure it
358 starts at 40% performance level**. If the task runs for a long enough time so
359 that its actual utilization goes above 80%, the utilization, or performance
360 level, will be capped.
361
362 The special value -1 is used to reset the uclamp settings to the system
363 default.
364
365 Note that resetting the uclamp value to system default using -1 is not the same
366 as manually setting uclamp value to system default. This distinction is
367 important because as we shall see in system interfaces, the default value for
368 RT could be changed. SCHED_NORMAL/OTHER might gain similar knobs too in the
369 future.
370
371 3.2. cgroup interface
372 ---------------------
373
374 There are two uclamp related values in the CPU cgroup controller:
375
376 * cpu.uclamp.min
377 * cpu.uclamp.max
378
379 When a task is attached to a CPU controller, its uclamp values will be impacted
380 as follows:
381
382 * cpu.uclamp.min is a protection as described in :ref:`section 3-3 of cgroup
383 v2 documentation <cgroupv2-protections-distributor>`.
384
385 If a task uclamp_min value is lower than cpu.uclamp.min, then the task will
386 inherit the cgroup cpu.uclamp.min value.
387
388 In a cgroup hierarchy, effective cpu.uclamp.min is the max of (child,
389 parent).
390
391 * cpu.uclamp.max is a limit as described in :ref:`section 3-2 of cgroup v2
392 documentation <cgroupv2-limits-distributor>`.
393
394 If a task uclamp_max value is higher than cpu.uclamp.max, then the task will
395 inherit the cgroup cpu.uclamp.max value.
396
397 In a cgroup hierarchy, effective cpu.uclamp.max is the min of (child,
398 parent).
399
400 For example, given following parameters:
401
402 ::
403
404 p0->uclamp[UCLAMP_MIN] = // system default;
405 p0->uclamp[UCLAMP_MAX] = // system default;
406
407 p1->uclamp[UCLAMP_MIN] = 40% * 1024;
408 p1->uclamp[UCLAMP_MAX] = 50% * 1024;
409
410 cgroup0->cpu.uclamp.min = 20% * 1024;
411 cgroup0->cpu.uclamp.max = 60% * 1024;
412
413 cgroup1->cpu.uclamp.min = 60% * 1024;
414 cgroup1->cpu.uclamp.max = 100% * 1024;
415
416 when p0 and p1 are attached to cgroup0, the values become:
417
418 ::
419
420 p0->uclamp[UCLAMP_MIN] = cgroup0->cpu.uclamp.min = 20% * 1024;
421 p0->uclamp[UCLAMP_MAX] = cgroup0->cpu.uclamp.max = 60% * 1024;
422
423 p1->uclamp[UCLAMP_MIN] = 40% * 1024; // intact
424 p1->uclamp[UCLAMP_MAX] = 50% * 1024; // intact
425
426 when p0 and p1 are attached to cgroup1, these instead become:
427
428 ::
429
430 p0->uclamp[UCLAMP_MIN] = cgroup1->cpu.uclamp.min = 60% * 1024;
431 p0->uclamp[UCLAMP_MAX] = cgroup1->cpu.uclamp.max = 100% * 1024;
432
433 p1->uclamp[UCLAMP_MIN] = cgroup1->cpu.uclamp.min = 60% * 1024;
434 p1->uclamp[UCLAMP_MAX] = 50% * 1024; // intact
435
436 Note that cgroup interfaces allows cpu.uclamp.max value to be lower than
437 cpu.uclamp.min. Other interfaces don't allow that.
438
439 3.3. System interface
440 ---------------------
441
442 3.3.1 sched_util_clamp_min
443 --------------------------
444
445 System wide limit of allowed UCLAMP_MIN range. By default it is set to 1024,
446 which means that permitted effective UCLAMP_MIN range for tasks is [0:1024].
447 By changing it to 512 for example the range reduces to [0:512]. This is useful
448 to restrict how much boosting tasks are allowed to acquire.
449
450 Requests from tasks to go above this knob value will still succeed, but
451 they won't be satisfied until it is more than p->uclamp[UCLAMP_MIN].
452
453 The value must be smaller than or equal to sched_util_clamp_max.
454
455 3.3.2 sched_util_clamp_max
456 --------------------------
457
458 System wide limit of allowed UCLAMP_MAX range. By default it is set to 1024,
459 which means that permitted effective UCLAMP_MAX range for tasks is [0:1024].
460
461 By changing it to 512 for example the effective allowed range reduces to
462 [0:512]. This means is that no task can run above 512, which implies that all
463 rqs are restricted too. IOW, the whole system is capped to half its performance
464 capacity.
465
466 This is useful to restrict the overall maximum performance point of the system.
467 For example, it can be handy to limit performance when running low on battery
468 or when the system wants to limit access to more energy hungry performance
469 levels when it's in idle state or screen is off.
470
471 Requests from tasks to go above this knob value will still succeed, but they
472 won't be satisfied until it is more than p->uclamp[UCLAMP_MAX].
473
474 The value must be greater than or equal to sched_util_clamp_min.
475
476 .. _uclamp-default-values:
477
478 3.4. Default values
479 -------------------
480
481 By default all SCHED_NORMAL/SCHED_OTHER tasks are initialized to:
482
483 ::
484
485 p_fair->uclamp[UCLAMP_MIN] = 0
486 p_fair->uclamp[UCLAMP_MAX] = 1024
487
488 That is, by default they're boosted to run at the maximum performance point of
489 changed at boot or runtime. No argument was made yet as to why we should
490 provide this, but can be added in the future.
491
492 For SCHED_FIFO/SCHED_RR tasks:
493
494 ::
495
496 p_rt->uclamp[UCLAMP_MIN] = 1024
497 p_rt->uclamp[UCLAMP_MAX] = 1024
498
499 That is by default they're boosted to run at the maximum performance point of
500 the system which retains the historical behavior of the RT tasks.
501
502 RT tasks default uclamp_min value can be modified at boot or runtime via
503 sysctl. See below section.
504
505 .. _sched-util-clamp-min-rt-default:
506
507 3.4.1 sched_util_clamp_min_rt_default
508 -------------------------------------
509
510 Running RT tasks at maximum performance point is expensive on battery powered
511 devices and not necessary. To allow system developer to offer good performance
512 guarantees for these tasks without pushing it all the way to maximum
513 performance point, this sysctl knob allows tuning the best boost value to
514 address the system requirement without burning power running at maximum
515 performance point all the time.
516
517 Application developer are encouraged to use the per task util clamp interface
518 to ensure they are performance and power aware. Ideally this knob should be set
519 to 0 by system designers and leave the task of managing performance
520 requirements to the apps.
521
522 4. How to use util clamp
523 ========================
524
525 Util clamp promotes the concept of user space assisted power and performance
526 management. At the scheduler level there is no info required to make the best
527 decision. However, with util clamp user space can hint to the scheduler to make
528 better decision about task placement and frequency selection.
529
530 Best results are achieved by not making any assumptions about the system the
531 application is running on and to use it in conjunction with a feedback loop to
532 dynamically monitor and adjust. Ultimately this will allow for a better user
533 experience at a better perf/watt.
534
535 For some systems and use cases, static setup will help to achieve good results.
536 Portability will be a problem in this case. How much work one can do at 100,
537 200 or 1024 is different for each system. Unless there's a specific target
538 system, static setup should be avoided.
539
540 There are enough possibilities to create a whole framework based on util clamp
541 or self contained app that makes use of it directly.
542
543 4.1. Boost important and DVFS-latency-sensitive tasks
544 -----------------------------------------------------
545
546 A GUI task might not be busy to warrant driving the frequency high when it
547 wakes up. However, it requires to finish its work within a specific time window
548 to deliver the desired user experience. The right frequency it requires at
549 wakeup will be system dependent. On some underpowered systems it will be high,
550 on other overpowered ones it will be low or 0.
551
552 This task can increase its UCLAMP_MIN value every time it misses the deadline
553 to ensure on next wake up it runs at a higher performance point. It should try
554 to approach the lowest UCLAMP_MIN value that allows to meet its deadline on any
555 particular system to achieve the best possible perf/watt for that system.
556
557 On heterogeneous systems, it might be important for this task to run on
558 a faster CPU.
559
560 **Generally it is advised to perceive the input as performance level or point
561 which will imply both task placement and frequency selection**.
562
563 4.2. Cap background tasks
564 -------------------------
565
566 Like explained for Android case in the introduction. Any app can lower
567 UCLAMP_MAX for some background tasks that don't care about performance but
568 could end up being busy and consume unnecessary system resources on the system.
569
570 4.3. Powersave mode
571 -------------------
572
573 sched_util_clamp_max system wide interface can be used to limit all tasks from
574 operating at the higher performance points which are usually energy
575 inefficient.
576
577 This is not unique to uclamp as one can achieve the same by reducing max
578 frequency of the cpufreq governor. It can be considered a more convenient
579 alternative interface.
580
581 4.4. Per-app performance restriction
582 ------------------------------------
583
584 Middleware/Utility can provide the user an option to set UCLAMP_MIN/MAX for an
585 app every time it is executed to guarantee a minimum performance point and/or
586 limit it from draining system power at the cost of reduced performance for
587 these apps.
588
589 If you want to prevent your laptop from heating up while on the go from
590 compiling the kernel and happy to sacrifice performance to save power, but
591 still would like to keep your browser performance intact, uclamp makes it
592 possible.
593
594 5. Limitations
595 ==============
596
597 .. _uclamp-capping-fail:
598
599 5.1. Capping frequency with uclamp_max fails under certain conditions
600 ---------------------------------------------------------------------
601
602 If task p0 is capped to run at 512:
603
604 ::
605
606 p0->uclamp[UCLAMP_MAX] = 512
607
608 and it shares the rq with p1 which is free to run at any performance point:
609
610 ::
611
612 p1->uclamp[UCLAMP_MAX] = 1024
613
614 then due to max aggregation the rq will be allowed to reach max performance
615 point:
616
617 ::
618
619 rq->uclamp[UCLAMP_MAX] = max(512, 1024) = 1024
620
621 Assuming both p0 and p1 have UCLAMP_MIN = 0, then the frequency selection for
622 the rq will depend on the actual utilization value of the tasks.
623
624 If p1 is a small task but p0 is a CPU intensive task, then due to the fact that
625 both are running at the same rq, p1 will cause the frequency capping to be left
626 from the rq although p1, which is allowed to run at any performance point,
627 doesn't actually need to run at that frequency.
628
629 5.2. UCLAMP_MAX can break PELT (util_avg) signal
630 ------------------------------------------------
631
632 PELT assumes that frequency will always increase as the signals grow to ensure
633 there's always some idle time on the CPU. But with UCLAMP_MAX, this frequency
634 increase will be prevented which can lead to no idle time in some
635 circumstances. When there's no idle time, a task will stuck in a busy loop,
636 which would result in util_avg being 1024.
637
638 Combing with issue described below, this can lead to unwanted frequency spikes
639 when severely capped tasks share the rq with a small non capped task.
640
641 As an example if task p, which have:
642
643 ::
644
645 p0->util_avg = 300
646 p0->uclamp[UCLAMP_MAX] = 0
647
648 wakes up on an idle CPU, then it will run at min frequency (Fmin) this
649 CPU is capable of. The max CPU frequency (Fmax) matters here as well,
650 since it designates the shortest computational time to finish the task's
651 work on this CPU.
652
653 ::
654
655 rq->uclamp[UCLAMP_MAX] = 0
656
657 If the ratio of Fmax/Fmin is 3, then maximum value will be:
658
659 ::
660
661 300 * (Fmax/Fmin) = 900
662
663 which indicates the CPU will still see idle time since 900 is < 1024. The
664 _actual_ util_avg will not be 900 though, but somewhere between 300 and 900. As
665 long as there's idle time, p->util_avg updates will be off by a some margin,
666 but not proportional to Fmax/Fmin.
667
668 ::
669
670 p0->util_avg = 300 + small_error
671
672 Now if the ratio of Fmax/Fmin is 4, the maximum value becomes:
673
674 ::
675
676 300 * (Fmax/Fmin) = 1200
677
678 which is higher than 1024 and indicates that the CPU has no idle time. When
679 this happens, then the _actual_ util_avg will become:
680
681 ::
682
683 p0->util_avg = 1024
684
685 If task p1 wakes up on this CPU, which have:
686
687 ::
688
689 p1->util_avg = 200
690 p1->uclamp[UCLAMP_MAX] = 1024
691
692 then the effective UCLAMP_MAX for the CPU will be 1024 according to max
693 aggregation rule. But since the capped p0 task was running and throttled
694 severely, then the rq->util_avg will be:
695
696 ::
697
698 p0->util_avg = 1024
699 p1->util_avg = 200
700
701 rq->util_avg = 1024
702 rq->uclamp[UCLAMP_MAX] = 1024
703
704 Hence lead to a frequency spike since if p0 wasn't throttled we should get:
705
706 ::
707
708 p0->util_avg = 300
709 p1->util_avg = 200
710
711 rq->util_avg = 500
712
713 and run somewhere near mid performance point of that CPU, not the Fmax we get.
714
715 5.3. Schedutil response time issues
716 -----------------------------------
717
718 schedutil has three limitations:
719
720 1. Hardware takes non-zero time to respond to any frequency change
721 request. On some platforms can be in the order of few ms.
722 2. Non fast-switch systems require a worker deadline thread to wake up
723 and perform the frequency change, which adds measurable overhead.
724 3. schedutil rate_limit_us drops any requests during this rate_limit_us
725 window.
726
727 If a relatively small task is doing critical job and requires a certain
728 performance point when it wakes up and starts running, then all these
729 limitations will prevent it from getting what it wants in the time scale it
730 expects.
731
732 This limitation is not only impactful when using uclamp, but will be more
733 prevalent as we no longer gradually ramp up or down. We could easily be
734 jumping between frequencies depending on the order tasks wake up, and their
735 respective uclamp values.
736
737 We regard that as a limitation of the capabilities of the underlying system
738 itself.
739
740 There is room to improve the behavior of schedutil rate_limit_us, but not much
741 to be done for 1 or 2. They are considered hard limitations of the system.
742

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

1. 소개

1-117

util clamp 또는 uclamp라고도 부르는 utilization clamping은 사용자 공간이 task의 성능 요구사항을 관리하는 데 관여할 수 있게 하는 scheduler 기능이다. Linux v5.3에서 처음 도입되었고 cgroup 지원은 v5.4에 병합되었다.

uclamp는 scheduler가 task의 성능 요구와 제한을 이해하도록 돕는 hint 방식이다. scheduler는 이 정보를 바탕으로 더 나은 결정을 내릴 수 있고, schedutil cpufreq governor를 사용할 때는 CPU frequency 선택에도 util clamp가 영향을 준다.

scheduler와 schedutil은 모두 PELT의 util_avg signal에 의해 구동된다. util clamp는 이 signal을 특정 지점으로 제한하는 방식으로 목적을 달성한다. utilization을 제한함으로써 system이 일정한 성능 지점에서 동작하도록 만드는 것이 이름의 유래다.

사용자 공간 관점에서는 util clamp를 utilization 수치라기보다 성능 제약에 대한 요청 또는 hint로 보는 편이 알맞다. 두 tunable이 이 범위를 정한다.

  • UCLAMP_MIN은 하한을 정한다.
  • UCLAMP_MAX는 상한을 정한다.

두 경계는 task가 system의 지정된 성능 범위 안에서 동작하도록 한다. UCLAMP_MIN은 task boost를, UCLAMP_MAX는 task cap을 뜻한다.

어떤 task가 원하는 사용자 경험을 제공하려면 최소한 어느 성능 지점에서 동작해야 한다고 scheduler에 알릴 수 있다. 반대로 특정 task가 너무 많은 자원을 쓰지 못하게 하여 정해진 성능 지점을 넘지 않도록 요청할 수도 있다. 사용자 공간에는 uclamp 값을 utilization보다 performance point로 추상화하는 편이 이해하기 쉽다.

예를 들어 game은 화면에서 관찰한 FPS와 util clamp 사이에 feedback loop를 만들 수 있다. frame이 떨어질 때 display pipeline task가 요구하는 최소 성능 지점을 동적으로 높일 수 있고, 수백 ms 뒤 계산량이 큰 장면이 시작될 것을 안다면 미리 이 task들을 높은 성능으로 준비시킬 수도 있다.

device 성능 편차가 큰 mobile hardware에서는 이런 동적 feedback loop가 각 system 능력에 맞는 사용자 경험을 만드는 데 큰 유연성을 준다.

정적 설정도 가능하다. 정확한 사용법은 system, application, 원하는 결과에 따라 달라진다.

Android에서는 task를 background, foreground, top-app 등으로 분류한다. background task가 사용할 수 있는 성능 지점에 상한을 두면 현재 사용자가 조작하는 top-app group처럼 중요한 task를 위해 자원을 남길 수 있고, background task의 전력 소비도 제한할 수 있다.

Arm big.LITTLE 같은 heterogeneous system에서는 이 제약의 효과가 더 분명하다. background task가 little core에 머물도록 유도하면 다음 두 결과를 얻는다.

  • big core를 비워 두어 사용자가 현재 상호작용하는 가장 중요한 top-app task를 즉시 실행할 수 있다.
  • CPU를 많이 쓰는 background task라도 전력 소모가 큰 core에서 실행되어 battery를 낭비하는 일을 줄인다.

이 문서에서 little core는 capacity가 1024보다 작은 CPU, big core는 capacity가 1024인 CPU를 뜻한다.

사용자 공간은 이런 uclamp 성능 요청 또는 hint를 통해 system resource가 가능한 한 효율적으로 사용되도록 만들고, 그 위에서 최선의 사용자 경험을 제공할 수 있다.

또 다른 용도는 scheduler utilization signal 계산 방식에 내재된 ramp-up latency를 극복하는 것이다.

최대 성능으로 실행되어야 하는 busy task라도 scheduler가 그 요구를 util_avg에서 파악하기까지 약 200 ms가 걸릴 수 있다. PELT half-life는 32 ms다. mobile game에서는 높은 frequency 선택이 늦어져 frame deadline을 놓칠 수 있다. UCLAMP_MIN=1024로 설정하면 이런 task는 실행을 시작할 때부터 가장 높은 성능 수준을 요청한다.

효과적으로 사용하면 눈에 보이는 사용자 경험과 성능 개선을 넘어 system 전체의 performance per watt도 높일 수 있다.

사용자 공간은 thermal subsystem과도 feedback loop를 구성하여 thermal throttling이 시작될 정도로 device가 뜨거워지지 않게 조절할 수 있다.

SCHED_NORMAL/SCHED_OTHER와 SCHED_FIFO/SCHED_RR는 모두 uclamp 요청과 hint를 적용한다.

SCHED_FIFO/SCHED_RR에서는 RT task를 항상 최대 frequency에 묶지 않고 원하는 성능 지점에서 실행할 수 있다. battery로 동작하는 범용 system에서 특히 유용하다.

설계상 RT task에는 task별 PELT signal이 없다. 예측하기 어려운 DVFS ramp-up delay에 대응하려면 RT task는 일정한 frequency에서 실행되어야 한다.

schedutil을 사용하면 RT task가 깨어날 때 frequency를 바꾸기 위한 delay가 한 번은 항상 생긴다. uclamp를 사용해도 이 비용은 달라지지 않는다. uclamp가 바꾸는 것은 schedutil이 모든 RT task에 무조건 MAX를 요청하는 대신 어떤 frequency를 요청할지 선택하는 부분이다. 기본값은 3.4절, RT 기본값 변경은 3.4.1절에서 다룬다.

2. 설계

118-180

util clamp는 system의 모든 task가 갖는 속성이다. task utilization signal의 경계를 정하고 scheduler 내부의 일부 결정에 bias를 주는 방식으로 동작한다.

실제 task utilization signal 자체를 변경하지는 않는다. 어느 시점에 PELT signal을 검사해도 원래 값은 그대로다. clamp는 task가 깨어나 scheduler가 실행할 CPU를 고르는 순간처럼, 그 값이 필요한 의사결정 지점에서만 적용된다.

task가 실행될 최소·최대 성능 지점을 요청하려면 frequency 선택과 task placement 양쪽에 영향을 줄 수 있어야 한다. 두 결정 모두 CPU runqueue, 줄여서 rq 수준의 utilization 값과 관계가 있으며 이것이 uclamp 설계의 핵심 과제다.

task가 rq에서 깨어나면 그 rq의 utilization signal은 enqueue된 모든 task의 uclamp 설정에 영향을 받는다. 예를 들어 한 task가 UTIL_MIN=512를 요청했다면 rq signal은 그 요청과 rq에 있는 다른 모든 task 요청을 함께 만족시켜야 한다.

rq에 붙은 모든 task의 util clamp 값을 집계하려면 enqueue와 dequeue마다 상태를 관리해야 한다. 이 위치는 scheduler hot path이므로 느려지면 많은 workload에 큰 영향을 주고 기능 자체의 실용성을 떨어뜨린다.

이를 해결하기 위해 전체 utilization 범위를 struct uclamp_bucket 여러 개로 나눈다. 그러면 rq의 모든 task를 검색하지 않고 가장 높은 non-empty bucket의 일부 task만 살펴보면 된다.

task enqueue 시 대응 bucket counter를 증가시키고 dequeue 시 감소시킨다. 이 방식으로 rq 수준의 effective uclamp 값을 더 적은 비용으로 추적한다.

task가 enqueue·dequeue될 때마다 rq의 현재 effective uclamp 값을 갱신한다. 자세한 동작은 2.1절에서 설명한다.

이후 rq의 effective uclamp가 필요한 경로는 의사결정을 내리는 정확한 시점에 이미 집계된 값을 읽기만 하면 된다.

현재 task placement에서는 Energy Aware Scheduling과 Capacity Aware Scheduling, 즉 EAS/CAS만 uclamp를 사용한다. 따라서 placement 영향은 heterogeneous system에 적용된다.

task가 깨어나면 scheduler는 각 rq의 현재 effective uclamp와 이 task를 그 rq에 enqueue했을 때의 예상 값을 비교하고, 가장 energy-efficient한 조합이 되는 rq를 선호한다.

schedutil도 frequency를 갱신할 때 현재 enqueue된 task 집합의 영향을 받은 rq effective uclamp를 읽고, 모든 요청 제약을 만족하는 frequency를 선택한다.

EAS를 사실상 비활성화하는 overutilization 상태를 정하는 경로도 uclamp를 사용한다. 이런 경로는 위 두 주요 사용례를 지원하는 내부 관리에 가깝고 구현 세부에 따라 달라질 수 있어 이 문서에서는 자세히 다루지 않는다.

2.1. Bucket

182-263
struct rq의 uclamp bucket 배치
Bucket 0Bucket 1...Bucket N
01 0부터 첫 경계다음 범위중간 범위마지막 경계부터 1024
02 p0, p1, p2p3다른 taskp4, p5
03 낮은 요청중간 요청counter로 집계가장 높은 요청

0부터 1024까지의 utilization 범위를 N개 bucket으로 나누고 task를 요청 값에 맞는 bucket에 집계한다. 이 그림은 개념도이며 실제 내부 자료구조를 그대로 나타낸 것은 아니다.

위 도식은 검색 범위를 줄이는 원리를 설명하기 위한 그림이며 struct rq와 struct uclamp_bucket의 실제 memory layout은 아니다.

enqueue·dequeue 때 rq effective uclamp를 결정하는 검색 범위를 줄이기 위해 전체 utilization 범위를 N개 bucket으로 나눈다. N은 compile time의 CONFIG_UCLAMP_BUCKETS_COUNT로 정하며 기본값은 5다.

rq에는 uclamp_id tunable인 UCLAMP_MIN과 UCLAMP_MAX 각각에 대한 bucket 집합이 있다.

각 bucket 범위는 1024/N이다. 기본값 5에서는 반올림한 DELTA가 205이고 다음 범위를 사용한다.

DELTA = round_closest(1024/5) = 204.8 = 205

Bucket 0: [0:204]
Bucket 1: [205:409]
Bucket 2: [410:614]
Bucket 3: [615:819]
Bucket 4: [820:1024]

다음 tunable을 가진 task p가 rq에 enqueue된다고 하자.

p->uclamp[UCLAMP_MIN] = 300
p->uclamp[UCLAMP_MAX] = 1024

UCLAMP_MIN에서는 bucket 1 counter를, UCLAMP_MAX에서는 bucket 4 counter를 증가시킨다. 이 rq에 각 범위의 task가 존재한다는 사실이 bucket에 반영된다.

rq는 uclamp_id마다 현재 effective uclamp 값을 별도로 추적한다.

task p를 enqueue할 때 rq 값은 다음 논리로 바뀐다.

// bucket 갱신 논리가 이 위치에서 실행된다
rq->uclamp[UCLAMP_MIN] = max(rq->uclamp[UCLAMP_MIN],
                              p->uclamp[UCLAMP_MIN])
// UCLAMP_MAX에도 같은 과정을 적용한다

task p를 dequeue할 때는 다음과 같이 남은 non-empty bucket 중 가장 높은 값을 찾는다.

// bucket 갱신 논리가 이 위치에서 실행된다
rq->uclamp[UCLAMP_MIN] = search_top_bucket_for_highest_value()
// UCLAMP_MAX에도 같은 과정을 적용한다

모든 bucket이 비면 rq uclamp 값은 system 기본값으로 돌아간다. 기본값은 3.4절에서 설명한다.

2.2-2.4. Max 집계, 계층 집계, 값의 범위

264-334

util clamp는 가장 높은 성능 지점을 요구하는 task의 요청을 만족하도록 조정된다.

여러 task가 같은 rq에 있으면 다른 task가 높은 성능을 필요로 하지 않거나 그 지점에 도달하지 못하도록 제한되었더라도, 가장 높은 성능을 요구하는 task가 필요한 성능을 얻을 수 있어야 한다.

예를 들어 같은 rq에 다음 두 task가 있다고 하자.

p0->uclamp[UCLAMP_MIN] = 300
p0->uclamp[UCLAMP_MAX] = 900

p1->uclamp[UCLAMP_MIN] = 500
p1->uclamp[UCLAMP_MAX] = 500

p0와 p1이 모두 같은 rq에 enqueue되면 UCLAMP_MIN과 UCLAMP_MAX 모두 각 요청의 max로 집계된다.

rq->uclamp[UCLAMP_MIN] = max(300, 500) = 500
rq->uclamp[UCLAMP_MAX] = max(900, 500) = 900
rq max aggregation
p0: MIN 300 / MAX 900p1: MIN 500 / MAX 500각 uclamp_id에서 max 계산rq: MIN 500 / MAX 900

MIN과 MAX는 서로 섞지 않고 uclamp_id별로 가장 큰 요청을 rq effective 값으로 선택한다.

5.1절에서 보듯 이 max aggregation은 특히 사용자 공간이 UCLAMP_MAX로 전력을 아끼려 할 때 나타나는 한계의 원인이다.

util clamp는 모든 task의 속성이지만 실제 적용되는 effective 값은 task 자신이나 middleware가 대신 요청한 값만으로 정해지지 않는다.

  • task가 속한 cgroup CPU controller의 uclamp 설정이 먼저 task 요청을 제한한다.
  • 그 결과는 다시 system-wide uclamp 설정의 제한을 받는다.

구체적인 interface는 3절에서 설명한다. task 요청은 cgroup과 system-wide 설정이 만든 경계를 지켜야 실제 effective 값이 된다.

요청이 현재 제약을 벗어나더라도 system은 값을 설정하는 작업 자체는 받아들인다. 이후 task가 다른 cgroup으로 이동하거나 system administrator가 전역 설정을 바꾸면 새 제약 안에 들어오는 범위에서 그 요청이 만족될 수 있다.

즉 task가 uclamp 값을 바꿀 때 계층 집계 때문에 error가 반환되는 방식이 아니라, 현재 cgroup과 system 설정에 따라 system이 그 요청을 그대로 충족하지 못할 수 있는 방식이다.

uclamp 성능 요청 범위는 0부터 1024까지이며 양 끝을 포함한다. cgroup interface에서는 0부터 100까지의 percentage를 사용하고, 다른 cgroup interface와 마찬가지로 100 대신 max를 쓸 수 있다.

3.1-3.2. Task와 cgroup interface

335-438

sched_setattr() system call에는 두 field가 추가되었다.

  • sched_util_min은 task가 실행될 때 system이 제공해야 할 최소 performance point, 즉 성능 하한을 요청한다.
  • sched_util_max는 task가 실행될 때 system이 넘지 않아야 할 최대 performance point, 즉 성능 상한을 요청한다.

다음은 utilization을 40%에서 80% 사이로 제한하는 예다.

attr->sched_util_min = 40% * 1024;
attr->sched_util_max = 80% * 1024;

task p가 실행될 때 scheduler는 가능한 한 40% 성능 수준에서 시작하도록 해야 한다. task가 충분히 오래 실행되어 실제 utilization이 80%를 넘으면 scheduler 의사결정에 쓰는 utilization 또는 성능 수준을 상한에 맞춘다.

특수값 -1은 uclamp 설정을 system 기본값으로 reset한다.

-1로 기본값을 상속하도록 reset하는 것과 현재 system 기본값 숫자를 직접 설정하는 것은 다르다. RT task의 기본값은 system interface로 바뀔 수 있기 때문에 이 차이가 중요하다. 향후 SCHED_NORMAL/SCHED_OTHER에도 비슷한 knob가 생길 수 있다.

CPU cgroup controller에는 cpu.uclamp.min과 cpu.uclamp.max 두 값이 있다.

cpu.uclamp.min은 cgroup v2 문서 3-3절의 protection이다. task의 uclamp_min이 cpu.uclamp.min보다 낮으면 task는 cgroup의 cpu.uclamp.min을 상속한다. cgroup hierarchy에서 effective cpu.uclamp.min은 child와 parent 중 큰 값이다.

cpu.uclamp.max는 cgroup v2 문서 3-2절의 limit다. task의 uclamp_max가 cpu.uclamp.max보다 높으면 task는 cgroup의 cpu.uclamp.max를 상속한다. hierarchy에서 effective cpu.uclamp.max는 child와 parent 중 작은 값이다.

항목계층 결합task에 주는 효과
cpu.uclamp.minmax(child, parent)task MIN이 낮으면 cgroup MIN으로 끌어올린다
cpu.uclamp.maxmin(child, parent)task MAX가 높으면 cgroup MAX로 낮춘다

다음 task와 cgroup 설정을 예로 든다.

p0->uclamp[UCLAMP_MIN] = // system default;
p0->uclamp[UCLAMP_MAX] = // system default;

p1->uclamp[UCLAMP_MIN] = 40% * 1024;
p1->uclamp[UCLAMP_MAX] = 50% * 1024;

cgroup0->cpu.uclamp.min = 20% * 1024;
cgroup0->cpu.uclamp.max = 60% * 1024;

cgroup1->cpu.uclamp.min = 60% * 1024;
cgroup1->cpu.uclamp.max = 100% * 1024;

p0와 p1을 cgroup0에 붙이면 값은 다음과 같다.

p0->uclamp[UCLAMP_MIN] = cgroup0->cpu.uclamp.min = 20% * 1024;
p0->uclamp[UCLAMP_MAX] = cgroup0->cpu.uclamp.max = 60% * 1024;

p1->uclamp[UCLAMP_MIN] = 40% * 1024; // 그대로 유지
p1->uclamp[UCLAMP_MAX] = 50% * 1024; // 그대로 유지

p0와 p1을 cgroup1에 붙이면 값은 다음처럼 바뀐다.

p0->uclamp[UCLAMP_MIN] = cgroup1->cpu.uclamp.min = 60% * 1024;
p0->uclamp[UCLAMP_MAX] = cgroup1->cpu.uclamp.max = 100% * 1024;

p1->uclamp[UCLAMP_MIN] = cgroup1->cpu.uclamp.min = 60% * 1024;
p1->uclamp[UCLAMP_MAX] = 50% * 1024; // 그대로 유지

cgroup interface는 cpu.uclamp.max를 cpu.uclamp.min보다 낮게 설정하는 것을 허용한다. 다른 uclamp interface에서는 이 관계를 허용하지 않는다.

3.3-3.4. System interface와 기본값

439-521

3.3.1 sched_util_clamp_min

sched_util_clamp_min은 허용할 UCLAMP_MIN 범위의 system-wide limit다. 기본값 1024에서는 task가 사용할 수 있는 effective UCLAMP_MIN 범위가 [0:1024]다. 이를 512로 바꾸면 [0:512]가 되어 task가 얻을 수 있는 boost의 최대치를 제한한다.

task가 이 knob보다 큰 값을 요청해도 설정 작업은 성공하지만, knob 값이 p->uclamp[UCLAMP_MIN]보다 커질 때까지 요청은 그대로 만족되지 않는다.

sched_util_clamp_min은 sched_util_clamp_max보다 작거나 같아야 한다.

3.3.2 sched_util_clamp_max

sched_util_clamp_max는 허용할 UCLAMP_MAX 범위의 system-wide limit다. 기본값 1024에서는 task의 effective UCLAMP_MAX 범위가 [0:1024]다.

이를 512로 바꾸면 허용 범위는 [0:512]가 된다. 어떤 task도 512보다 높은 성능에서 실행할 수 없고 모든 rq에도 같은 제한이 적용되므로, system 전체 성능이 절반으로 cap된다.

이 knob는 system 전체의 최대 성능 지점을 제한할 때 유용하다. battery가 부족하거나 idle·screen-off 상태에서 전력 소모가 큰 성능 수준에 접근하지 못하게 하는 용도로 쓸 수 있다.

task가 knob보다 높은 값을 요청해도 설정은 성공하지만, 전역 knob가 p->uclamp[UCLAMP_MAX]보다 커질 때까지 요청은 충족되지 않는다.

sched_util_clamp_max는 sched_util_clamp_min보다 크거나 같아야 한다.

3.4 기본값

기본적으로 모든 SCHED_NORMAL/SCHED_OTHER task는 다음 값으로 초기화된다.

p_fair->uclamp[UCLAMP_MIN] = 0
p_fair->uclamp[UCLAMP_MAX] = 1024

원문 488-490행의 문장은 중간 구절이 빠진 상태다. 값 자체가 뜻하는 바는 fair task가 기본적으로 boost도 cap도 받지 않아 [0:1024] 전체 범위를 사용할 수 있다는 것이다. 이 기본값을 boot 또는 runtime에 바꿀 별도 interface가 필요한지는 아직 근거가 정리되지 않았고 향후 추가될 수 있다.

SCHED_FIFO/SCHED_RR task의 기본값은 다음과 같다.

p_rt->uclamp[UCLAMP_MIN] = 1024
p_rt->uclamp[UCLAMP_MAX] = 1024

RT task는 기본적으로 system의 최대 성능 지점으로 boost된다. 이는 기존 RT task 동작을 유지하기 위한 값이다.

RT task의 기본 uclamp_min은 boot 또는 runtime에 sysctl로 바꿀 수 있다.

3.4.1 sched_util_clamp_min_rt_default

battery device에서 RT task를 항상 최대 성능으로 실행하는 것은 비싸고 반드시 필요하지도 않다. 이 sysctl은 system 요구사항을 만족할 만큼의 성능 보장은 유지하면서 항상 MAX에서 실행해 전력을 낭비하지 않도록 기본 boost 값을 조정한다.

application 개발자는 task별 util clamp interface로 성능과 전력을 직접 고려하는 것이 권장된다. 이상적으로 system designer는 이 knob를 0으로 두고 application이 자신의 성능 요구사항을 관리하게 해야 한다.

4. Util clamp 사용법

522-593

util clamp는 사용자 공간이 보조하는 전력·성능 관리 개념을 제공한다. scheduler만으로는 최선의 결정을 내리는 데 필요한 application 의미를 알 수 없지만, 사용자 공간은 util clamp를 통해 task placement와 frequency 선택에 필요한 hint를 줄 수 있다.

application이 실행되는 system을 미리 가정하지 않고 feedback loop로 실행 결과를 관찰하며 값을 동적으로 조정할 때 가장 좋은 결과를 얻는다. 최종 목표는 더 좋은 사용자 경험과 더 나은 performance per watt다.

일부 system과 사용례에서는 정적 설정도 좋은 결과를 내지만 이식성이 문제가 된다. 100, 200, 1024라는 값으로 수행할 수 있는 일의 양은 system마다 다르므로 특정 target system이 없다면 정적 설정을 피하는 편이 좋다.

util clamp를 중심으로 완전한 관리 framework를 만들 수도 있고 application이 직접 interface를 사용할 수도 있다.

4.1 중요한 task와 DVFS latency에 민감한 task boost

GUI task는 wakeup 순간의 util_avg만 보면 frequency를 높일 만큼 busy하지 않을 수 있다. 하지만 사용자 경험을 위해 정해진 시간 안에 작업을 끝내야 하며, wakeup 때 필요한 frequency는 system 성능에 따라 달라진다. 느린 system에서는 높을 수 있고 충분히 빠른 system에서는 낮거나 0일 수도 있다.

task가 deadline을 놓칠 때마다 UCLAMP_MIN을 높이면 다음 wakeup에서는 더 높은 성능에서 시작한다. 각 system에서 deadline을 만족하는 가장 낮은 UCLAMP_MIN에 수렴하도록 조정해야 최적의 performance per watt를 얻는다.

heterogeneous system에서는 이 task를 더 빠른 CPU에 배치하는 것도 중요할 수 있다.

일반적으로 입력값은 단순 utilization이 아니라 task placement와 frequency 선택을 함께 뜻하는 performance level 또는 performance point로 해석해야 한다.

4.2 Background task cap

소개에서 설명한 Android 사례처럼 application은 성능이 중요하지 않지만 busy해져 불필요한 system resource를 쓸 수 있는 background task의 UCLAMP_MAX를 낮출 수 있다.

4.3 Powersave mode

system-wide sched_util_clamp_max로 모든 task가 일반적으로 energy efficiency가 낮은 고성능 지점에서 동작하지 못하게 제한할 수 있다.

같은 결과는 cpufreq governor의 maximum frequency를 낮춰도 얻을 수 있으므로 uclamp만의 고유 기능은 아니다. 더 편리한 대체 interface로 볼 수 있다.

4.4 Application별 성능 제한

middleware나 utility는 application을 실행할 때마다 UCLAMP_MIN/MAX를 설정하는 사용자 option을 제공할 수 있다. 최소 성능을 보장하거나, 성능 저하를 감수하는 대신 application이 system power를 지나치게 소비하지 못하도록 제한한다.

예를 들어 이동 중 laptop에서 kernel을 compile할 때 성능을 희생해 발열과 전력 소비를 낮추면서 browser 성능은 그대로 유지하도록 서로 다른 uclamp 제약을 적용할 수 있다.

5.1. Max aggregation 때문에 UCLAMP_MAX cap이 실패하는 경우

594-628

task p0의 실행 성능을 512로 cap했다고 하자.

p0->uclamp[UCLAMP_MAX] = 512

p0가 모든 performance point를 사용할 수 있는 p1과 rq를 공유한다.

p1->uclamp[UCLAMP_MAX] = 1024

max aggregation 때문에 rq에는 최대 performance point가 허용된다.

rq->uclamp[UCLAMP_MAX] = max(512, 1024) = 1024

p0와 p1의 UCLAMP_MIN이 모두 0이라면 rq frequency는 두 task의 실제 utilization에 따라 정해진다.

p1이 작은 task이고 p0가 CPU-intensive task라면 둘이 같은 rq에서 실행된다는 이유만으로 p1이 rq의 frequency cap을 풀어 버린다. p1은 모든 성능 지점을 사용할 권한은 있지만 실제로 높은 frequency가 필요하지 않을 수 있는데도 이 결과가 생긴다.

5.2. UCLAMP_MAX가 PELT util_avg를 왜곡하는 경우

629-714

PELT는 signal이 커지면 frequency도 올라 CPU에 일정한 idle 시간이 생긴다고 가정한다. UCLAMP_MAX가 frequency 상승을 막으면 어떤 상황에서는 idle 시간이 완전히 사라진다. task가 계속 busy loop에 머물면 util_avg는 1024까지 올라간다.

앞의 max aggregation 문제와 결합하면 심하게 cap된 task가 작은 non-capped task와 rq를 공유할 때 원하지 않는 frequency spike가 발생할 수 있다.

다음 값을 가진 task p0가 idle CPU에서 깨어난다고 하자.

p0->util_avg = 300
p0->uclamp[UCLAMP_MAX] = 0

rq->uclamp[UCLAMP_MAX] = 0

p0는 그 CPU가 지원하는 minimum frequency Fmin에서 실행된다. 이 작업을 가장 짧은 시간에 끝낼 때의 frequency가 Fmax이므로 Fmax/Fmin 비율이 결과에 중요하다.

Fmax/Fmin이 3이면 최대 환산 부하는 다음과 같다.

300 * (Fmax/Fmin) = 900

900은 1024보다 작으므로 CPU에는 여전히 idle 시간이 생긴다. 실제 util_avg가 정확히 900이 되는 것은 아니며 300과 900 사이에 있다. idle 시간이 남는 동안 p0->util_avg 갱신에는 작은 오차가 생기지만 Fmax/Fmin에 정확히 비례하지는 않는다.

p0->util_avg = 300 + small_error

Fmax/Fmin이 4이면 최대 환산 부하는 다음처럼 1024를 넘는다.

300 * (Fmax/Fmin) = 1200

p0->util_avg = 1024

CPU에 idle 시간이 사라지면 실제 util_avg가 1024에 도달한다. 이때 다음 값을 가진 p1이 같은 CPU에서 깨어난다고 하자.

p1->util_avg = 200
p1->uclamp[UCLAMP_MAX] = 1024

max aggregation에 따라 CPU의 effective UCLAMP_MAX는 1024가 된다. p0가 심하게 throttling된 채 계속 실행되었으므로 rq 상태는 다음과 같다.

p0->util_avg = 1024
p1->util_avg = 200

rq->util_avg = 1024
rq->uclamp[UCLAMP_MAX] = 1024

p0가 throttling되지 않았다면 p0 300과 p1 200을 합쳐 rq->util_avg=500 정도가 되어 CPU의 중간 성능 지점 부근에서 실행했을 것이다. 실제로는 누적된 1024와 풀린 UCLAMP_MAX 때문에 Fmax로 뛰어 frequency spike가 난다.

p0->util_avg = 300
p1->util_avg = 200

rq->util_avg = 500
심한 cap 뒤에 생기는 frequency spike
p0: util_avg 300, UCLAMP_MAX 0Fmin 실행, Fmax/Fmin=4idle 소멸, p0 util_avg=1024p1: util_avg 200, UCLAMP_MAX 1024 wakeuprq UCLAMP_MAX=1024, rq util_avg=1024중간 성능이 아니라 Fmax 요청

낮은 frequency에서 idle이 사라져 p0의 util_avg가 포화된 뒤 작은 non-capped task가 UCLAMP_MAX를 풀면, 포화된 rq signal이 즉시 높은 frequency를 요구한다.

5.3. Schedutil 응답 시간의 한계

715-741

schedutil에는 세 가지 한계가 있다.

  • hardware가 frequency 변경 요청에 응답하는 데는 0이 아닌 시간이 들며, 일부 platform에서는 수 ms가 걸린다.
  • fast-switch를 지원하지 않는 system은 worker deadline thread가 깨어나 frequency를 바꿔야 하므로 측정 가능한 overhead가 더해진다.
  • schedutil의 rate_limit_us 구간 안에 들어온 새 요청은 버려진다.

작은 task가 시간 제약이 큰 작업을 수행하여 wakeup 직후 특정 performance point가 필요하더라도 이 한계 때문에 기대한 시간 안에 원하는 성능을 받지 못할 수 있다.

이 문제는 uclamp에서만 나타나는 것은 아니지만, uclamp를 사용하면 점진적인 frequency ramp-up·ramp-down 대신 task wakeup 순서와 각 uclamp 값에 따라 frequency 사이를 크게 뛰어다닐 수 있어 더 자주 드러날 수 있다.

문서는 이를 기반 system 능력의 한계로 본다.

schedutil rate_limit_us의 동작은 개선할 여지가 있지만 hardware 응답 시간과 non-fast-switch worker overhead는 해결하기 어렵다. 두 항목은 system의 hard limitation으로 간주한다.