fdbf0bbebb01cbbd0868d3951380bfd840d03410
[vpp.git] / vppinfra / vppinfra / test_qhash.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 <vppinfra/bitmap.h>
16 #include <vppinfra/os.h>
17 #include <vppinfra/qhash.h>
18 #include <vppinfra/random.h>
19 #include <vppinfra/time.h>
20
21 typedef struct
22 {
23   u32 n_iter, seed, n_keys, n_hash_keys, verbose;
24
25   u32 max_vector;
26
27   uword *hash;
28
29   uword *keys_in_hash_bitmap;
30
31   u32 *qhash;
32
33   uword *keys;
34
35   uword *lookup_keys;
36   uword *lookup_key_indices;
37   u32 *lookup_results;
38
39   u32 *get_multiple_results;
40
41   clib_time_t time;
42
43   f64 overflow_fraction, ave_elts;
44   f64 get_time, hash_get_time;
45   f64 set_time, set_count;
46   f64 unset_time, unset_count;
47   f64 hash_set_time, hash_unset_time;
48 } test_qhash_main_t;
49
50 clib_error_t *
51 test_qhash_main (unformat_input_t * input)
52 {
53   clib_error_t *error = 0;
54   test_qhash_main_t _tm, *tm = &_tm;
55   uword i, iter;
56
57   memset (tm, 0, sizeof (tm[0]));
58   tm->n_iter = 10;
59   tm->seed = 1;
60   tm->n_keys = 10;
61   tm->max_vector = 1;
62
63   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
64     {
65       if (unformat (input, "iter %d", &tm->n_iter))
66         ;
67       else if (unformat (input, "seed %d", &tm->seed))
68         ;
69       else if (unformat (input, "keys %d", &tm->n_keys))
70         ;
71       else if (unformat (input, "size %d", &tm->n_hash_keys))
72         ;
73       else if (unformat (input, "vector %d", &tm->max_vector))
74         ;
75       else if (unformat (input, "verbose"))
76         tm->verbose = 1;
77       else
78         {
79           error = clib_error_create ("unknown input `%U'\n",
80                                      format_unformat_error, input);
81           goto done;
82         }
83     }
84
85   if (!tm->seed)
86     tm->seed = random_default_seed ();
87
88   clib_time_init (&tm->time);
89
90   clib_warning ("iter %d, seed %u, keys %d, max vector %d, ",
91                 tm->n_iter, tm->seed, tm->n_keys, tm->max_vector);
92
93   vec_resize (tm->keys, tm->n_keys);
94   vec_resize (tm->get_multiple_results, tm->n_keys);
95   for (i = 0; i < vec_len (tm->keys); i++)
96     tm->keys[i] = random_uword (&tm->seed);
97
98   if (!tm->n_hash_keys)
99     tm->n_hash_keys = 2 * max_pow2 (tm->n_keys);
100   tm->n_hash_keys = clib_max (tm->n_keys, tm->n_hash_keys);
101   qhash_resize (tm->qhash, tm->n_hash_keys);
102
103   {
104     qhash_t *h = qhash_header (tm->qhash);
105     int i;
106     for (i = 0; i < ARRAY_LEN (h->hash_seeds); i++)
107       h->hash_seeds[i] = random_uword (&tm->seed);
108   }
109
110   vec_resize (tm->lookup_keys, tm->max_vector);
111   vec_resize (tm->lookup_key_indices, tm->max_vector);
112   vec_resize (tm->lookup_results, tm->max_vector);
113
114   for (iter = 0; iter < tm->n_iter; iter++)
115     {
116       uword *p, j, n, is_set;
117
118       n = tm->max_vector;
119
120       is_set = random_u32 (&tm->seed) & 1;
121       is_set |= hash_elts (tm->hash) < (tm->n_keys / 4);
122       if (hash_elts (tm->hash) > (3 * tm->n_keys) / 4)
123         is_set = 0;
124
125       _vec_len (tm->lookup_keys) = n;
126       _vec_len (tm->lookup_key_indices) = n;
127       j = 0;
128       while (j < n)
129         {
130           i = random_u32 (&tm->seed) % vec_len (tm->keys);
131           if (clib_bitmap_get (tm->keys_in_hash_bitmap, i) != is_set)
132             {
133               f64 t[2];
134               tm->lookup_key_indices[j] = i;
135               tm->lookup_keys[j] = tm->keys[i];
136               t[0] = clib_time_now (&tm->time);
137               if (is_set)
138                 hash_set (tm->hash, tm->keys[i], i);
139               else
140                 hash_unset (tm->hash, tm->keys[i]);
141               t[1] = clib_time_now (&tm->time);
142               if (is_set)
143                 tm->hash_set_time += t[1] - t[0];
144               else
145                 tm->hash_unset_time += t[1] - t[0];
146               tm->keys_in_hash_bitmap
147                 = clib_bitmap_set (tm->keys_in_hash_bitmap, i, is_set);
148               j++;
149             }
150         }
151
152       {
153         f64 t[2];
154
155         if (is_set)
156           {
157             t[0] = clib_time_now (&tm->time);
158             qhash_set_multiple (tm->qhash,
159                                 tm->lookup_keys,
160                                 vec_len (tm->lookup_keys),
161                                 tm->lookup_results);
162             t[1] = clib_time_now (&tm->time);
163             tm->set_time += t[1] - t[0];
164             tm->set_count += vec_len (tm->lookup_keys);
165             for (i = 0; i < vec_len (tm->lookup_keys); i++)
166               {
167                 uword r = tm->lookup_results[i];
168                 *vec_elt_at_index (tm->qhash, r) = tm->lookup_key_indices[i];
169               }
170           }
171         else
172           {
173             t[0] = clib_time_now (&tm->time);
174             qhash_unset_multiple (tm->qhash,
175                                   tm->lookup_keys,
176                                   vec_len (tm->lookup_keys),
177                                   tm->lookup_results);
178             t[1] = clib_time_now (&tm->time);
179             tm->unset_time += t[1] - t[0];
180             tm->unset_count += vec_len (tm->lookup_keys);
181
182             for (i = 0; i < vec_len (tm->lookup_keys); i++)
183               {
184                 uword r = tm->lookup_results[i];
185                 *vec_elt_at_index (tm->qhash, r) = ~0;
186               }
187           }
188       }
189
190       if (qhash_elts (tm->qhash) != hash_elts (tm->hash))
191         os_panic ();
192
193       {
194         qhash_t *h;
195         uword i, k, l, count;
196
197         h = qhash_header (tm->qhash);
198
199         for (i = k = 0; k < vec_len (h->hash_key_valid_bitmap); k++)
200           i += count_set_bits (h->hash_key_valid_bitmap[k]);
201         k = hash_elts (h->overflow_hash);
202         l = qhash_elts (tm->qhash);
203         if (i + k != l)
204           os_panic ();
205
206         count = hash_elts (h->overflow_hash);
207         for (i = 0; i < (1 << h->log2_hash_size); i++)
208           count += tm->qhash[i] != ~0;
209         if (count != qhash_elts (tm->qhash))
210           os_panic ();
211
212         {
213           u32 *tmp = 0;
214
215           /* *INDENT-OFF* */
216           hash_foreach (k, l, h->overflow_hash, ({
217             j = qhash_hash_mix (h, k) / QHASH_KEYS_PER_BUCKET;
218             vec_validate (tmp, j);
219             tmp[j] += 1;
220           }));
221           /* *INDENT-ON* */
222
223           for (k = 0; k < vec_len (tmp); k++)
224             {
225               if (k >= vec_len (h->overflow_counts))
226                 os_panic ();
227               if (h->overflow_counts[k] != tmp[k])
228                 os_panic ();
229             }
230           for (; k < vec_len (h->overflow_counts); k++)
231             if (h->overflow_counts[k] != 0)
232               os_panic ();
233
234           vec_free (tmp);
235         }
236       }
237
238       {
239         f64 t[2];
240
241         t[0] = clib_time_now (&tm->time);
242         qhash_get_multiple (tm->qhash, tm->keys, vec_len (tm->keys),
243                             tm->get_multiple_results);
244         t[1] = clib_time_now (&tm->time);
245         tm->get_time += t[1] - t[0];
246
247         for (i = 0; i < vec_len (tm->keys); i++)
248           {
249             u32 r;
250
251             t[0] = clib_time_now (&tm->time);
252             p = hash_get (tm->hash, tm->keys[i]);
253             t[1] = clib_time_now (&tm->time);
254             tm->hash_get_time += t[1] - t[0];
255
256             r = qhash_get (tm->qhash, tm->keys[i]);
257             if (p)
258               {
259                 if (p[0] != i)
260                   os_panic ();
261                 if (*vec_elt_at_index (tm->qhash, r) != i)
262                   os_panic ();
263               }
264             else
265               {
266                 if (r != ~0)
267                   os_panic ();
268               }
269             if (r != tm->get_multiple_results[i])
270               os_panic ();
271           }
272       }
273
274       tm->overflow_fraction +=
275         ((f64) qhash_n_overflow (tm->qhash) / qhash_elts (tm->qhash));
276       tm->ave_elts += qhash_elts (tm->qhash);
277     }
278
279   fformat (stderr, "%d iter %.6e overflow, %.4f ave. elts\n",
280            tm->n_iter,
281            tm->overflow_fraction / tm->n_iter, tm->ave_elts / tm->n_iter);
282
283   tm->get_time /= tm->n_iter * vec_len (tm->keys);
284   tm->hash_get_time /= tm->n_iter * vec_len (tm->keys);
285
286   tm->set_time /= tm->set_count;
287   tm->unset_time /= tm->unset_count;
288   tm->hash_set_time /= tm->set_count;
289   tm->hash_unset_time /= tm->unset_count;
290
291   fformat (stderr,
292            "get/set/unset clocks %.2e %.2e %.2e clib %.2e %.2e %.2e ratio %.2f %.2f %.2f\n",
293            tm->get_time * tm->time.clocks_per_second,
294            tm->set_time * tm->time.clocks_per_second,
295            tm->unset_time * tm->time.clocks_per_second,
296            tm->hash_get_time * tm->time.clocks_per_second,
297            tm->hash_set_time * tm->time.clocks_per_second,
298            tm->hash_unset_time * tm->time.clocks_per_second,
299            tm->hash_get_time / tm->get_time, tm->hash_set_time / tm->set_time,
300            tm->hash_unset_time / tm->unset_time);
301
302
303 done:
304   return error;
305 }
306
307 #ifdef CLIB_UNIX
308 int
309 main (int argc, char *argv[])
310 {
311   unformat_input_t i;
312   clib_error_t *error;
313
314   unformat_init_command_line (&i, argv);
315   error = test_qhash_main (&i);
316   unformat_free (&i);
317   if (error)
318     {
319       clib_error_report (error);
320       return 1;
321     }
322   else
323     return 0;
324 }
325 #endif /* CLIB_UNIX */
326
327 /*
328  * fd.io coding-style-patch-verification: ON
329  *
330  * Local Variables:
331  * eval: (c-set-style "gnu")
332  * End:
333  */