Home | History | Annotate | Line # | Download | only in analyze
      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