rand.c 8.1 KB

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