]> mj.ucw.cz Git - libucw.git/blob - ucw/random-test.c
Random: Switched flags and buf_size arguments of strongrand (slightly nicer usage).
[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 static int o_test_strong;
24
25 static double o_bench_scale = 1.0;
26 static int o_bench_only_safe;
27 static int o_bench_all;
28 static int o_bench_libs;
29 static int o_bench_legacy;
30 static int o_bench_fast;
31 static int o_bench_strong;
32
33 static struct opt_section options = {
34   OPT_ITEMS {
35     OPT_HELP("Usage: random-test <options>"),
36     OPT_HELP(""),
37     OPT_HELP("Options:"),
38     OPT_HELP_OPTION,
39     OPT_BOOL(0, "test-fast", o_test_fast, 0, "\tTest fast random generator"),
40     OPT_BOOL(0, "test-strong", o_test_strong, 0, "\tTest strong random generator"),
41     OPT_BOOL(0, "bench-all", o_bench_all, 0, "\tBench everything"),
42     OPT_BOOL(0, "bench-libs", o_bench_libs, 0, "\tBench libc"),
43     OPT_BOOL(0, "bench-legacy", o_bench_legacy, 0, "\tBench legacy interface"),
44     OPT_BOOL(0, "bench-fast", o_bench_fast, 0, "\tBench fast random generator"),
45     OPT_BOOL(0, "bench-strong", o_bench_strong, 0, "\tBench strong random generator"),
46     OPT_DOUBLE(0, "bench-scale", o_bench_scale, OPT_REQUIRED_VALUE, "<scale>\tIncrease/decrease the length of benchmark (default: 1.0)"),
47     OPT_BOOL(0, "bench-only-safe", o_bench_only_safe, 0, "\tDon't benchmark too verbose or risky functions"),
48     OPT_END,
49   }
50 };
51
52 #define TEST(x) do { if (unlikely(!(x))) test_failed(#x, __LINE__); } while (0)
53 static NONRET void test_failed(const char *check, uint line)
54 {
55   msg(L_FATAL, "TEST: Failed at line %u: TEST(%s)", line, check);
56   abort();
57 }
58
59 static void test_fast(void)
60 {
61   struct fastrand *r = fastrand_new();
62   fastrand_gen_seed(r);
63   for (uint i = 0; i < 5000; i++)
64     {
65       u64 x, y;
66
67       TEST((x = fastrand_max(r, 33)) <= 32);
68       TEST((y = fastrand_bits(r, x)) < (1LLU << x));
69
70       TEST((x = fastrand_max(r, 65)) <= 64);
71       TEST((y = fastrand_bits_u64(r, x)) <= ((x == 64) ? UINT64_MAX : (1LLU << x) - 1));
72
73       x = fastrand_u32(r) ? : 1;
74       TEST((y = fastrand_max(r, x)) < x);
75       x = fastrand_u64(r) ? : 1;
76       TEST((y = fastrand_max_u64(r, x)) < x);
77
78       double d;
79       d = fastrand_double(r);
80       TEST(d >= 0.0 - DBL_EPSILON && d < 1.0 + DBL_EPSILON);
81     }
82   fastrand_delete(r);
83   printf("OK\n");
84 }
85
86 static void test_strong(void)
87 {
88   byte buf[100];
89   for (uint j = 0; j < 2; j++)
90     {
91       struct strongrand *r = strongrand_std_open(STRONGRAND_STD_URANDOM, (j == 0) ? 0 : 128);
92       for (uint i = 1; i <= 100; i++)
93         {
94           strongrand_mem(r, buf, i);
95           if (i % 10 == 0)
96             strongrand_reset(r);
97         }
98       strongrand_close(r);
99     }
100   printf("OK\n");
101 }
102
103 #define BENCH(func) BENCH_SLOW(1, func)
104 #define BENCH_SLOW(mult, func) \
105   do { \
106     timestamp_t bench_timer; \
107     init_timer(&bench_timer); \
108     for (uint bench_i = 50000000 * o_bench_scale / (mult) / 4; bench_i--; ) \
109       { func; func; func; func; } \
110     msg(L_INFO, "%7u %s", (mult) * get_timer(&bench_timer), #func); \
111   } while (0)
112
113 static void bench_libs(void)
114 {
115   uint seed = time(NULL) + getpid();
116
117   uint iso_state = seed;
118   srand(seed);
119
120   u64 bsd_buf[128 / 8];
121   struct random_data bsd_data;
122   bsd_data.state = NULL;
123   if (initstate_r(seed, (char *)bsd_buf, sizeof(bsd_buf), &bsd_data) < 0)
124     die("initstate_r(): %m");
125
126   u64 bsd_buf2[128 / 8];
127   errno = 0;
128   initstate(seed, (char *)bsd_buf2, sizeof(bsd_buf2));
129   if (errno)
130     die("initstate(): %m");
131
132   struct drand48_data svid_data;
133   if (srand48_r(seed, &svid_data) < 0)
134     die("srand48_r(): %m");
135   srand48(seed);
136
137   int32_t int32_res;
138   long int long_res;
139
140   msg(L_INFO, "Benchmarking libc ISO generator");
141   BENCH_SLOW(100, srand(seed));
142   BENCH(rand());
143   BENCH(rand_r(&iso_state));
144
145   msg(L_INFO, "Benchmarking libc BSD generator");
146   BENCH_SLOW(100, srandom(seed));
147   BENCH(random());
148   BENCH(random_r(&bsd_data, &int32_res));
149
150   msg(L_INFO, "Benchmarking libc SVID generator");
151   BENCH(srand48(seed));
152   BENCH(mrand48());
153   BENCH(mrand48_r(&svid_data, &long_res));
154 }
155
156 static void bench_legacy(void)
157 {
158   msg(L_INFO, "Benchmarking ucw legacy generator");
159   srand(time(NULL) + getpid());
160   BENCH(random_u32());
161   BENCH(random_u64());
162   BENCH(random_max(1000000));
163   BENCH(random_max_u64(1000000));
164   BENCH_SLOW(2, random_max_u64(1000000000000LL));
165   BENCH_SLOW(2, random_max_u64((1LLU << 63) - 1)); // It uses slightly suboptimal intervals, so +0 is still a bad case
166   BENCH_SLOW(4, random_max_u64((1LLU << 63) + 1));
167   BENCH_SLOW(2, random_max(1 + random_max(1000000)));
168   BENCH_SLOW(4, random_max_u64(1 + random_max_u64(1000000000000LL)));
169 }
170
171 static void bench_fast(void)
172 {
173   msg(L_INFO, "Benchmarking fast random generator");
174   struct fastrand *r = fastrand_new();
175
176   if (!o_bench_only_safe)
177     {
178       // Can produce huge log with LOCAL_DEBUG
179       uint seed = time(NULL) + getpid();
180       BENCH_SLOW(100, fastrand_gen_seed(r));
181       BENCH_SLOW(100, fastrand_set_seed(r, seed));
182     }
183
184   fastrand_gen_seed(r);
185
186   BENCH(fastrand_bits(r, 1));
187   BENCH(fastrand_bits(r, 32));
188   BENCH(fastrand_bits_u64(r, 64));
189   BENCH(fastrand_u32(r));
190   BENCH(fastrand_u64(r));
191   BENCH(fastrand_max(r, 1000000));
192   BENCH(fastrand_max(r, (1U << 30) + 0));
193   BENCH(fastrand_max(r, (1U << 30) + 1)); // Worst case for 31 bit generators (typical RAND_MAX)
194   BENCH(fastrand_max(r, (1U << 31) + 0));
195   BENCH(fastrand_max(r, (1U << 31) + 1)); // Worst case for 32 bit generators
196   BENCH(fastrand_max_u64(r, 1000000));
197   BENCH_SLOW(2, fastrand_max_u64(r, 1000000000000LL));
198   BENCH_SLOW(2, fastrand_max_u64(r, (1LLU << 63) + 0));
199   BENCH_SLOW(4, fastrand_max_u64(r, (1LLU << 63) + 1));
200   BENCH_SLOW(2, fastrand_max(r, 1 + fastrand_max(r, 1000000)));
201   BENCH_SLOW(4, fastrand_max_u64(r, 1 + fastrand_max_u64(r, 1000000000000LL)));
202   BENCH_SLOW(4, fastrand_double(r));
203
204   byte buf[1000];
205   BENCH(fastrand_mem(r, buf, 1));
206   BENCH(fastrand_mem(r, buf, 4));
207   BENCH_SLOW(10, fastrand_mem(r, buf, 100));
208   BENCH_SLOW(100, fastrand_mem(r, buf, 1000));
209
210   fastrand_delete(r);
211 }
212
213 static void bench_strong(void)
214 {
215   byte buf[100];
216   struct strongrand *r;
217
218   msg(L_INFO, "Benchmarking unbuffered strong random generator");
219   r = strongrand_std_open(STRONGRAND_STD_URANDOM, 0);
220   BENCH_SLOW(50, strongrand_mem(r, buf, 1));
221   BENCH_SLOW(200, strongrand_mem(r, buf, 100));
222   strongrand_close(r);
223
224   msg(L_INFO, "Benchmarking buffered strong random generator");
225   r = strongrand_std_open(STRONGRAND_STD_URANDOM, 128);
226   BENCH_SLOW(50, strongrand_mem(r, buf, 1));
227   BENCH_SLOW(200, strongrand_mem(r, buf, 100));
228   strongrand_close(r);
229
230   msg(L_INFO, "Benchmarking buffered strong random generator with autoreset");
231   r = strongrand_std_open(STRONGRAND_STD_URANDOM | STRONGRAND_STD_AUTO_RESET, 128);
232   BENCH_SLOW(50, strongrand_mem(r, buf, 1));
233   BENCH_SLOW(200, strongrand_mem(r, buf, 100));
234   strongrand_close(r);
235 }
236
237 int main(int argc UNUSED, char **argv)
238 {
239   cf_def_file = INSTALL_CONFIG_DIR "/common";
240   opt_parse(&options, argv + 1);
241
242   if (o_test_fast)
243     test_fast();
244   if (o_test_strong)
245     test_strong();
246
247   if (o_bench_all || o_bench_libs)
248     bench_libs();
249   if (o_bench_all || o_bench_legacy)
250     bench_legacy();
251   if (o_bench_all || o_bench_fast)
252     bench_fast();
253   if (o_bench_all || o_bench_strong)
254     bench_strong();
255
256   return 0;
257 }