1 1.1 christos #include "test/jemalloc_test.h" 2 1.1 christos 3 1.1 christos /******************************************************************************/ 4 1.1 christos 5 1.1 christos /* 6 1.1 christos * General purpose tool for examining random number distributions. 7 1.1 christos * 8 1.1 christos * Input - 9 1.1 christos * (a) a random number generator, and 10 1.1 christos * (b) the buckets: 11 1.1 christos * (1) number of buckets, 12 1.1 christos * (2) width of each bucket, in log scale, 13 1.1 christos * (3) expected mean and stddev of the count of random numbers in each 14 1.1 christos * bucket, and 15 1.1 christos * (c) number of iterations to invoke the generator. 16 1.1 christos * 17 1.1 christos * The program generates the specified amount of random numbers, and assess how 18 1.1 christos * well they conform to the expectations: for each bucket, output - 19 1.1 christos * (a) the (given) expected mean and stddev, 20 1.1 christos * (b) the actual count and any interesting level of deviation: 21 1.1 christos * (1) ~68% buckets should show no interesting deviation, meaning a 22 1.1 christos * deviation less than stddev from the expectation; 23 1.1 christos * (2) ~27% buckets should show '+' / '-', meaning a deviation in the range 24 1.1 christos * of [stddev, 2 * stddev) from the expectation; 25 1.1 christos * (3) ~4% buckets should show '++' / '--', meaning a deviation in the 26 1.1 christos * range of [2 * stddev, 3 * stddev) from the expectation; and 27 1.1 christos * (4) less than 0.3% buckets should show more than two '+'s / '-'s. 28 1.1 christos * 29 1.1 christos * Technical remarks: 30 1.1 christos * (a) The generator is expected to output uint64_t numbers, so you might need 31 1.1 christos * to define a wrapper. 32 1.1 christos * (b) The buckets must be of equal width and the lowest bucket starts at 33 1.1 christos * [0, 2^lg_bucket_width - 1). 34 1.1 christos * (c) Any generated number >= n_bucket * 2^lg_bucket_width will be counted 35 1.1 christos * towards the last bucket; the expected mean and stddev provided should 36 1.1 christos * also reflect that. 37 1.1 christos * (d) The number of iterations is advised to be determined so that the bucket 38 1.1 christos * with the minimal expected proportion gets a sufficient count. 39 1.1 christos */ 40 1.1 christos 41 1.1 christos static void 42 1.1 christos fill(size_t a[], const size_t n, const size_t k) { 43 1.1 christos for (size_t i = 0; i < n; ++i) { 44 1.1 christos a[i] = k; 45 1.1 christos } 46 1.1 christos } 47 1.1 christos 48 1.1 christos static void 49 1.1 christos collect_buckets(uint64_t (*gen)(void *), void *opaque, size_t buckets[], 50 1.1 christos const size_t n_bucket, const size_t lg_bucket_width, const size_t n_iter) { 51 1.1 christos for (size_t i = 0; i < n_iter; ++i) { 52 1.1 christos uint64_t num = gen(opaque); 53 1.1 christos uint64_t bucket_id = num >> lg_bucket_width; 54 1.1 christos if (bucket_id >= n_bucket) { 55 1.1 christos bucket_id = n_bucket - 1; 56 1.1 christos } 57 1.1 christos ++buckets[bucket_id]; 58 1.1 christos } 59 1.1 christos } 60 1.1 christos 61 1.1 christos static void 62 1.1 christos print_buckets(const size_t buckets[], const size_t means[], 63 1.1 christos const size_t stddevs[], const size_t n_bucket) { 64 1.1 christos for (size_t i = 0; i < n_bucket; ++i) { 65 1.1 christos malloc_printf("%zu:\tmean = %zu,\tstddev = %zu,\tbucket = %zu", 66 1.1 christos i, means[i], stddevs[i], buckets[i]); 67 1.1 christos 68 1.1 christos /* Make sure there's no overflow. */ 69 1.1 christos assert(buckets[i] + stddevs[i] >= stddevs[i]); 70 1.1 christos assert(means[i] + stddevs[i] >= stddevs[i]); 71 1.1 christos 72 1.1 christos if (buckets[i] + stddevs[i] <= means[i]) { 73 1.1 christos malloc_write(" "); 74 1.1 christos for (size_t t = means[i] - buckets[i]; t >= stddevs[i]; 75 1.1 christos t -= stddevs[i]) { 76 1.1 christos malloc_write("-"); 77 1.1 christos } 78 1.1 christos } else if (buckets[i] >= means[i] + stddevs[i]) { 79 1.1 christos malloc_write(" "); 80 1.1 christos for (size_t t = buckets[i] - means[i]; t >= stddevs[i]; 81 1.1 christos t -= stddevs[i]) { 82 1.1 christos malloc_write("+"); 83 1.1 christos } 84 1.1 christos } 85 1.1 christos malloc_write("\n"); 86 1.1 christos } 87 1.1 christos } 88 1.1 christos 89 1.1 christos static void 90 1.1 christos bucket_analysis(uint64_t (*gen)(void *), void *opaque, size_t buckets[], 91 1.1 christos const size_t means[], const size_t stddevs[], const size_t n_bucket, 92 1.1 christos const size_t lg_bucket_width, const size_t n_iter) { 93 1.1 christos for (size_t i = 1; i <= 3; ++i) { 94 1.1 christos malloc_printf("round %zu\n", i); 95 1.1 christos fill(buckets, n_bucket, 0); 96 1.1 christos collect_buckets(gen, opaque, buckets, n_bucket, 97 1.1 christos lg_bucket_width, n_iter); 98 1.1 christos print_buckets(buckets, means, stddevs, n_bucket); 99 1.1 christos } 100 1.1 christos } 101 1.1 christos 102 1.1 christos /* (Recommended) minimal bucket mean. */ 103 1.1 christos #define MIN_BUCKET_MEAN 10000 104 1.1 christos 105 1.1 christos /******************************************************************************/ 106 1.1 christos 107 1.1 christos /* Uniform random number generator. */ 108 1.1 christos 109 1.1 christos typedef struct uniform_gen_arg_s uniform_gen_arg_t; 110 1.1 christos struct uniform_gen_arg_s { 111 1.1 christos uint64_t state; 112 1.1 christos const unsigned lg_range; 113 1.1 christos }; 114 1.1 christos 115 1.1 christos static uint64_t 116 1.1 christos uniform_gen(void *opaque) { 117 1.1 christos uniform_gen_arg_t *arg = (uniform_gen_arg_t *)opaque; 118 1.1 christos return prng_lg_range_u64(&arg->state, arg->lg_range); 119 1.1 christos } 120 1.1 christos 121 1.1 christos TEST_BEGIN(test_uniform) { 122 1.1 christos #define LG_N_BUCKET 5 123 1.1 christos #define N_BUCKET (1 << LG_N_BUCKET) 124 1.1 christos 125 1.1 christos #define QUOTIENT_CEIL(n, d) (((n) - 1) / (d) + 1) 126 1.1 christos 127 1.1 christos const unsigned lg_range_test = 25; 128 1.1 christos 129 1.1 christos /* 130 1.1 christos * Mathematical tricks to guarantee that both mean and stddev are 131 1.1 christos * integers, and that the minimal bucket mean is at least 132 1.1 christos * MIN_BUCKET_MEAN. 133 1.1 christos */ 134 1.1 christos const size_t q = 1 << QUOTIENT_CEIL(LG_CEIL(QUOTIENT_CEIL( 135 1.1 christos MIN_BUCKET_MEAN, N_BUCKET * (N_BUCKET - 1))), 2); 136 1.1 christos const size_t stddev = (N_BUCKET - 1) * q; 137 1.1 christos const size_t mean = N_BUCKET * stddev * q; 138 1.1 christos const size_t n_iter = N_BUCKET * mean; 139 1.1 christos 140 1.1 christos size_t means[N_BUCKET]; 141 1.1 christos fill(means, N_BUCKET, mean); 142 1.1 christos size_t stddevs[N_BUCKET]; 143 1.1 christos fill(stddevs, N_BUCKET, stddev); 144 1.1 christos 145 1.1 christos uniform_gen_arg_t arg = {(uint64_t)(uintptr_t)&lg_range_test, 146 1.1 christos lg_range_test}; 147 1.1 christos size_t buckets[N_BUCKET]; 148 1.1 christos assert_zu_ge(lg_range_test, LG_N_BUCKET, ""); 149 1.1 christos const size_t lg_bucket_width = lg_range_test - LG_N_BUCKET; 150 1.1 christos 151 1.1 christos bucket_analysis(uniform_gen, &arg, buckets, means, stddevs, 152 1.1 christos N_BUCKET, lg_bucket_width, n_iter); 153 1.1 christos 154 1.1 christos #undef LG_N_BUCKET 155 1.1 christos #undef N_BUCKET 156 1.1 christos #undef QUOTIENT_CEIL 157 1.1 christos } 158 1.1 christos TEST_END 159 1.1 christos 160 1.1 christos /******************************************************************************/ 161 1.1 christos 162 1.1 christos /* Geometric random number generator; compiled only when prof is on. */ 163 1.1 christos 164 1.1 christos #ifdef JEMALLOC_PROF 165 1.1 christos 166 1.1 christos /* 167 1.1 christos * Fills geometric proportions and returns the minimal proportion. See 168 1.1 christos * comments in test_prof_sample for explanations for n_divide. 169 1.1 christos */ 170 1.1 christos static double 171 1.1 christos fill_geometric_proportions(double proportions[], const size_t n_bucket, 172 1.1 christos const size_t n_divide) { 173 1.1 christos assert(n_bucket > 0); 174 1.1 christos assert(n_divide > 0); 175 1.1 christos double x = 1.; 176 1.1 christos for (size_t i = 0; i < n_bucket; ++i) { 177 1.1 christos if (i == n_bucket - 1) { 178 1.1 christos proportions[i] = x; 179 1.1 christos } else { 180 1.1 christos double y = x * exp(-1. / n_divide); 181 1.1 christos proportions[i] = x - y; 182 1.1 christos x = y; 183 1.1 christos } 184 1.1 christos } 185 1.1 christos /* 186 1.1 christos * The minimal proportion is the smaller one of the last two 187 1.1 christos * proportions for geometric distribution. 188 1.1 christos */ 189 1.1 christos double min_proportion = proportions[n_bucket - 1]; 190 1.1 christos if (n_bucket >= 2 && proportions[n_bucket - 2] < min_proportion) { 191 1.1 christos min_proportion = proportions[n_bucket - 2]; 192 1.1 christos } 193 1.1 christos return min_proportion; 194 1.1 christos } 195 1.1 christos 196 1.1 christos static size_t 197 1.1 christos round_to_nearest(const double x) { 198 1.1 christos return (size_t)(x + .5); 199 1.1 christos } 200 1.1 christos 201 1.1 christos static void 202 1.1 christos fill_references(size_t means[], size_t stddevs[], const double proportions[], 203 1.1 christos const size_t n_bucket, const size_t n_iter) { 204 1.1 christos for (size_t i = 0; i < n_bucket; ++i) { 205 1.1 christos double x = n_iter * proportions[i]; 206 1.1 christos means[i] = round_to_nearest(x); 207 1.1 christos stddevs[i] = round_to_nearest(sqrt(x * (1. - proportions[i]))); 208 1.1 christos } 209 1.1 christos } 210 1.1 christos 211 1.1 christos static uint64_t 212 1.1 christos prof_sample_gen(void *opaque) { 213 1.1 christos return prof_sample_new_event_wait((tsd_t *)opaque) - 1; 214 1.1 christos } 215 1.1 christos 216 1.1 christos #endif /* JEMALLOC_PROF */ 217 1.1 christos 218 1.1 christos TEST_BEGIN(test_prof_sample) { 219 1.1 christos test_skip_if(!config_prof); 220 1.1 christos #ifdef JEMALLOC_PROF 221 1.1 christos 222 1.1 christos /* Number of divisions within [0, mean). */ 223 1.1 christos #define LG_N_DIVIDE 3 224 1.1 christos #define N_DIVIDE (1 << LG_N_DIVIDE) 225 1.1 christos 226 1.1 christos /* Coverage of buckets in terms of multiples of mean. */ 227 1.1 christos #define LG_N_MULTIPLY 2 228 1.1 christos #define N_GEO_BUCKET (N_DIVIDE << LG_N_MULTIPLY) 229 1.1 christos 230 1.1 christos test_skip_if(!opt_prof); 231 1.1 christos 232 1.1 christos size_t lg_prof_sample_test = 25; 233 1.1 christos 234 1.1 christos size_t lg_prof_sample_orig = lg_prof_sample; 235 1.1 christos assert_d_eq(mallctl("prof.reset", NULL, NULL, &lg_prof_sample_test, 236 1.1 christos sizeof(size_t)), 0, ""); 237 1.1 christos malloc_printf("lg_prof_sample = %zu\n", lg_prof_sample_test); 238 1.1 christos 239 1.1 christos double proportions[N_GEO_BUCKET + 1]; 240 1.1 christos const double min_proportion = fill_geometric_proportions(proportions, 241 1.1 christos N_GEO_BUCKET + 1, N_DIVIDE); 242 1.1 christos const size_t n_iter = round_to_nearest(MIN_BUCKET_MEAN / 243 1.1 christos min_proportion); 244 1.1 christos size_t means[N_GEO_BUCKET + 1]; 245 1.1 christos size_t stddevs[N_GEO_BUCKET + 1]; 246 1.1 christos fill_references(means, stddevs, proportions, N_GEO_BUCKET + 1, n_iter); 247 1.1 christos 248 1.1 christos tsd_t *tsd = tsd_fetch(); 249 1.1 christos assert_ptr_not_null(tsd, ""); 250 1.1 christos size_t buckets[N_GEO_BUCKET + 1]; 251 1.1 christos assert_zu_ge(lg_prof_sample, LG_N_DIVIDE, ""); 252 1.1 christos const size_t lg_bucket_width = lg_prof_sample - LG_N_DIVIDE; 253 1.1 christos 254 1.1 christos bucket_analysis(prof_sample_gen, tsd, buckets, means, stddevs, 255 1.1 christos N_GEO_BUCKET + 1, lg_bucket_width, n_iter); 256 1.1 christos 257 1.1 christos assert_d_eq(mallctl("prof.reset", NULL, NULL, &lg_prof_sample_orig, 258 1.1 christos sizeof(size_t)), 0, ""); 259 1.1 christos 260 1.1 christos #undef LG_N_DIVIDE 261 1.1 christos #undef N_DIVIDE 262 1.1 christos #undef LG_N_MULTIPLY 263 1.1 christos #undef N_GEO_BUCKET 264 1.1 christos 265 1.1 christos #endif /* JEMALLOC_PROF */ 266 1.1 christos } 267 1.1 christos TEST_END 268 1.1 christos 269 1.1 christos /******************************************************************************/ 270 1.1 christos 271 1.1 christos int 272 1.1 christos main(void) { 273 1.1 christos return test_no_reentrancy( 274 1.1 christos test_uniform, 275 1.1 christos test_prof_sample); 276 1.1 christos } 277