Typos. A bunch of typos I've been collecting.
[vpp.git] / src / plugins / unittest / bihash_test.c
1 /*
2  * Copyright (c) 2015 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 #include <vlib/vlib.h>
16 #include <vppinfra/time.h>
17 #include <vppinfra/cache.h>
18 #include <vppinfra/error.h>
19 #include <sys/resource.h>
20 #include <stdio.h>
21 #include <pthread.h>
22
23 #include <vppinfra/bihash_8_8.h>
24 #include <vppinfra/bihash_template.h>
25
26 #include <vppinfra/bihash_template.c>
27
28 typedef struct
29 {
30   volatile u32 thread_barrier;
31   volatile u32 threads_running;
32   volatile u64 sequence_number;
33   u64 seed;
34   u32 nbuckets;
35   u32 nitems;
36   u32 ncycles;
37   u32 report_every_n;
38   u32 search_iter;
39   int careful_delete_tests;
40   int verbose;
41   int non_random_keys;
42   u32 nthreads;
43   uword *key_hash;
44   u64 *keys;
45   uword hash_memory_size;
46     BVT (clib_bihash) hash;
47   clib_time_t clib_time;
48   void *global_heap;
49
50   unformat_input_t *input;
51
52   /* convenience */
53   vlib_main_t *vlib_main;
54 } bihash_test_main_t;
55
56 static bihash_test_main_t bihash_test_main;
57
58 static clib_error_t *
59 test_bihash_vec64 (bihash_test_main_t * tm)
60 {
61   u32 user_buckets = 1228800;
62   u32 user_memory_size = 209715200;
63   BVT (clib_bihash_kv) kv;
64   int i, j;
65   f64 before;
66   f64 *cum_times = 0;
67   BVT (clib_bihash) * h;
68
69   h = &tm->hash;
70
71   BV (clib_bihash_init) (h, "test", user_buckets, user_memory_size);
72
73   before = clib_time_now (&tm->clib_time);
74
75   for (j = 0; j < 10; j++)
76     {
77       for (i = 1; i <= j * 1000 + 1; i++)
78         {
79           kv.key = i;
80           kv.value = 1;
81
82           BV (clib_bihash_add_del) (h, &kv, 1 /* is_add */ );
83         }
84
85       vec_add1 (cum_times, clib_time_now (&tm->clib_time) - before);
86     }
87
88   for (j = 0; j < vec_len (cum_times); j++)
89     fformat (stdout, "Cum time for %d: %.4f (us)\n", (j + 1) * 1000,
90              cum_times[j] * 1e6);
91
92   return 0;
93 }
94
95 void *
96 test_bihash_thread_fn (void *arg)
97 {
98   BVT (clib_bihash) * h;
99   BVT (clib_bihash_kv) kv;
100   bihash_test_main_t *tm = &bihash_test_main;
101   int i, j;
102   u32 my_thread_index = (uword) arg;
103
104   while (tm->thread_barrier)
105     ;
106
107   h = &tm->hash;
108
109   for (i = 0; i < tm->ncycles; i++)
110     {
111       for (j = 0; j < tm->nitems; j++)
112         {
113           kv.key = ((u64) my_thread_index << 32) | (u64) j;
114           kv.value = ((u64) my_thread_index << 32) | (u64) j;
115           (void) __atomic_add_fetch (&tm->sequence_number, 1,
116                                      __ATOMIC_ACQUIRE);
117           BV (clib_bihash_add_del) (h, &kv, 1 /* is_add */ );
118         }
119       for (j = 0; j < tm->nitems; j++)
120         {
121           kv.key = ((u64) my_thread_index << 32) | (u64) j;
122           kv.value = ((u64) my_thread_index << 32) | (u64) j;
123           (void) __atomic_add_fetch (&tm->sequence_number, 1,
124                                      __ATOMIC_ACQUIRE);
125           BV (clib_bihash_add_del) (h, &kv, 0 /* is_add */ );
126         }
127     }
128
129   (void) __atomic_sub_fetch (&tm->threads_running, 1, __ATOMIC_ACQUIRE);
130   pthread_exit (0);
131   return (0);                   /* not so much */
132 }
133
134 static clib_error_t *
135 test_bihash_threads (bihash_test_main_t * tm)
136 {
137   int i;
138   pthread_t handle;
139   BVT (clib_bihash) * h;
140   int rv;
141   f64 before, after, delta;
142
143   h = &tm->hash;
144
145   BV (clib_bihash_init) (h, "test", tm->nbuckets, tm->hash_memory_size);
146
147   tm->thread_barrier = 1;
148
149   /* Start the worker threads */
150   for (i = 0; i < tm->nthreads; i++)
151     {
152       rv = pthread_create (&handle, NULL, test_bihash_thread_fn,
153                            (void *) (uword) i);
154       if (rv)
155         {
156           clib_unix_warning ("pthread_create returned %d", rv);
157         }
158     }
159   tm->threads_running = i;
160   tm->sequence_number = 0;
161   CLIB_MEMORY_BARRIER ();
162
163   /* start the workers */
164   before = vlib_time_now (tm->vlib_main);
165   tm->thread_barrier = 0;
166
167   while (tm->threads_running > 0)
168     CLIB_PAUSE ();
169
170   after = vlib_time_now (tm->vlib_main);
171   delta = after - before;
172
173   clib_warning ("%lld items in %.2f seconds, %.2f items/sec",
174                 (u64) tm->nthreads * (u64) tm->nitems, delta,
175                 delta >
176                 0.0 ? ((f64) ((u64) tm->nthreads * (u64) tm->nitems)) /
177                 delta : 0.0);
178
179   BV (clib_bihash_free) (h);
180   return 0;
181 }
182
183 /*
184  * Callback to blow up spectacularly if anything remains in the table
185  */
186 static void
187 count_items (BVT (clib_bihash_kv) * kvp, void *notused)
188 {
189   _clib_error (CLIB_ERROR_ABORT, 0, 0,
190                "bihash test FAILED, items left in table!");
191 }
192
193 static clib_error_t *
194 test_bihash (bihash_test_main_t * tm)
195 {
196   int i, j;
197   uword *p;
198   uword total_searches;
199   f64 before, delta;
200   BVT (clib_bihash) * h;
201   BVT (clib_bihash_kv) kv;
202   u32 acycle;
203
204   h = &tm->hash;
205
206   BV (clib_bihash_init) (h, "test", tm->nbuckets, tm->hash_memory_size);
207
208   for (acycle = 0; acycle < tm->ncycles; acycle++)
209     {
210       if ((acycle % tm->report_every_n) == 0)
211         {
212           fformat (stdout, "Cycle %lld out of %lld...\n",
213                    acycle, tm->ncycles);
214
215           fformat (stdout, "Pick %lld unique %s keys...\n",
216                    tm->nitems, tm->non_random_keys ? "non-random" : "random");
217         }
218
219       for (i = 0; i < tm->nitems; i++)
220         {
221           u64 rndkey;
222
223           if (tm->non_random_keys == 0)
224             {
225
226             again:
227               rndkey = random_u64 (&tm->seed);
228
229               p = hash_get (tm->key_hash, rndkey);
230               if (p)
231                 goto again;
232             }
233           else
234             rndkey = (u64) (i + 1) << 16;
235           rndkey += acycle;
236
237           hash_set (tm->key_hash, rndkey, i + 1);
238           vec_add1 (tm->keys, rndkey);
239         }
240
241
242       if ((acycle % tm->report_every_n) == 0)
243         fformat (stdout, "Add items...\n");
244
245       for (i = 0; i < tm->nitems; i++)
246         {
247           kv.key = tm->keys[i];
248           kv.value = i + 1;
249
250           BV (clib_bihash_add_del) (h, &kv, 1 /* is_add */ );
251
252           if (tm->verbose > 1)
253             {
254               fformat (stdout, "--------------------\n");
255               fformat (stdout, "After adding key %llu value %lld...\n",
256                        tm->keys[i], (u64) (i + 1));
257               fformat (stdout, "%U", BV (format_bihash), h,
258                        2 /* very verbose */ );
259             }
260         }
261
262       if ((acycle % tm->report_every_n) == 0)
263         {
264           fformat (stdout, "%U", BV (format_bihash), h,
265                    0 /* very verbose */ );
266
267           fformat (stdout, "Search for items %d times...\n", tm->search_iter);
268         }
269
270       before = clib_time_now (&tm->clib_time);
271
272       for (j = 0; j < tm->search_iter; j++)
273         {
274           for (i = 0; i < tm->nitems; i++)
275             {
276               kv.key = tm->keys[i];
277               if (BV (clib_bihash_search) (h, &kv, &kv) < 0)
278                 if (BV (clib_bihash_search) (h, &kv, &kv) < 0)
279                   clib_warning
280                     ("[%d] search for key %lld failed unexpectedly\n", i,
281                      tm->keys[i]);
282               if (kv.value != (u64) (i + 1))
283                 clib_warning
284                   ("[%d] search for key %lld returned %lld, not %lld\n", i,
285                    tm->keys, kv.value, (u64) (i + 1));
286             }
287         }
288
289       if ((acycle % tm->report_every_n) == 0)
290         {
291           delta = clib_time_now (&tm->clib_time) - before;
292           total_searches = (uword) tm->search_iter * (uword) tm->nitems;
293
294           if (delta > 0)
295             fformat (stdout, "%.f searches per second\n",
296                      ((f64) total_searches) / delta);
297
298           fformat (stdout, "%lld searches in %.6f seconds\n", total_searches,
299                    delta);
300
301           fformat (stdout, "Standard E-hash search for items %d times...\n",
302                    tm->search_iter);
303         }
304
305       before = clib_time_now (&tm->clib_time);
306
307       for (j = 0; j < tm->search_iter; j++)
308         {
309           for (i = 0; i < tm->nitems; i++)
310             {
311               p = hash_get (tm->key_hash, tm->keys[i]);
312               if (p == 0 || p[0] != (uword) (i + 1))
313                 clib_warning ("ugh, couldn't find %lld\n", tm->keys[i]);
314             }
315         }
316
317       delta = clib_time_now (&tm->clib_time) - before;
318       total_searches = (uword) tm->search_iter * (uword) tm->nitems;
319
320       if ((acycle % tm->report_every_n) == 0)
321         {
322           fformat (stdout, "%lld searches in %.6f seconds\n",
323                    total_searches, delta);
324
325           if (delta > 0)
326             fformat (stdout, "%.f searches per second\n",
327                      ((f64) total_searches) / delta);
328           fformat (stdout, "Delete items...\n");
329         }
330
331       for (i = 0; i < tm->nitems; i++)
332         {
333           int j;
334           int rv;
335
336           kv.key = tm->keys[i];
337           kv.value = (u64) (i + 1);
338           rv = BV (clib_bihash_add_del) (h, &kv, 0 /* is_add */ );
339
340           if (rv < 0)
341             clib_warning ("delete key %lld not ok but should be",
342                           tm->keys[i]);
343
344           if (tm->careful_delete_tests)
345             {
346               for (j = 0; j < tm->nitems; j++)
347                 {
348                   kv.key = tm->keys[j];
349                   rv = BV (clib_bihash_search) (h, &kv, &kv);
350                   if (j <= i && rv >= 0)
351                     {
352                       clib_warning
353                         ("i %d j %d search ok but should not be, value %lld",
354                          i, j, kv.value);
355                     }
356                   if (j > i && rv < 0)
357                     {
358                       clib_warning ("i %d j %d search not ok but should be",
359                                     i, j);
360                     }
361                 }
362             }
363         }
364       if ((acycle % tm->report_every_n) == 0)
365         {
366           struct rusage r_usage;
367           getrusage (RUSAGE_SELF, &r_usage);
368           fformat (stdout, "Kernel RSS: %ld bytes\n", r_usage.ru_maxrss);
369           fformat (stdout, "%U\n", BV (format_bihash), h, 0 /* verbose */ );
370         }
371
372       /* Clean up side-bet hash table and random key vector */
373       hash_free (tm->key_hash);
374       vec_reset_length (tm->keys);
375       /* Recreate hash table if we're going to need it again */
376       if (acycle != (tm->ncycles - 1))
377         tm->key_hash = hash_create (tm->nitems, sizeof (uword));
378     }
379
380   fformat (stdout, "End of run, should be empty...\n");
381
382   fformat (stdout, "%U", BV (format_bihash), h, 0 /* very verbose */ );
383
384   /* ASSERTs if any items remain */
385   BV (clib_bihash_foreach_key_value_pair) (h, count_items, 0);
386
387   BV (clib_bihash_free) (h);
388
389   vec_free (tm->keys);
390   hash_free (tm->key_hash);
391
392   return 0;
393 }
394
395 static clib_error_t *
396 test_bihash_command_fn (vlib_main_t * vm,
397                         unformat_input_t * input, vlib_cli_command_t * cmd)
398 {
399   bihash_test_main_t *tm = &bihash_test_main;
400   clib_error_t *error;
401   int which = 0;
402
403   tm->hash_memory_size = 32ULL << 20;
404   tm->nbuckets = 32000;
405   tm->nitems = 100000;
406   tm->ncycles = 10;
407   tm->report_every_n = 50000;
408   tm->seed = 0x1badf00d;
409
410   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
411     {
412       if (unformat (input, "seed %u", &tm->seed))
413         ;
414
415       else if (unformat (input, "nbuckets %d", &tm->nbuckets))
416         ;
417       else if (unformat (input, "non-random-keys"))
418         tm->non_random_keys = 1;
419       else if (unformat (input, "nitems %d", &tm->nitems))
420         ;
421       else if (unformat (input, "ncycles %d", &tm->ncycles))
422         ;
423       else if (unformat (input, "careful %d", &tm->careful_delete_tests))
424         ;
425       else if (unformat (input, "verbose %d", &tm->verbose))
426         ;
427       else if (unformat (input, "search %d", &tm->search_iter))
428         ;
429       else if (unformat (input, "report-every %d", &tm->report_every_n))
430         ;
431       else if (unformat (input, "memory-size %U",
432                          unformat_memory_size, &tm->hash_memory_size))
433         ;
434       else if (unformat (input, "vec64"))
435         which = 1;
436       else if (unformat (input, "threads %u", &tm->nthreads))
437         which = 2;
438       else if (unformat (input, "verbose"))
439         tm->verbose = 1;
440       else
441         return clib_error_return (0, "unknown input '%U'",
442                                   format_unformat_error, input);
443     }
444
445   /* Preallocate hash table, key vector */
446   tm->key_hash = hash_create (tm->nitems, sizeof (uword));
447   vec_validate (tm->keys, tm->nitems - 1);
448   _vec_len (tm->keys) = 0;
449
450   switch (which)
451     {
452     case 0:
453       error = test_bihash (tm);
454       break;
455
456     case 1:
457       error = test_bihash_vec64 (tm);
458       break;
459
460     case 2:
461       error = test_bihash_threads (tm);
462       break;
463
464     default:
465       return clib_error_return (0, "no such test?");
466     }
467
468   return error;
469 }
470
471 /* *INDENT-OFF* */
472 VLIB_CLI_COMMAND (test_bihash_command, static) =
473 {
474   .path = "test bihash",
475   .short_help = "test bihash",
476   .function = test_bihash_command_fn,
477 };
478 /* *INDENT-ON* */
479
480 static clib_error_t *
481 bihash_test_init (vlib_main_t * vm)
482 {
483   bihash_test_main_t *tm = &bihash_test_main;
484
485   tm->vlib_main = vm;
486   return (0);
487 }
488
489 VLIB_INIT_FUNCTION (bihash_test_init);
490
491 /*
492  * fd.io coding-style-patch-verification: ON
493  *
494  * Local Variables:
495  * eval: (c-set-style "gnu")
496  * End:
497  */