aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle
diff options
context:
space:
mode:
authorYouquan Song <youquan.song@intel.com>2012-10-26 06:27:07 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2012-11-14 18:34:20 -0500
commitc96ca4fb76b711279be063da083f09b8d65af5c5 (patch)
tree391f775fd069d79bd8aa1586c6b5ed848b7f5ee5 /drivers/cpuidle
parentd73d68dc49e09143e8e3bef10670a021c26ec4a5 (diff)
cpuidle: Get typical recent sleep interval
The function detect_repeating_patterns was not very useful for workloads with alternating long and short pauses, for example virtual machines handling network requests for each other (say a web and database server). Instead, try to find a recent sleep interval that is somewhere between the median and the mode sleep time, by discarding outliers to the up side and recalculating the average and standard deviation until that is no longer required. This should do something sane with a sleep interval series like: 200 180 210 10000 30 1000 170 200 The current code would simply discard such a series, while the new code will guess a typical sleep interval just shy of 200. The original patch come from Rik van Riel <riel@redhat.com>. Signed-off-by: Rik van Riel <riel@redhat.com> Signed-off-by: Youquan Song <youquan.song@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r--drivers/cpuidle/governors/menu.c69
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 */
248static int detect_repeating_patterns(struct menu_device *data) 248static 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 */ 255again:
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