summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2025-02-06 15:26:41 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2025-02-25 12:00:35 +0100
commit8de7606f0fe2bf5a918fe97d425e16e190a24fe6 (patch)
treeff35f76f5f696f8b57e74f7409f0e3f821299438
parent60256e458e1c29652b2f9e4f2ba71fc7b09bd30c (diff)
cpuidle: menu: Eliminate outliers on both ends of the sample set
Currently, get_typical_interval() attempts to eliminate outliers at the high end of the sample set only (probably in order to bias the prediction toward lower values), but this it problematic because if the outliers are present at the low end of the sample set, discarding the highest values will not help to reduce the variance. Since the presence of outliers at the low end of the sample set is generally as likely as their presence at the high end of the sample set, modify get_typical_interval() to treat samples at the largest distances from the average (on both ends of the sample set) as outliers. This should increase the likelihood of making a meaningful prediction in some cases. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reported-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Tested-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Reviewed-by: Christian Loehle <christian.loehle@arm.com> Tested-by: Christian Loehle <christian.loehle@arm.com> Tested-by: Aboorva Devarajan <aboorvad@linux.ibm.com> Link: https://patch.msgid.link/2301940.iZASKD2KPV@rjwysocki.net
-rw-r--r--drivers/cpuidle/governors/menu.c32
1 files changed, 22 insertions, 10 deletions
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 680ff20e5528..48ebbde750e5 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -116,30 +116,37 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev);
*/
static unsigned int get_typical_interval(struct menu_device *data)
{
- unsigned int max, divisor, thresh = UINT_MAX;
+ s64 value, min_thresh = -1, max_thresh = UINT_MAX;
+ unsigned int max, min, divisor;
u64 avg, variance, avg_sq;
int i;
again:
/* Compute the average and variance of past intervals. */
max = 0;
+ min = UINT_MAX;
avg = 0;
variance = 0;
divisor = 0;
for (i = 0; i < INTERVALS; i++) {
- unsigned int value = data->intervals[i];
-
- /* Discard data points above or at the threshold. */
- if (value >= thresh)
+ value = data->intervals[i];
+ /*
+ * Discard the samples outside the interval between the min and
+ * max thresholds.
+ */
+ if (value <= min_thresh || value >= max_thresh)
continue;
divisor++;
avg += value;
- variance += (u64)value * value;
+ variance += value * value;
if (value > max)
max = value;
+
+ if (value < min)
+ min = value;
}
if (!max)
@@ -175,10 +182,10 @@ again:
}
/*
- * If we have outliers to the upside in our distribution, discard
- * those by setting the threshold to exclude these outliers, then
+ * If there are outliers, discard them by setting thresholds to exclude
+ * data points at a large enough distance from the average, then
* calculate the average and standard deviation again. Once we get
- * down to the bottom 3/4 of our samples, stop excluding samples.
+ * down to the last 3/4 of our samples, stop excluding samples.
*
* This can deal with workloads that have long pauses interspersed
* with sporadic activity with a bunch of short pauses.
@@ -186,7 +193,12 @@ again:
if ((divisor * 4) <= INTERVALS * 3)
return UINT_MAX;
- thresh = max;
+ /* Update the thresholds for the next round. */
+ if (avg - min > max - avg)
+ min_thresh = min;
+ else
+ max_thresh = max;
+
goto again;
}