mean and variance: More tests

Add some more tests that test conventional and weighted mean
simultaneously, and with a table of values that represents events that
we'll be using this to look for so we can verify-by-eyeball that the
output looks sane.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2023-05-25 22:22:25 -04:00
parent aab5e0972a
commit 65bc410907
3 changed files with 101 additions and 16 deletions

View File

@ -176,14 +176,12 @@ static inline s64 fast_divpow2(s64 n, u8 d)
*
* see linked pdf equation 12.
*/
static inline struct mean_and_variance
mean_and_variance_update(struct mean_and_variance s, s64 v)
static inline void
mean_and_variance_update(struct mean_and_variance *s, s64 v)
{
return (struct mean_and_variance) {
.n = s.n + 1,
.sum = s.sum + v,
.sum_squares = u128_add(s.sum_squares, u128_square(abs(v))),
};
s->n++;
s->sum += v;
s->sum_squares = u128_add(s->sum_squares, u128_square(abs(v)));
}
s64 mean_and_variance_get_mean(struct mean_and_variance s);

View File

@ -9,15 +9,15 @@ static void mean_and_variance_basic_test(struct kunit *test)
{
struct mean_and_variance s = {};
s = mean_and_variance_update(s, 2);
s = mean_and_variance_update(s, 2);
mean_and_variance_update(&s, 2);
mean_and_variance_update(&s, 2);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(s), 2);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_variance(s), 0);
KUNIT_EXPECT_EQ(test, s.n, 2);
s = mean_and_variance_update(s, 4);
s = mean_and_variance_update(s, 4);
mean_and_variance_update(&s, 4);
mean_and_variance_update(&s, 4);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(s), 3);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_variance(s), 1);
@ -33,8 +33,6 @@ static void mean_and_variance_weighted_test(struct kunit *test)
{
struct mean_and_variance_weighted s = { .weight = 2 };
s.weight = 2;
mean_and_variance_weighted_update(&s, 10);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s), 10);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s), 0);
@ -60,7 +58,6 @@ static void mean_and_variance_weighted_test(struct kunit *test)
mean_and_variance_weighted_update(&s, -30);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s), -16);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s), 72);
}
static void mean_and_variance_weighted_advanced_test(struct kunit *test)
@ -81,7 +78,93 @@ static void mean_and_variance_weighted_advanced_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s), -11);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s), 107);
}
static void do_mean_and_variance_test(struct kunit *test,
s64 initial_value,
s64 initial_n,
s64 n,
unsigned weight,
s64 *data,
s64 *mean,
s64 *stddev,
s64 *weighted_mean,
s64 *weighted_stddev)
{
struct mean_and_variance mv = {};
struct mean_and_variance_weighted vw = { .weight = weight };
for (unsigned i = 0; i < initial_n; i++) {
mean_and_variance_update(&mv, initial_value);
mean_and_variance_weighted_update(&vw, initial_value);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(mv), initial_value);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_stddev(mv), 0);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(vw), initial_value);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_stddev(vw),0);
}
for (unsigned i = 0; i < n; i++) {
mean_and_variance_update(&mv, data[i]);
mean_and_variance_weighted_update(&vw, data[i]);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(mv), mean[i]);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_stddev(mv), stddev[i]);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(vw), weighted_mean[i]);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_stddev(vw),weighted_stddev[i]);
}
KUNIT_EXPECT_EQ(test, mv.n, initial_n + n);
}
/* Test behaviour with a single outlier, then back to steady state: */
static void mean_and_variance_test_1(struct kunit *test)
{
s64 d[] = { 100, 10, 10, 10, 10, 10, 10 };
s64 mean[] = { 22, 21, 20, 19, 18, 17, 16 };
s64 stddev[] = { 32, 29, 28, 27, 26, 25, 24 };
s64 weighted_mean[] = { 32, 27, 22, 19, 17, 15, 14 };
s64 weighted_stddev[] = { 38, 35, 31, 27, 24, 21, 18 };
do_mean_and_variance_test(test, 10, 6, ARRAY_SIZE(d), 2,
d, mean, stddev, weighted_mean, weighted_stddev);
}
static void mean_and_variance_test_2(struct kunit *test)
{
s64 d[] = { 100, 10, 10, 10, 10, 10, 10 };
s64 mean[] = { 10, 10, 10, 10, 10, 10, 10 };
s64 stddev[] = { 9, 9, 9, 9, 9, 9, 9 };
s64 weighted_mean[] = { 32, 27, 22, 19, 17, 15, 14 };
s64 weighted_stddev[] = { 38, 35, 31, 27, 24, 21, 18 };
do_mean_and_variance_test(test, 10, 6, ARRAY_SIZE(d), 2,
d, mean, stddev, weighted_mean, weighted_stddev);
}
/* Test behaviour where we switch from one steady state to another: */
static void mean_and_variance_test_3(struct kunit *test)
{
s64 d[] = { 100, 100, 100, 100, 100 };
s64 mean[] = { 22, 32, 40, 46, 50 };
s64 stddev[] = { 32, 39, 42, 44, 45 };
s64 weighted_mean[] = { 32, 49, 61, 71, 78 };
s64 weighted_stddev[] = { 38, 44, 44, 41, 38 };
do_mean_and_variance_test(test, 10, 6, ARRAY_SIZE(d), 2,
d, mean, stddev, weighted_mean, weighted_stddev);
}
static void mean_and_variance_test_4(struct kunit *test)
{
s64 d[] = { 100, 100, 100, 100, 100 };
s64 mean[] = { 10, 11, 12, 13, 14 };
s64 stddev[] = { 9, 13, 15, 17, 19 };
s64 weighted_mean[] = { 32, 49, 61, 71, 78 };
s64 weighted_stddev[] = { 38, 44, 44, 41, 38 };
do_mean_and_variance_test(test, 10, 6, ARRAY_SIZE(d), 2,
d, mean, stddev, weighted_mean, weighted_stddev);
}
static void mean_and_variance_fast_divpow2(struct kunit *test)
@ -139,6 +222,10 @@ static struct kunit_case mean_and_variance_test_cases[] = {
KUNIT_CASE(mean_and_variance_basic_test),
KUNIT_CASE(mean_and_variance_weighted_test),
KUNIT_CASE(mean_and_variance_weighted_advanced_test),
KUNIT_CASE(mean_and_variance_test_1),
KUNIT_CASE(mean_and_variance_test_2),
KUNIT_CASE(mean_and_variance_test_3),
KUNIT_CASE(mean_and_variance_test_4),
{}
};

View File

@ -350,7 +350,7 @@ static inline void bch2_time_stats_update_one(struct bch2_time_stats *stats,
if (time_after64(end, start)) {
duration = end - start;
stats->duration_stats = mean_and_variance_update(stats->duration_stats, duration);
mean_and_variance_update(&stats->duration_stats, duration);
mean_and_variance_weighted_update(&stats->duration_stats_weighted, duration);
stats->max_duration = max(stats->max_duration, duration);
stats->min_duration = min(stats->min_duration, duration);
@ -359,7 +359,7 @@ static inline void bch2_time_stats_update_one(struct bch2_time_stats *stats,
if (time_after64(end, stats->last_event)) {
freq = end - stats->last_event;
stats->freq_stats = mean_and_variance_update(stats->freq_stats, freq);
mean_and_variance_update(&stats->freq_stats, freq);
mean_and_variance_weighted_update(&stats->freq_stats_weighted, freq);
stats->max_freq = max(stats->max_freq, freq);
stats->min_freq = min(stats->min_freq, freq);