c11 safe string handling support
[vpp.git] / src / vppinfra / test_cuckoo_bihash.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 __thread int thread_id = 0;
17
18 #include <vppinfra/time.h>
19 #include <vppinfra/cache.h>
20 #include <vppinfra/error.h>
21 #include <vppinfra/heap.h>
22 #include <vppinfra/format.h>
23 #include <vppinfra/pool.h>
24 #include <vppinfra/error.h>
25 #include <vppinfra/hash.h>
26 #include <vppinfra/cache.h>
27
28 #define os_get_cpu_number() (thread_id)
29
30 #include <vppinfra/cuckoo_8_8.h>
31 #include <vppinfra/cuckoo_template.h>
32 #include <vppinfra/cuckoo_template.c>
33
34 #include <vppinfra/bihash_8_8.h>
35 #include <vppinfra/bihash_template.h>
36 #include <vppinfra/bihash_template.c>
37
38 #include <pthread.h>
39
40 #define MAX_THREADS 255
41
42 typedef struct
43 {
44   void *tm;
45   int thread_idx;
46   u64 nlookups;
47 } thread_data_t;
48
49 typedef struct
50 {
51   u64 deadline;
52   u64 seed;
53   u32 nbuckets;
54   u32 nitems;
55   u32 runtime;
56   int verbose;
57   int non_random_keys;
58   int nthreads;
59   int search_iter;
60   uword *key_hash;
61   u64 *keys;
62     CVT (clib_cuckoo) ch;
63     BVT (clib_bihash) bh;
64   clib_time_t clib_time;
65   u64 *key_add_del_sequence;
66   u8 *key_op_sequence;
67   u64 *key_search_sequence[MAX_THREADS];
68   unformat_input_t *input;
69   u64 nadds;
70   u64 ndels;
71   pthread_t bwriter_thread;
72   pthread_t breader_threads[MAX_THREADS];
73   pthread_t cwriter_thread;
74   pthread_t creader_threads[MAX_THREADS];
75   thread_data_t wthread_data;
76   thread_data_t rthread_data[MAX_THREADS];
77 } test_main_t;
78
79 test_main_t test_main;
80
81 uword
82 vl (void *v)
83 {
84   return vec_len (v);
85 }
86
87 #define w_thread(x, guts)                                               \
88   void *x##writer_thread (void *v)                                      \
89   {                                                                     \
90     test_main_t *tm = v;                                                \
91     uword counter = 0;                                                  \
92     u64 nadds = 0;                                                      \
93     u64 ndels = 0;                                                      \
94     u64 deadline = tm->deadline;                                        \
95     do                                                                  \
96       {                                                                 \
97         for (counter = 0; counter < vec_len (tm->key_add_del_sequence); \
98              ++counter)                                                 \
99           {                                                             \
100             u64 idx = tm->key_add_del_sequence[counter];                \
101             u8 op = tm->key_op_sequence[counter];                       \
102             if (op)                                                     \
103               {                                                         \
104                 ++nadds;                                                \
105               }                                                         \
106             else                                                        \
107               {                                                         \
108                 ++ndels;                                                \
109               }                                                         \
110             guts;                                                       \
111             if (clib_cpu_time_now () > deadline)                        \
112               {                                                         \
113                 break;                                                  \
114               }                                                         \
115           }                                                             \
116       }                                                                 \
117     while (clib_cpu_time_now () < deadline);                            \
118     tm->nadds = nadds;                                                  \
119     tm->ndels = ndels;                                                  \
120     return NULL;                                                        \
121   }
122
123 /* *INDENT-OFF* */
124 w_thread (b, {
125   BVT (clib_bihash_kv) kv;
126   kv.key = tm->keys[idx];
127   kv.value = *hash_get (tm->key_hash, kv.key);
128   BV (clib_bihash_add_del) (&tm->bh, &kv, op);
129 });
130 /* *INDENT-ON* */
131
132 /* *INDENT-OFF* */
133 w_thread (c, {
134   CVT (clib_cuckoo_kv) kv;
135   kv.key = tm->keys[idx];
136   kv.value = *hash_get (tm->key_hash, kv.key);
137   CV (clib_cuckoo_add_del) (&tm->ch, &kv, op);
138 });
139 /* *INDENT-ON* */
140
141 #define r_thread(x, guts)                                      \
142   void *x##reader_thread (void *v)                             \
143   {                                                            \
144     thread_data_t *data = v;                                   \
145     thread_id = data->thread_idx;                              \
146     test_main_t *tm = data->tm;                                \
147     uword thread_idx = data->thread_idx;                       \
148     u64 *idx;                                                  \
149     uword nlookups = 0;                                        \
150     u64 deadline = tm->deadline;                               \
151     do                                                         \
152       {                                                        \
153         vec_foreach (idx, tm->key_search_sequence[thread_idx]) \
154         {                                                      \
155           guts;                                                \
156           ++nlookups;                                          \
157           if (clib_cpu_time_now () > deadline)                 \
158             {                                                  \
159               break;                                           \
160             }                                                  \
161         }                                                      \
162       }                                                        \
163     while (clib_cpu_time_now () < deadline);                   \
164     data->nlookups = nlookups;                                 \
165     return NULL;                                               \
166   }
167
168 /* *INDENT-OFF* */
169 r_thread (c, {
170   CVT (clib_cuckoo_kv) kv;
171   kv.key = tm->keys[*idx];
172   kv.value = *hash_get (tm->key_hash, kv.key);
173   CV (clib_cuckoo_search) (&tm->ch, &kv, &kv);
174 });
175 /* *INDENT-ON* */
176
177 /* *INDENT-OFF* */
178 r_thread (b, {
179   BVT (clib_bihash_kv) kv;
180   kv.key = tm->keys[*idx];
181   kv.value = *hash_get (tm->key_hash, kv.key);
182   BV (clib_bihash_search) (&tm->bh, &kv, &kv);
183 });
184 /* *INDENT-ON* */
185
186 #define run_threads(x)                                                        \
187   do                                                                          \
188     {                                                                         \
189                                                                               \
190       before = clib_time_now (&tm->clib_time);                                \
191       tm->deadline = clib_cpu_time_now () +                                   \
192                      tm->runtime * tm->clib_time.clocks_per_second;           \
193       fformat (stdout, #x "-> Start threads..., runtime is %llu second(s)\n", \
194                (long long unsigned)tm->runtime);                              \
195                                                                               \
196       /*                                                                      \
197       fformat (stdout, #x "-> Writer thread only...\n");                      \
198       if (0 !=                                                                \
199           pthread_create (&tm->x##writer_thread, NULL, x##writer_thread, tm)) \
200         {                                                                     \
201           perror ("pthread_create()");                                        \
202           abort ();                                                           \
203         }                                                                     \
204                                                                               \
205       if (0 != pthread_join (tm->x##writer_thread, NULL))                     \
206         {                                                                     \
207           perror ("pthread_join()");                                          \
208           abort ();                                                           \
209         }                                                                     \
210                                                                               \
211       delta = clib_time_now (&tm->clib_time) - before;                        \
212       fformat (stdout, #x "-> %wu adds, %wu dels in %.6f seconds\n",          \
213                tm->nadds, tm->ndels, delta);                                  \
214       tm->nadds = 0;                                                          \
215       tm->ndels = 0;                                                          \
216       */                                                                      \
217                                                                               \
218       fformat (stdout, #x "-> Writer + %d readers\n", tm->nthreads);          \
219       before = clib_time_now (&tm->clib_time);                                \
220       tm->deadline = clib_cpu_time_now () +                                   \
221                      tm->runtime * tm->clib_time.clocks_per_second;           \
222       if (0 !=                                                                \
223           pthread_create (&tm->x##writer_thread, NULL, x##writer_thread, tm)) \
224         {                                                                     \
225           perror ("pthread_create()");                                        \
226           abort ();                                                           \
227         }                                                                     \
228                                                                               \
229       for (i = 0; i < tm->nthreads; i++)                                      \
230         {                                                                     \
231           tm->rthread_data[i].nlookups = 0;                                   \
232           if (0 != pthread_create (&tm->x##reader_threads[i], NULL,           \
233                                    x##reader_thread, &tm->rthread_data[i]))   \
234             {                                                                 \
235               perror ("pthread_create()");                                    \
236               abort ();                                                       \
237             }                                                                 \
238         }                                                                     \
239                                                                               \
240       if (0 != pthread_join (tm->x##writer_thread, NULL))                     \
241         {                                                                     \
242           perror ("pthread_join()");                                          \
243           abort ();                                                           \
244         }                                                                     \
245                                                                               \
246       for (i = 0; i < tm->nthreads; i++)                                      \
247         {                                                                     \
248           if (0 != pthread_join (tm->x##reader_threads[i], NULL))             \
249             {                                                                 \
250               perror ("pthread_join()");                                      \
251               abort ();                                                       \
252             }                                                                 \
253         }                                                                     \
254                                                                               \
255       delta = clib_time_now (&tm->clib_time) - before;                        \
256                                                                               \
257       total_searches = 0;                                                     \
258       for (i = 0; i < tm->nthreads; ++i)                                      \
259         {                                                                     \
260           u64 nlookups = tm->rthread_data[i].nlookups;                        \
261           fformat (stdout, #x "-> Thread #%d: %u searches\n", i, nlookups);   \
262           total_searches += nlookups;                                         \
263         }                                                                     \
264                                                                               \
265       if (delta > 0)                                                          \
266         {                                                                     \
267           ops = (tm->nadds + tm->ndels) / (f64)delta;                         \
268           fformat (stdout, #x "-> %.f add/dels per second\n", ops);           \
269           sps = ((f64)total_searches) / delta;                                \
270           fformat (stdout, #x "-> %.f searches per second\n", sps);           \
271         }                                                                     \
272                                                                               \
273       fformat (stdout,                                                        \
274                #x "-> %wu adds, %wu dels, %lld searches in %.6f seconds\n",   \
275                tm->nadds, tm->ndels, total_searches, delta);                  \
276     }                                                                         \
277   while (0);
278
279 static void
280 cb (CVT (clib_cuckoo) * h, void *ctx)
281 {
282   fformat (stdout, "Garbage callback called...\n");
283 }
284
285 static clib_error_t *
286 test_cuckoo_bihash (test_main_t * tm)
287 {
288   int i;
289   uword *p;
290   uword total_searches;
291   f64 before, delta;
292   f64 ops = 0, sps = 0;
293   f64 bops = 0, bsps = 0;
294   f64 cops = 0, csps = 0;
295   CVT (clib_cuckoo) * ch;
296   BVT (clib_bihash) * bh;
297
298   ch = &tm->ch;
299   bh = &tm->bh;
300
301   CV (clib_cuckoo_init) (ch, "test", 1, cb, NULL);
302   BV (clib_bihash_init) (bh, (char *) "test", tm->nbuckets, 256 << 20);
303
304   fformat (stdout, "Pick %lld unique %s keys...\n", tm->nitems,
305            tm->non_random_keys ? "non-random" : "random");
306
307   for (i = 0; i < tm->nitems; i++)
308     {
309       u64 rndkey;
310
311       if (tm->non_random_keys == 0)
312         {
313
314         again:
315           rndkey = random_u64 (&tm->seed);
316
317           p = hash_get (tm->key_hash, rndkey);
318           if (p)
319             goto again;
320         }
321       else
322         rndkey = (u64) (i + 1) << 16;
323
324       hash_set (tm->key_hash, rndkey, i + 1);
325       vec_add1 (tm->keys, rndkey);
326
327       int j;
328       for (j = 0; j < tm->nthreads; ++j)
329         {
330           u64 *x = tm->key_search_sequence[j];
331           vec_add1 (x, random_u64 (&tm->seed) % tm->nitems);
332           tm->key_search_sequence[j] = x;
333         }
334       vec_add1 (tm->key_add_del_sequence,
335                 random_u64 (&tm->seed) % tm->nitems);
336       vec_add1 (tm->key_op_sequence, (rndkey % 10 < 8) ? 1 : 0);
337     }
338
339   int thread_counter = 0;
340   tm->wthread_data.tm = tm;
341   tm->wthread_data.thread_idx = thread_counter;
342   for (i = 0; i < tm->nthreads; ++i)
343     {
344       tm->rthread_data[i].tm = tm;
345       tm->rthread_data[i].thread_idx = thread_counter;
346       tm->rthread_data[i].nlookups = 0;
347       ++thread_counter;
348     }
349
350   int iter;
351   for (iter = 0; iter < tm->search_iter; ++iter)
352     {
353       fformat (stdout, "Bihash test #%d\n", iter);
354       run_threads (b);
355       bops = ops;
356       bsps = sps;
357       fformat (stdout, "%U", BV (format_bihash), bh, 0);
358       fformat (stdout, "Cuckoo test #%d\n", iter);
359       run_threads (c);
360       cops = ops;
361       csps = sps;
362       fformat (stdout, "%U", CV (format_cuckoo), ch, 0);
363       fformat (stdout,
364                "Bihash add/del speed is %.2f%% of cuckoo add/del speed\n",
365                bops / cops * 100);
366       fformat (stdout,
367                "Bihash search speed is %.2f%% of cuckoo search speed\n",
368                bsps / csps * 100);
369     }
370   return 0;
371 }
372
373 clib_error_t *
374 test_cuckoo_bihash_main (test_main_t * tm)
375 {
376   unformat_input_t *i = tm->input;
377   clib_error_t *error;
378
379   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
380     {
381       if (unformat (i, "seed %u", &tm->seed))
382         ;
383       else if (unformat (i, "nbuckets %d", &tm->nbuckets))
384         ;
385       else if (unformat (i, "non-random-keys"))
386         tm->non_random_keys = 1;
387       else if (unformat (i, "nitems %d", &tm->nitems))
388         ;
389       else if (unformat (i, "search_iter %d", &tm->search_iter))
390         ;
391       else if (unformat (i, "verbose %d", &tm->verbose))
392         ;
393       else if (unformat (i, "runtime %d", &tm->runtime))
394         ;
395       else if (unformat (i, "nthreads %d", &tm->nthreads))
396         ;
397       else if (unformat (i, "verbose"))
398         tm->verbose = 1;
399       else
400         return clib_error_return (0, "unknown input '%U'",
401                                   format_unformat_error, i);
402     }
403
404   error = test_cuckoo_bihash (tm);
405
406   return error;
407 }
408
409 #ifdef CLIB_UNIX
410 int
411 main (int argc, char *argv[])
412 {
413   unformat_input_t i;
414   clib_error_t *error;
415   test_main_t *tm = &test_main;
416   clib_memset (&test_main, 0, sizeof (test_main));
417
418   clib_mem_init (0, 3ULL << 30);
419
420   tm->input = &i;
421   tm->seed = 0xdeaddabe;
422
423   tm->nbuckets = 2;
424   tm->nitems = 5;
425   tm->verbose = 1;
426   tm->nthreads = 1;
427   clib_time_init (&tm->clib_time);
428   tm->runtime = 1;
429   tm->search_iter = 1;
430   tm->key_hash = hash_create (0, sizeof (uword));
431
432   unformat_init_command_line (&i, argv);
433   error = test_cuckoo_bihash_main (tm);
434   unformat_free (&i);
435
436   if (error)
437     {
438       clib_error_report (error);
439       return 1;
440     }
441   return 0;
442 }
443 #endif /* CLIB_UNIX */
444
445 /*
446  * fd.io coding-style-patch-verification: ON
447  *
448  * Local Variables:
449  * eval: (c-set-style "gnu")
450  * End:
451  */