Skip to content

Commit 83077d6

Browse files
committed
Improve dudect test with cropped time analysis
This commit introduces several improvements to the dudect test, including cropped time analysis and performance optimizations. - Remove outliers caused by context switches, interrupts, or system activity using a percentile-based threshold. - Store measurements in multiple t-test contexts to track t-tests in different percentile thresholds. - Fix integer overflow and improve the efficiency in the 'cmp()' function by using a branch-free comparison '(a > b) - (a < b)'. - Optimize the calculation of 'max_t' and 'max_tau' by deferring computations until necessary, reducing unnecessary calculation when measurements are insufficient. Change-Id: Icb910a8b9d93305da8e478e7e79cd25891c9e72e
1 parent 5223a1d commit 83077d6

File tree

1 file changed

+79
-9
lines changed

1 file changed

+79
-9
lines changed

dudect/fixture.c

+79-9
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
#define ENOUGH_MEASURE 10000
4444
#define TEST_TRIES 10
4545

46-
static t_context_t *t;
46+
/* Number of percentiles to calculate */
47+
#define NUM_PERCENTILES (100)
48+
#define DUDECT_TESTS (NUM_PERCENTILES + 1)
49+
50+
static t_context_t *ctxs[DUDECT_TESTS];
4751

4852
/* threshold values for Welch's t-test */
4953
enum {
@@ -56,6 +60,37 @@ static void __attribute__((noreturn)) die(void)
5660
exit(111);
5761
}
5862

63+
static int64_t percentile(const int64_t *a_sorted, double which, size_t size)
64+
{
65+
assert(which >= 0 && which <= 1.0);
66+
size_t pos = (size_t) (which * size);
67+
return a_sorted[pos];
68+
}
69+
70+
/* leverages the fact that comparison expressions return 1 or 0. */
71+
static int cmp(const void *aa, const void *bb)
72+
{
73+
int64_t a = *(const int64_t *) aa, b = *(const int64_t *) bb;
74+
return (a > b) - (a < b);
75+
}
76+
77+
/* This function is used to set different thresholds for cropping measurements.
78+
* To filter out slow measurements, we keep only the fastest ones by a
79+
* complementary exponential decay scale as thresholds for cropping
80+
* measurements: threshold(x) = 1 - 0.5^(10 * x / N_MEASURES), where x is the
81+
* counter of the measurement.
82+
*/
83+
static void prepare_percentiles(int64_t *exec_times, int64_t *percentiles)
84+
{
85+
qsort(exec_times, N_MEASURES, sizeof(int64_t), cmp);
86+
87+
for (size_t i = 0; i < NUM_PERCENTILES; i++) {
88+
percentiles[i] = percentile(
89+
exec_times, 1 - (pow(0.5, 10 * (double) (i + 1) / NUM_PERCENTILES)),
90+
N_MEASURES);
91+
}
92+
}
93+
5994
static void differentiate(int64_t *exec_times,
6095
const int64_t *before_ticks,
6196
const int64_t *after_ticks)
@@ -64,7 +99,9 @@ static void differentiate(int64_t *exec_times,
6499
exec_times[i] = after_ticks[i] - before_ticks[i];
65100
}
66101

67-
static void update_statistics(const int64_t *exec_times, uint8_t *classes)
102+
static void update_statistics(const int64_t *exec_times,
103+
uint8_t *classes,
104+
int64_t *percentiles)
68105
{
69106
for (size_t i = 0; i < N_MEASURES; i++) {
70107
int64_t difference = exec_times[i];
@@ -73,15 +110,36 @@ static void update_statistics(const int64_t *exec_times, uint8_t *classes)
73110
continue;
74111

75112
/* do a t-test on the execution time */
76-
t_push(t, difference, classes[i]);
113+
t_push(ctxs[0], difference, classes[i]);
114+
115+
/* t-test on cropped execution times, for several cropping thresholds.
116+
*/
117+
for (size_t j = 0; j < NUM_PERCENTILES; j++) {
118+
if (difference < percentiles[j]) {
119+
t_push(ctxs[j + 1], difference, classes[i]);
120+
}
121+
}
77122
}
78123
}
79124

125+
static t_context_t *max_test()
126+
{
127+
size_t max_idx = 0;
128+
double max_t = 0.0f;
129+
for (size_t i = 0; i < NUM_PERCENTILES + 1; i++) {
130+
double t = fabs(t_compute(ctxs[i]));
131+
if (t > max_t) {
132+
max_t = t;
133+
max_idx = i;
134+
}
135+
}
136+
return ctxs[max_idx];
137+
}
138+
80139
static bool report(void)
81140
{
82-
double max_t = fabs(t_compute(t));
141+
t_context_t *t = max_test();
83142
double number_traces_max_t = t->n[0] + t->n[1];
84-
double max_tau = max_t / sqrt(number_traces_max_t);
85143

86144
printf("\033[A\033[2K");
87145
printf("measure: %7.2lf M, ", (number_traces_max_t / 1e6));
@@ -91,6 +149,9 @@ static bool report(void)
91149
return false;
92150
}
93151

152+
double max_t = fabs(t_compute(t));
153+
double max_tau = max_t / sqrt(number_traces_max_t);
154+
94155
/* max_t: the t statistic value
95156
* max_tau: a t value normalized by sqrt(number of measurements).
96157
* this way we can compare max_tau taken with different
@@ -123,6 +184,7 @@ static bool doit(int mode)
123184
int64_t *exec_times = calloc(N_MEASURES, sizeof(int64_t));
124185
uint8_t *classes = calloc(N_MEASURES, sizeof(uint8_t));
125186
uint8_t *input_data = calloc(N_MEASURES * CHUNK_SIZE, sizeof(uint8_t));
187+
int64_t *percentiles = calloc(NUM_PERCENTILES, sizeof(int64_t));
126188

127189
if (!before_ticks || !after_ticks || !exec_times || !classes ||
128190
!input_data) {
@@ -133,28 +195,32 @@ static bool doit(int mode)
133195

134196
bool ret = measure(before_ticks, after_ticks, input_data, mode);
135197
differentiate(exec_times, before_ticks, after_ticks);
136-
update_statistics(exec_times, classes);
198+
prepare_percentiles(exec_times, percentiles);
199+
update_statistics(exec_times, classes, percentiles);
137200
ret &= report();
138201

139202
free(before_ticks);
140203
free(after_ticks);
141204
free(exec_times);
142205
free(classes);
143206
free(input_data);
207+
free(percentiles);
144208

145209
return ret;
146210
}
147211

148212
static void init_once(void)
149213
{
150214
init_dut();
151-
t_init(t);
215+
for (size_t i = 0; i < DUDECT_TESTS; i++) {
216+
ctxs[i] = malloc(sizeof(t_context_t));
217+
t_init(ctxs[i]);
218+
}
152219
}
153220

154221
static bool test_const(char *text, int mode)
155222
{
156223
bool result = false;
157-
t = malloc(sizeof(t_context_t));
158224

159225
for (int cnt = 0; cnt < TEST_TRIES; ++cnt) {
160226
printf("Testing %s...(%d/%d)\n\n", text, cnt, TEST_TRIES);
@@ -166,7 +232,11 @@ static bool test_const(char *text, int mode)
166232
if (result)
167233
break;
168234
}
169-
free(t);
235+
236+
for (size_t i = 0; i < DUDECT_TESTS; i++) {
237+
free(ctxs[i]);
238+
}
239+
170240
return result;
171241
}
172242

0 commit comments

Comments
 (0)