8000 cpuidle: Replace ktime_get() with local_clock() · bsd-unix/linux@e93e59c · GitHub
[go: up one dir, main page]

Skip to content

Commit e93e59c

Browse files
dlezcanorafaeljw
authored andcommitted
cpuidle: Replace ktime_get() with local_clock()
The ktime_get() can have a non negligeable overhead, use local_clock() instead. In order to test the difference between ktime_get() and local_clock(), a quick hack has been added to trigger, via debugfs, 10000 times a call to ktime_get() and local_clock() and measure the elapsed time. Then the average value, the min and max is computed for each call. From userspace, the test above was called 100 times every 2 seconds. So, ktime_get() and local_clock() have been called 1000000 times in total. The results are: ktime_get(): ============ * average: 101 ns (stddev: 27.4) * maximum: 38313 ns * minimum: 65 ns local_clock(): ============== * average: 60 ns (stddev: 9.8) * maximum: 13487 ns * minimum: 46 ns The local_clock() is faster and more stable. Even if it is a drop in the ocean, changing the ktime_get() by the local_clock() allows to save 80ns at idle time (entry + exit). And in some circumstances, especially when there are several CPUs racing for the clock access, we save tens of microseconds. The idle duration resulting from a diff is converted from nanosec to microsec. This could be done with integer division (div 1000) - which is an expensive operation or by 10 bits shifting (div 1024) - which is fast but unprecise. The following table gives some results at the limits. ------------------------------------------ | nsec | div(1000) | div(1024) | ------------------------------------------ | 1e3 | 1 usec | 976 nsec | ------------------------------------------ | 1e6 | 1000 usec | 976 usec | ------------------------------------------ | 1e9 | 1000000 usec | 976562 usec | ------------------------------------------ There is a linear deviation of 2.34%. This loss of precision is acceptable in the context of the resulting diff which is used for statistics. These ones are processed to guess estimate an approximation of the duration of the next idle period which ends up into an idle state selection. The selection criteria takes into account the next duration based on large intervals, represented by the idle state's target residency. The 2^10 division is enough because the approximation regarding the 1e3 division is lost in all the approximations done for the next idle duration computation. Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> [ rjw: Subject ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent f0fb0dd commit e93e59c

File tree

1 file changed

+8
-4
lines changed

1 file changed

+8
-4
lines changed

drivers/cpuidle/cpuidle.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
173173

174174
struct cpuidle_state *target_state = &drv->states[index];
175175
bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
176-
ktime_t time_start, time_end;
176+
u64 time_start, time_end;
177177
s64 diff;
178178

179179
/*
@@ -195,13 +195,13 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
195195
sched_idle_set_state(target_state);
196196

197197
trace_cpu_idle_rcuidle(index, dev->cpu);
198-
time_start = ktime_get();
198+
time_start = local_clock();
199199

200200
stop_critical_timings();
201201
entered_state = target_state->enter(dev, drv, index);
202202
start_critical_timings();
203203

204-
time_end = ktime_get();
204+
time_end = local_clock();
205205
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
206206

207207
/* The cpu is no longer idle or about to enter idle. */
@@ -217,7 +217,11 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
217217
if (!cpuidle_state_is_coupled(drv, entered_state))
218218
local_irq_enable();
219219

220-
diff = ktime_to_us(ktime_sub(time_end, time_start));
220+
/*
221+
* local_clock() returns the time in nanosecond, let's shift
222+
* by 10 (divide by 1024) to have microsecond based time.
223+
*/
224+
diff = (time_end - time_start) >> 10;
221225
if (diff > INT_MAX)
222226
diff = INT_MAX;
223227

0 commit comments

Comments
 (0)
0