Re: [PATCH v2 0/6] cpuidle: speed up do_idle() by caching the governor latency QoS constraint
From: Yaxiong Tian
Date: Thu Sep 17 2026 - 22:10:45 EST
On 2026/9/18 03:34, Rafael J. Wysocki (Intel) wrote:
> On Wed, Jul 29, 2026 at 8:16 AM Yaxiong Tian <tianyaxiong@xxxxxxxxxx> wrote:
>> cpuidle_governor_latency_req() is evaluated on every idle-state
>> selection. It aggregates the per-CPU resume latency with the global
>> CPU latency and wakeup latency QoS limits
> True.
>
>> by repeatedly calling
>> get_cpu_device() and pm_qos_read_value() cpu_latency_qos_limit()
>> cpu_wakeup_latency_qos_limit().
> I would say "every time" rather than "repeatedly", but fair enough.
>
> However, those functions don't do anything expensive except for
> defensive checks that can be omitted. Have you tried to omit them?
I haven't tried that, but I found that function_graph may introduce measurement errors, especially for very short functions.
>
>> Use ftrace's function_graph, we can see:
>> parent: do_idle
>> parent_total_ns: 36671010505
>> parent_count: 5994
>>
>> SYMBOL TIME_NS %ROOT %PARENT COUNT
>> --------------------------------------------------------------------------------------------------
>> do_idle 36671010505 100.00% 100.00% 5994
>> cpuidle_idle_call 35566528731 96.99% 96.99% 8570
>> call_cpuidle 35476606844 96.74% 99.75% 8561
>> cpuidle_enter 35472932468 96.73% 99.99% 8526
>> cpuidle_select 52097031 0.14% 0.15% 8580
>> menu_select 49555181 0.14% 95.12% 8580
>> tick_nohz_get_sleep_length 28843887 0.08% 58.21% 8570
>> cpuidle_governor_latency_req 9567488 0.03% 19.31% 8580
>> tick_nohz_tick_stopped 2057031 0.01% 4.15% 15695
>> cpuidle_reflect 11427201 0.03% 0.03% 8561
>> menu_reflect 6579506 0.02% 57.58% 8526
>> tick_nohz_idle_got_tick 2231559 0.01% 33.92% 8526
>> __sysvec_apic_timer_interrupt 105520 0.00% 0.92% 3
>> tick_nohz_idle_stop_tick 8279641 0.02% 0.02% 1475
>> ---- skip
>>
>> The majority of the time spent in cpuidle_enter for CPUs entering
>> idle state has already been charged to the idle path. Among the
>> remaining contributors, cpuidle_governor_latency_req() accounts
>> for a non-negligible portion of the overall latency.
>>
>> Under the menu governor this shows up hot: ftrace data shows,
>> cpuidle_governor_latency_req() accounts for about 19.9% of
>> menu_select() time (~1.9 us/call). After caching the aggregated
>> value per CPU and invalidating via QoS notifiers, that share drops to
>> about 4.2% (~0.3 us/call), roughly a 6x reduction on this path.
> Is there any real-world workload in which that difference is actually visible?
Sorry, I only tested it under no load.
>
>> The ftrace data before and after the optimization is shown below:
>> 1) original
>> parent: menu_select
>> parent_total_ns: 160492937
>> parent_count: 16718
>>
>> SYMBOL TIME_NS %ROOT %PARENT COUNT
>> --------------------------------------------------------------------------------------------------
>> menu_select 160492937 100.00% 100.00% 16718
> Where did you lose menu_update()?
Because menu_update() was optimized away by the compiler, it can't be seen in /proc/kallsyms, so it won't be traced by ftrace either.
>
>> tick_nohz_get_sleep_length 100262940 62.47% 62.47% 16698
>> tick_nohz_next_event 67891299 42.30% 67.71% 16689
>> rcu_needs_cpu 2825649 1.76% 4.16% 16689
>> timekeeping_max_deferment 2380377 1.48% 3.51% 15296
>> hrtimer_next_event_without 17865162 11.13% 17.82% 15296
>> hrtimer_bases_next_event_without 2707631 1.69% 15.16% 15296
>> _raw_spin_lock_irqsave 2461132 1.53% 13.78% 15296
>> native_queued_spin_lock_slowpath 177 0.00% 0.01% 1
>> _raw_spin_unlock_irqrestore 2364647 1.47% 13.24% 15296
>> can_stop_idle_tick 2906072 1.81% 2.90% 16698
>> cpuidle_governor_latency_req 31988150 19.93% 19.93% 16718
>> get_cpu_device 4122502 2.57% 12.89% 16718
>> pm_qos_read_value 3427318 2.14% 10.71% 16718
>> cpu_latency_qos_limit 3005804 1.87% 9.40% 16718
>> cpu_wakeup_latency_qos_limit 3005475 1.87% 9.40% 16718
>> tick_nohz_tick_stopped 4551981 2.84% 2.84% 29496
>>
>> %ROOT = share of menu_select; %PARENT = share of immediate caller (inclusive)
>>
>> 2) post-optimized
>> parent: menu_select
>> parent_total_ns: 55428604
>> parent_count: 7626
>>
>> SYMBOL TIME_NS %ROOT %PARENT COUNT
>> --------------------------------------------------------------------------------------------------
>> menu_select 55428604 100.00% 100.00% 7626
> And here?
>
>> tick_nohz_get_sleep_length 37464607 67.59% 67.59% 7544
>> tick_nohz_next_event 24076913 43.44% 64.27% 7522
>> get_next_timer_interrupt 16332381 29.47% 67.83% 5854
>> rcu_needs_cpu 1489633 2.69% 6.19% 7522
>> timekeeping_max_deferment 870851 1.57% 3.62% 5586
>> hrtimer_next_event_without 6140153 11.08% 16.39% 5586
>> hrtimer_bases_next_event_without 979000 1.77% 15.94% 5586
>> _raw_spin_lock_irqsave 808786 1.46% 13.17% 5586
>> _raw_spin_unlock_irqrestore 785518 1.42% 12.79% 5586
>> can_stop_idle_tick 1583137 2.86% 4.23% 7544
>> cpuidle_governor_latency_req 2321119 4.19% 4.19% 7626
>> tick_nohz_tick_stopped 1863015 3.36% 3.36% 12751
>>
>> %ROOT = share of menu_select; %PARENT = share of immediate caller (inclusive)
>>
>> This series:
>> - patch1~pathc3: Add QoS notifiers-related infrastructure and updates to
>> latency_req_gen.
>> - patch4: Implement QoS constraint for cache-aggregated governor latency
>> - patch5: add latency_req QoS idle-state selection test
>> - patch6: add idle-state disable selftest
> So this adds two per-CPU variables and a ton of extra complexity to
> optimize the case in which the majority of the governor computations
> aren't actually done.
>
> I'm totally unconvinced.
I agree, it doesn't seem worth the effort.
>
> Thanks!