Commit 13982929 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

cpuidle: menu: Use one loop for average and variance computations



Use the observation that one loop is sufficient to compute the average
of an array of values and their variance to eliminate one of the loops
from get_typical_interval().

While at it, make get_typical_interval() consistently use u64 as the
64-bit unsigned integer data type and rearrange some white space and the
declarations of local variables in it (to make them follow the reverse
X-mas tree pattern).

No intentional functional impact.

Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: default avatarArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
Reviewed-by: default avatarChristian Loehle <christian.loehle@arm.com>
Tested-by: default avatarChristian Loehle <christian.loehle@arm.com>
Tested-by: default avatarAboorva Devarajan <aboorvad@linux.ibm.com>
Link: https://patch.msgid.link/3339073.aeNJFYEL58@rjwysocki.net
parent d2cd195b
Loading
Loading
Loading
Loading
+28 −33
Original line number Diff line number Diff line
@@ -116,49 +116,45 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev);
 */
static unsigned int get_typical_interval(struct menu_device *data)
{
	int i, divisor;
	unsigned int max, thresh, avg;
	uint64_t sum, variance;

	thresh = INT_MAX; /* Discard outliers above this value */
	unsigned int max, divisor, thresh = INT_MAX;
	u64 avg, variance, avg_sq;
	int i;

again:

	/* First calculate the average of past intervals */
	/* Compute the average and variance of past intervals. */
	max = 0;
	sum = 0;
	avg = 0;
	variance = 0;
	divisor = 0;
	for (i = 0; i < INTERVALS; i++) {
		unsigned int value = data->intervals[i];
		if (value <= thresh) {
			sum += value;

		/* Discard data points above the threshold. */
		if (value > thresh)
			continue;

		divisor++;

		avg += value;
		variance += (u64)value * value;

		if (value > max)
			max = value;
	}
	}

	if (!max)
		return UINT_MAX;

	if (divisor == INTERVALS)
		avg = sum >> INTERVAL_SHIFT;
	else
		avg = div_u64(sum, divisor);

	/* Then try to determine variance */
	variance = 0;
	for (i = 0; i < INTERVALS; i++) {
		unsigned int value = data->intervals[i];
		if (value <= thresh) {
			int64_t diff = (int64_t)value - avg;
			variance += diff * diff;
		}
	}
	if (divisor == INTERVALS)
	if (divisor == INTERVALS) {
		avg >>= INTERVAL_SHIFT;
		variance >>= INTERVAL_SHIFT;
	else
	} else {
		do_div(avg, divisor);
		do_div(variance, divisor);
	}

	avg_sq = avg * avg;
	variance -= avg_sq;

	/*
	 * The typical interval is obtained when standard deviation is
@@ -173,11 +169,10 @@ static unsigned int get_typical_interval(struct menu_device *data)
	 * Use this result only if there is no timer to wake us up sooner.
	 */
	if (likely(variance <= U64_MAX/36)) {
		if ((((u64)avg*avg > variance*36) && (divisor * 4 >= INTERVALS * 3))
							|| variance <= 400) {
		if ((avg_sq > variance * 36 && divisor * 4 >= INTERVALS * 3) ||
		    variance <= 400)
			return avg;
	}
	}

	/*
	 * If we have outliers to the upside in our distribution, discard