diff options
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 69 |
1 files changed, 46 insertions, 23 deletions
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 43a54fd6bfa2..2efee27714a0 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c | |||
@@ -245,36 +245,59 @@ static enum hrtimer_restart menu_hrtimer_notify(struct hrtimer *hrtimer) | |||
245 | * of points is below a threshold. If it is... then use the | 245 | * of points is below a threshold. If it is... then use the |
246 | * average of these 8 points as the estimated value. | 246 | * average of these 8 points as the estimated value. |
247 | */ | 247 | */ |
248 | static int detect_repeating_patterns(struct menu_device *data) | 248 | static u32 get_typical_interval(struct menu_device *data) |
249 | { | 249 | { |
250 | int i; | 250 | int i = 0, divisor = 0; |
251 | uint64_t avg = 0; | 251 | uint64_t max = 0, avg = 0, stddev = 0; |
252 | uint64_t stddev = 0; /* contains the square of the std deviation */ | 252 | int64_t thresh = LLONG_MAX; /* Discard outliers above this value. */ |
253 | int ret = 0; | 253 | unsigned int ret = 0; |
254 | |||
255 | /* first calculate average and standard deviation of the past */ | ||
256 | for (i = 0; i < INTERVALS; i++) | ||
257 | avg += data->intervals[i]; | ||
258 | avg = avg / INTERVALS; | ||
259 | 254 | ||
260 | /* if the avg is beyond the known next tick, it's worthless */ | 255 | again: |
261 | if (avg > data->expected_us) | ||
262 | return 0; | ||
263 | |||
264 | for (i = 0; i < INTERVALS; i++) | ||
265 | stddev += (data->intervals[i] - avg) * | ||
266 | (data->intervals[i] - avg); | ||
267 | 256 | ||
268 | stddev = stddev / INTERVALS; | 257 | /* first calculate average and standard deviation of the past */ |
258 | max = avg = divisor = stddev = 0; | ||
259 | for (i = 0; i < INTERVALS; i++) { | ||
260 | int64_t value = data->intervals[i]; | ||
261 | if (value <= thresh) { | ||
262 | avg += value; | ||
263 | divisor++; | ||
264 | if (value > max) | ||
265 | max = value; | ||
266 | } | ||
267 | } | ||
268 | do_div(avg, divisor); | ||
269 | 269 | ||
270 | for (i = 0; i < INTERVALS; i++) { | ||
271 | int64_t value = data->intervals[i]; | ||
272 | if (value <= thresh) { | ||
273 | int64_t diff = value - avg; | ||
274 | stddev += diff * diff; | ||
275 | } | ||
276 | } | ||
277 | do_div(stddev, divisor); | ||
278 | stddev = int_sqrt(stddev); | ||
270 | /* | 279 | /* |
271 | * now.. if stddev is small.. then assume we have a | 280 | * If we have outliers to the upside in our distribution, discard |
272 | * repeating pattern and predict we keep doing this. | 281 | * those by setting the threshold to exclude these outliers, then |
282 | * calculate the average and standard deviation again. Once we get | ||
283 | * down to the bottom 3/4 of our samples, stop excluding samples. | ||
284 | * | ||
285 | * This can deal with workloads that have long pauses interspersed | ||
286 | * with sporadic activity with a bunch of short pauses. | ||
287 | * | ||
288 | * The typical interval is obtained when standard deviation is small | ||
289 | * or standard deviation is small compared to the average interval. | ||
273 | */ | 290 | */ |
274 | 291 | if (((avg > stddev * 6) && (divisor * 4 >= INTERVALS * 3)) | |
275 | if (avg && stddev < STDDEV_THRESH) { | 292 | || stddev <= 20) { |
276 | data->predicted_us = avg; | 293 | data->predicted_us = avg; |
277 | ret = 1; | 294 | ret = 1; |
295 | return ret; | ||
296 | |||
297 | } else if ((divisor * 4) > INTERVALS * 3) { | ||
298 | /* Exclude the max interval */ | ||
299 | thresh = max - 1; | ||
300 | goto again; | ||
278 | } | 301 | } |
279 | 302 | ||
280 | return ret; | 303 | return ret; |
@@ -330,7 +353,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
330 | data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket], | 353 | data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket], |
331 | RESOLUTION * DECAY); | 354 | RESOLUTION * DECAY); |
332 | 355 | ||
333 | repeat = detect_repeating_patterns(data); | 356 | repeat = get_typical_interval(data); |
334 | 357 | ||
335 | /* | 358 | /* |
336 | * We want to default to C1 (hlt), not to busy polling | 359 | * We want to default to C1 (hlt), not to busy polling |