diff options
author | Rasmus Villemoes <linux@rasmusvillemoes.dk> | 2016-02-16 14:19:18 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-02-16 18:27:16 -0500 |
commit | 7024b18ca461bab45e5fb329f6e3d904d5109401 (patch) | |
tree | 2dd0d4bc8479ccf843094e6c584fafb7dd3260ac /drivers/cpuidle | |
parent | 18558cae0272f8fd9647e69d3fec1565a7949865 (diff) |
cpuidle: menu: avoid expensive square root computation
Computing the integer square root is a rather expensive operation, at
least compared to doing a 64x64 -> 64 multiply (avg*avg) and, on 64
bit platforms, doing an extra comparison to a constant (variance <=
U64_MAX/36).
On 64 bit platforms, this does mean that we add a restriction on the
range of the variance where we end up using the estimate (since
previously the stddev <= ULONG_MAX was a tautology), but on the other
hand, we extend the range quite substantially on 32 bit platforms - in
both cases, we now allow standard deviations up to 715 seconds, which
is for example guaranteed if all observations are less than 1430
seconds.
Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 35 |
1 files changed, 17 insertions, 18 deletions
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 0742b3296673..beef7ae123ba 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c | |||
@@ -200,7 +200,7 @@ static void get_typical_interval(struct menu_device *data) | |||
200 | { | 200 | { |
201 | int i, divisor; | 201 | int i, divisor; |
202 | unsigned int max, thresh; | 202 | unsigned int max, thresh; |
203 | uint64_t avg, stddev; | 203 | uint64_t avg, variance; |
204 | 204 | ||
205 | thresh = UINT_MAX; /* Discard outliers above this value */ | 205 | thresh = UINT_MAX; /* Discard outliers above this value */ |
206 | 206 | ||
@@ -224,36 +224,35 @@ again: | |||
224 | else | 224 | else |
225 | do_div(avg, divisor); | 225 | do_div(avg, divisor); |
226 | 226 | ||
227 | /* Then try to determine standard deviation */ | 227 | /* Then try to determine variance */ |
228 | stddev = 0; | 228 | variance = 0; |
229 | for (i = 0; i < INTERVALS; i++) { | 229 | for (i = 0; i < INTERVALS; i++) { |
230 | unsigned int value = data->intervals[i]; | 230 | unsigned int value = data->intervals[i]; |
231 | if (value <= thresh) { | 231 | if (value <= thresh) { |
232 | int64_t diff = value - avg; | 232 | int64_t diff = value - avg; |
233 | stddev += diff * diff; | 233 | variance += diff * diff; |
234 | } | 234 | } |
235 | } | 235 | } |
236 | if (divisor == INTERVALS) | 236 | if (divisor == INTERVALS) |
237 | stddev >>= INTERVAL_SHIFT; | 237 | variance >>= INTERVAL_SHIFT; |
238 | else | 238 | else |
239 | do_div(stddev, divisor); | 239 | do_div(variance, divisor); |
240 | 240 | ||
241 | /* | 241 | /* |
242 | * The typical interval is obtained when standard deviation is small | 242 | * The typical interval is obtained when standard deviation is |
243 | * or standard deviation is small compared to the average interval. | 243 | * small (stddev <= 20 us, variance <= 400 us^2) or standard |
244 | * | 244 | * deviation is small compared to the average interval (avg > |
245 | * int_sqrt() formal parameter type is unsigned long. When the | 245 | * 6*stddev, avg^2 > 36*variance). The average is smaller than |
246 | * greatest difference to an outlier exceeds ~65 ms * sqrt(divisor) | 246 | * UINT_MAX aka U32_MAX, so computing its square does not |
247 | * the resulting squared standard deviation exceeds the input domain | 247 | * overflow a u64. We simply reject this candidate average if |
248 | * of int_sqrt on platforms where unsigned long is 32 bits in size. | 248 | * the standard deviation is greater than 715 s (which is |
249 | * In such case reject the candidate average. | 249 | * rather unlikely). |
250 | * | 250 | * |
251 | * Use this result only if there is no timer to wake us up sooner. | 251 | * Use this result only if there is no timer to wake us up sooner. |
252 | */ | 252 | */ |
253 | if (likely(stddev <= ULONG_MAX)) { | 253 | if (likely(variance <= U64_MAX/36)) { |
254 | stddev = int_sqrt(stddev); | 254 | if (((avg*avg > variance*36) && (divisor * 4 >= INTERVALS * 3)) |
255 | if (((avg > stddev * 6) && (divisor * 4 >= INTERVALS * 3)) | 255 | || variance <= 400) { |
256 | || stddev <= 20) { | ||
257 | if (data->next_timer_us > avg) | 256 | if (data->next_timer_us > avg) |
258 | data->predicted_us = avg; | 257 | data->predicted_us = avg; |
259 | return; | 258 | return; |