]> mj.ucw.cz Git - libucw.git/blob - ucw/random-test.c
Random: Implemented fastrand_double().
[libucw.git] / ucw / random-test.c
1 /*
2  *      UCW Library -- Random numbers: Testing
3  *
4  *      (c) 2020--2022 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include <ucw/lib.h>
11 #include <ucw/random.h>
12 #include <ucw/opt.h>
13 #include <ucw/time.h>
14
15 #include <errno.h>
16 #include <float.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <time.h>
20 #include <unistd.h>
21
22 static int o_test_fast;
23
24 static double o_bench_scale = 1.0;
25 static int o_bench_only_safe;
26 static int o_bench_all;
27 static int o_bench_libs;
28 static int o_bench_legacy;
29 static int o_bench_fast;
30
31 static struct opt_section options = {
32   OPT_ITEMS {
33     OPT_HELP("Usage: random-test <options>"),
34     OPT_HELP(""),
35     OPT_HELP("Options:"),
36     OPT_HELP_OPTION,
37     OPT_BOOL(0, "test-fast", o_test_fast, 0, "\tTest fast random generator"),
38     OPT_BOOL(0, "bench-all", o_bench_all, 0, "\tBench everything"),
39     OPT_BOOL(0, "bench-libs", o_bench_libs, 0, "\tBench libc"),
40     OPT_BOOL(0, "bench-legacy", o_bench_legacy, 0, "\tBench legacy interface"),
41     OPT_BOOL(0, "bench-fast", o_bench_fast, 0, "\tBench fast random generator"),
42     OPT_DOUBLE(0, "bench-scale", o_bench_scale, OPT_REQUIRED_VALUE, "<scale>\tIncrease/decrease the length of benchmark (default: 1.0)"),
43     OPT_BOOL(0, "bench-only-safe", o_bench_only_safe, 0, "\tDon't benchmark too verbose or risky functions"),
44     OPT_END,
45   }
46 };
47
48 #define TEST(x) do { if (unlikely(!(x))) test_failed(#x, __LINE__); } while (0)
49 static NONRET void test_failed(const char *check, uint line)
50 {
51   msg(L_FATAL, "TEST: Failed at line %u: TEST(%s)", line, check);
52   abort();
53 }
54
55 static void test_fast(void)
56 {
57   struct fastrand *r = fastrand_new();
58   fastrand_gen_seed(r);
59   for (uint i = 0; i < 5000; i++)
60     {
61       u64 x, y;
62
63       TEST((x = fastrand_max(r, 33)) <= 32);
64       TEST((y = fastrand_bits(r, x)) < (1LLU << x));
65
66       TEST((x = fastrand_max(r, 65)) <= 64);
67       TEST((y = fastrand_bits_u64(r, x)) <= ((x == 64) ? UINT64_MAX : (1LLU << x) - 1));
68
69       x = fastrand_u32(r) ? : 1;
70       TEST((y = fastrand_max(r, x)) < x);
71       x = fastrand_u64(r) ? : 1;
72       TEST((y = fastrand_max_u64(r, x)) < x);
73
74       double d;
75       d = fastrand_double(r);
76       TEST(d >= 0.0 - DBL_EPSILON && d < 1.0 + DBL_EPSILON);
77     }
78   fastrand_delete(r);
79   printf("OK\n");
80 }
81
82 #define BENCH(func) BENCH_SLOW(1, func)
83 #define BENCH_SLOW(mult, func) \
84   do { \
85     timestamp_t bench_timer; \
86     init_timer(&bench_timer); \
87     for (uint bench_i = 50000000 * o_bench_scale / (mult) / 4; bench_i--; ) \
88       { func; func; func; func; } \
89     msg(L_INFO, "%7u %s", (mult) * get_timer(&bench_timer), #func); \
90   } while (0)
91
92 static void bench_libs(void)
93 {
94   uint seed = time(NULL) + getpid();
95
96   uint iso_state = seed;
97   srand(seed);
98
99   u64 bsd_buf[128 / 8];
100   struct random_data bsd_data;
101   bsd_data.state = NULL;
102   if (initstate_r(seed, (char *)bsd_buf, sizeof(bsd_buf), &bsd_data) < 0)
103     die("initstate_r(): %m");
104
105   u64 bsd_buf2[128 / 8];
106   errno = 0;
107   initstate(seed, (char *)bsd_buf2, sizeof(bsd_buf2));
108   if (errno)
109     die("initstate(): %m");
110
111   struct drand48_data svid_data;
112   if (srand48_r(seed, &svid_data) < 0)
113     die("srand48_r(): %m");
114   srand48(seed);
115
116   int32_t int32_res;
117   long int long_res;
118
119   msg(L_INFO, "Benchmarking libc ISO generator");
120   BENCH_SLOW(100, srand(seed));
121   BENCH(rand());
122   BENCH(rand_r(&iso_state));
123
124   msg(L_INFO, "Benchmarking libc BSD generator");
125   BENCH_SLOW(100, srandom(seed));
126   BENCH(random());
127   BENCH(random_r(&bsd_data, &int32_res));
128
129   msg(L_INFO, "Benchmarking libc SVID generator");
130   BENCH(srand48(seed));
131   BENCH(mrand48());
132   BENCH(mrand48_r(&svid_data, &long_res));
133 }
134
135 static void bench_legacy(void)
136 {
137   msg(L_INFO, "Benchmarking ucw legacy generator");
138   srand(time(NULL) + getpid());
139   BENCH(random_u32());
140   BENCH(random_u64());
141   BENCH(random_max(1000000));
142   BENCH(random_max_u64(1000000));
143   BENCH_SLOW(2, random_max_u64(1000000000000LL));
144   BENCH_SLOW(2, random_max_u64((1LLU << 63) - 1)); // It uses slightly suboptimal intervals, so +0 is still a bad case
145   BENCH_SLOW(4, random_max_u64((1LLU << 63) + 1));
146   BENCH_SLOW(2, random_max(1 + random_max(1000000)));
147   BENCH_SLOW(4, random_max_u64(1 + random_max_u64(1000000000000LL)));
148 }
149
150 static void bench_fast(void)
151 {
152   msg(L_INFO, "Benchmarking fast random generator");
153   struct fastrand *r = fastrand_new();
154
155   if (!o_bench_only_safe)
156     {
157       // Can produce huge log with LOCAL_DEBUG
158       uint seed = time(NULL) + getpid();
159       BENCH_SLOW(100, fastrand_gen_seed(r));
160       BENCH_SLOW(100, fastrand_set_seed(r, seed));
161     }
162
163   fastrand_gen_seed(r);
164
165   BENCH(fastrand_bits(r, 1));
166   BENCH(fastrand_bits(r, 32));
167   BENCH(fastrand_bits_u64(r, 64));
168   BENCH(fastrand_u32(r));
169   BENCH(fastrand_u64(r));
170   BENCH(fastrand_max(r, 1000000));
171   BENCH(fastrand_max(r, (1U << 30) + 0));
172   BENCH(fastrand_max(r, (1U << 30) + 1)); // Worst case for 31 bit generators (typical RAND_MAX)
173   BENCH(fastrand_max(r, (1U << 31) + 0));
174   BENCH(fastrand_max(r, (1U << 31) + 1)); // Worst case for 32 bit generators
175   BENCH(fastrand_max_u64(r, 1000000));
176   BENCH_SLOW(2, fastrand_max_u64(r, 1000000000000LL));
177   BENCH_SLOW(2, fastrand_max_u64(r, (1LLU << 63) + 0));
178   BENCH_SLOW(4, fastrand_max_u64(r, (1LLU << 63) + 1));
179   BENCH_SLOW(2, fastrand_max(r, 1 + fastrand_max(r, 1000000)));
180   BENCH_SLOW(4, fastrand_max_u64(r, 1 + fastrand_max_u64(r, 1000000000000LL)));
181   BENCH_SLOW(4, fastrand_double(r));
182
183   byte buf[1000];
184   BENCH(fastrand_mem(r, buf, 1));
185   BENCH(fastrand_mem(r, buf, 4));
186   BENCH_SLOW(10, fastrand_mem(r, buf, 100));
187   BENCH_SLOW(100, fastrand_mem(r, buf, 1000));
188
189   fastrand_delete(r);
190 }
191
192 int main(int argc UNUSED, char **argv)
193 {
194   cf_def_file = INSTALL_CONFIG_DIR "/common";
195   opt_parse(&options, argv + 1);
196
197   if (o_test_fast)
198     test_fast();
199
200   if (o_bench_all || o_bench_libs)
201     bench_libs();
202   if (o_bench_all || o_bench_legacy)
203     bench_legacy();
204   if (o_bench_all || o_bench_fast)
205     bench_fast();
206
207   return 0;
208 }