aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle
diff options
context:
space:
mode:
authorArjan van de Ven <arjan@linux.intel.com>2010-05-24 17:32:59 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-05-25 11:07:02 -0400
commit1f85f87d4f81d1e5a2d502d48316a1bdc5acac0b (patch)
treeef9254e52665274ae4d0fd1381cc2ae5a48791f6 /drivers/cpuidle
parent6cdafaae41d52e6ef9a5c5be23602ef083e4d0f9 (diff)
cpuidle: add a repeating pattern detector to the menu governor
Currently, the menu governor uses the (corrected) next timer as key item for predicting the idle duration. It turns out that there are specific cases where this breaks down: There are cases where we have a very repetitive pattern of idle durations, where the idle period is pretty much the same, for reasons completely unrelated to the next timer event. Examples of such repeating patterns are network loads with irq mitigation, the mouse moving but in theory also the wifi beacons. This patch adds a relatively simple detector for such repeating patterns, where the standard deviation of the last 8 idle periods is compared to a threshold. With this extra predictor in place, measurements show that the DECAY factor can now be increased (the decaying average will now decay slower) to get an even more stable result. [arjan@infradead.org: fix bug identified by Frank] Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Cc: Corrado Zoccolo <czoccolo@gmail.com> Cc: Frank Rowand <frank.rowand@am.sony.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r--drivers/cpuidle/governors/menu.c60
1 files changed, 59 insertions, 1 deletions
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index b81ad9c731a..52ff8aa63f8 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -21,9 +21,12 @@
21#include <linux/math64.h> 21#include <linux/math64.h>
22 22
23#define BUCKETS 12 23#define BUCKETS 12
24#define INTERVALS 8
24#define RESOLUTION 1024 25#define RESOLUTION 1024
25#define DECAY 4 26#define DECAY 8
26#define MAX_INTERESTING 50000 27#define MAX_INTERESTING 50000
28#define STDDEV_THRESH 400
29
27 30
28/* 31/*
29 * Concepts and ideas behind the menu governor 32 * Concepts and ideas behind the menu governor
@@ -64,6 +67,16 @@
64 * indexed based on the magnitude of the expected duration as well as the 67 * indexed based on the magnitude of the expected duration as well as the
65 * "is IO outstanding" property. 68 * "is IO outstanding" property.
66 * 69 *
70 * Repeatable-interval-detector
71 * ----------------------------
72 * There are some cases where "next timer" is a completely unusable predictor:
73 * Those cases where the interval is fixed, for example due to hardware
74 * interrupt mitigation, but also due to fixed transfer rate devices such as
75 * mice.
76 * For this, we use a different predictor: We track the duration of the last 8
77 * intervals and if the stand deviation of these 8 intervals is below a
78 * threshold value, we use the average of these intervals as prediction.
79 *
67 * Limiting Performance Impact 80 * Limiting Performance Impact
68 * --------------------------- 81 * ---------------------------
69 * C states, especially those with large exit latencies, can have a real 82 * C states, especially those with large exit latencies, can have a real
@@ -104,6 +117,8 @@ struct menu_device {
104 unsigned int exit_us; 117 unsigned int exit_us;
105 unsigned int bucket; 118 unsigned int bucket;
106 u64 correction_factor[BUCKETS]; 119 u64 correction_factor[BUCKETS];
120 u32 intervals[INTERVALS];
121 int interval_ptr;
107}; 122};
108 123
109 124
@@ -175,6 +190,42 @@ static u64 div_round64(u64 dividend, u32 divisor)
175 return div_u64(dividend + (divisor / 2), divisor); 190 return div_u64(dividend + (divisor / 2), divisor);
176} 191}
177 192
193/*
194 * Try detecting repeating patterns by keeping track of the last 8
195 * intervals, and checking if the standard deviation of that set
196 * of points is below a threshold. If it is... then use the
197 * average of these 8 points as the estimated value.
198 */
199static void detect_repeating_patterns(struct menu_device *data)
200{
201 int i;
202 uint64_t avg = 0;
203 uint64_t stddev = 0; /* contains the square of the std deviation */
204
205 /* first calculate average and standard deviation of the past */
206 for (i = 0; i < INTERVALS; i++)
207 avg += data->intervals[i];
208 avg = avg / INTERVALS;
209
210 /* if the avg is beyond the known next tick, it's worthless */
211 if (avg > data->expected_us)
212 return;
213
214 for (i = 0; i < INTERVALS; i++)
215 stddev += (data->intervals[i] - avg) *
216 (data->intervals[i] - avg);
217
218 stddev = stddev / INTERVALS;
219
220 /*
221 * now.. if stddev is small.. then assume we have a
222 * repeating pattern and predict we keep doing this.
223 */
224
225 if (avg && stddev < STDDEV_THRESH)
226 data->predicted_us = avg;
227}
228
178/** 229/**
179 * menu_select - selects the next idle state to enter 230 * menu_select - selects the next idle state to enter
180 * @dev: the CPU 231 * @dev: the CPU
@@ -218,6 +269,8 @@ static int menu_select(struct cpuidle_device *dev)
218 data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket], 269 data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket],
219 RESOLUTION * DECAY); 270 RESOLUTION * DECAY);
220 271
272 detect_repeating_patterns(data);
273
221 /* 274 /*
222 * We want to default to C1 (hlt), not to busy polling 275 * We want to default to C1 (hlt), not to busy polling
223 * unless the timer is happening really really soon. 276 * unless the timer is happening really really soon.
@@ -310,6 +363,11 @@ static void menu_update(struct cpuidle_device *dev)
310 new_factor = 1; 363 new_factor = 1;
311 364
312 data->correction_factor[data->bucket] = new_factor; 365 data->correction_factor[data->bucket] = new_factor;
366
367 /* update the repeating-pattern data */
368 data->intervals[data->interval_ptr++] = last_idle_us;
369 if (data->interval_ptr >= INTERVALS)
370 data->interval_ptr = 0;
313} 371}
314 372
315/** 373/**