Initial commit of vpp code.
[vpp.git] / vppinfra / vppinfra / test_bihash_template.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/time.h>
16 #include <vppinfra/cache.h>
17 #include <vppinfra/error.h>
18
19 #include <vppinfra/bihash_8_8.h>
20 #include <vppinfra/bihash_template.h>
21
22 #include <vppinfra/bihash_template.c>
23
24 typedef struct {
25   u32 seed;
26   u32 nbuckets;
27   u32 nitems;
28   u32 search_iter;
29   int careful_delete_tests;
30   int verbose;
31   int non_random_keys;
32   uword * key_hash;
33   u64 * keys;
34   BVT(clib_bihash) hash;
35   clib_time_t clib_time;
36
37   unformat_input_t * input;
38   
39 } test_main_t;
40
41 test_main_t test_main;
42
43 uword vl (void * v)
44 {
45     return vec_len (v);
46 }
47
48 static clib_error_t * test_bihash (test_main_t * tm)
49 {
50   int i, j;
51   uword * p;
52   uword total_searches;
53   f64 before, delta;
54   BVT(clib_bihash) * h;
55   BVT(clib_bihash_kv) kv;
56
57   h = &tm->hash;
58
59   BV(clib_bihash_init) (h, "test", tm->nbuckets, 3ULL<<30);
60   
61   fformat (stdout, "Pick %lld unique %s keys...\n", 
62            tm->nitems, tm->non_random_keys ? "non-random" : "random");
63
64   for (i = 0; i < tm->nitems; i++)
65     {
66       u64 rndkey;
67
68       if (tm->non_random_keys == 0)
69         {
70
71         again:
72           rndkey = random_u64 (&tm->seed);
73           
74           p = hash_get (tm->key_hash, rndkey);
75           if (p)
76             goto again;
77         }
78       else
79           rndkey = (u64)(i+1) << 16;
80
81       hash_set (tm->key_hash, rndkey, i+1);
82       vec_add1 (tm->keys, rndkey);
83     }
84
85   fformat (stdout, "Add items...\n");
86   for (i = 0; i < tm->nitems; i++)
87     {
88       kv.key = tm->keys[i];
89       kv.value = i+1;
90
91       BV(clib_bihash_add_del) (h, &kv, 1 /* is_add */);
92
93       if (tm->verbose > 1)
94         {
95           fformat (stdout, "--------------------\n");
96           fformat (stdout, "After adding key %llu value %lld...\n", 
97                    tm->keys[i], (u64)(i+1));
98           fformat (stdout, "%U", BV(format_bihash), h, 2 /* very verbose */);
99         }
100     }
101
102   fformat (stdout, "%U", BV(format_bihash), h, 0 /* very verbose */);
103
104   fformat (stdout, "Search for items %d times...\n", tm->search_iter);
105
106   before = clib_time_now (&tm->clib_time);
107
108   for (j = 0; j < tm->search_iter; j++)
109     {
110       u64 hash1 = clib_xxhash (tm->keys[0]);
111
112       for (i = 0; i < tm->nitems; i++)
113         {
114           if (i < (tm->nitems - 3))
115             {
116               clib_bihash_bucket_t * b;
117               BVT(clib_bihash_value) * v;
118               u64 hash2 = clib_xxhash (tm->keys[i+3]);
119               u32 bucket_index = hash2 & (h->nbuckets-1);
120               b = &h->buckets[bucket_index];
121               CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD);
122
123               bucket_index = hash1 & (h->nbuckets-1);
124               b = &h->buckets[bucket_index];
125               v = BV(clib_bihash_get_value) (h, b->offset);
126               hash1 >>= h->log2_nbuckets;
127               hash1 = hash1 & ((1<<b->log2_pages)-1);
128               v += hash1;
129               CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD);
130               
131               hash1 = hash2;
132             }
133
134           kv.key = tm->keys[i];
135           if (BV(clib_bihash_search) (h, &kv, &kv) < 0)
136             clib_warning ("search for key %lld failed unexpectedly\n", 
137                           tm->keys[i]);
138           if (kv.value != (u64)(i+1))
139             clib_warning ("search for key %lld returned %lld, not %lld\n",
140                           tm->keys, kv.value, (u64)(i+1));
141         }
142     }
143
144   delta = clib_time_now (&tm->clib_time) - before;
145   total_searches = (uword)tm->search_iter * (uword) tm->nitems;
146
147   if (delta > 0)
148     fformat (stdout, "%.f searches per second\n",
149              ((f64)total_searches) / delta);
150
151   fformat (stdout, "%lld searches in %.6f seconds\n", total_searches, delta);
152
153   fformat (stdout, "Standard E-hash search for items %d times...\n", 
154            tm->search_iter);
155
156   before = clib_time_now (&tm->clib_time);
157
158   for (j = 0; j < tm->search_iter; j++)
159     {
160       for (i = 0; i < tm->nitems; i++)
161         {
162           p = hash_get (tm->key_hash, tm->keys[i]);
163           if (p == 0 || p[0] != (uword)(i+1))
164             clib_warning ("ugh, couldn't find %lld\n", tm->keys[i]);
165         }
166     }
167
168   delta = clib_time_now (&tm->clib_time) - before;
169   total_searches = (uword)tm->search_iter * (uword) tm->nitems;
170
171   fformat (stdout, "%lld searches in %.6f seconds\n", total_searches, delta);
172
173   if (delta > 0)
174     fformat (stdout, "%.f searches per second\n",
175              ((f64)total_searches) / delta);
176
177   fformat (stdout, "Delete items...\n");
178
179   for (i = 0; i < tm->nitems; i++)
180     {
181       int j;
182       int rv;
183
184       kv.key = tm->keys[i];
185       kv.value = (u64)(i+1);
186       rv = BV(clib_bihash_add_del) (h, &kv,  0 /* is_add */);
187
188       if (rv < 0)
189         clib_warning ("delete key %lld not ok but should be",
190                       tm->keys[i]);
191
192       if (tm->careful_delete_tests)
193         {
194           for (j = 0; j < tm->nitems; j++)
195             {
196               kv.key = tm->keys[j];
197               rv = BV(clib_bihash_search) (h, &kv, &kv);
198               if (j <= i && rv >= 0)
199                 {
200                   clib_warning 
201                     ( "i %d j %d search ok but should not be, value %lld",
202                       i, j, kv.value);
203                 }
204               if (j > i && rv < 0)
205                 {
206                   clib_warning ("i %d j %d search not ok but should be",
207                                 i, j);
208                 }
209             }
210         }
211     }
212
213   fformat (stdout, "After deletions, should be empty...\n");
214
215   fformat (stdout, "%U", BV(format_bihash), h, 0 /* very verbose */);
216   return 0;
217 }
218
219 clib_error_t * 
220 test_bihash_main (test_main_t * tm)
221 {
222   unformat_input_t * i = tm->input;
223   clib_error_t * error;
224
225   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
226     {
227       if (unformat (i, "seed %u", &tm->seed))
228         ;
229
230       else if (unformat (i, "nbuckets %d", &tm->nbuckets))
231         ;
232       else if (unformat (i, "non-random-keys"))
233         tm->non_random_keys = 1;
234       else if (unformat (i, "nitems %d", &tm->nitems))
235         ;
236       else if (unformat (i, "careful %d", &tm->careful_delete_tests))
237         ;
238       else if (unformat (i, "verbose %d", &tm->verbose))
239         ;
240       else if (unformat (i, "search %d", &tm->search_iter))
241         ;
242       else if (unformat (i, "verbose"))
243         tm->verbose = 1;
244       else
245         return clib_error_return (0, "unknown input '%U'", 
246                                   format_unformat_error, i);
247     }
248     
249   error = test_bihash (tm);
250
251   return error;
252 }
253
254 #ifdef CLIB_UNIX
255 int main (int argc, char * argv[])
256 {
257   unformat_input_t i;
258   clib_error_t * error;
259   test_main_t * tm = &test_main;
260
261   clib_mem_init (0, 3ULL<<30);
262
263   tm->input = &i;
264   tm->seed = 0xdeaddabe;
265
266   tm->nbuckets = 2;
267   tm->nitems = 5;
268   tm->verbose = 1;
269   tm->search_iter = 1;
270   tm->careful_delete_tests = 0;
271   tm->key_hash = hash_create (0, sizeof (uword));
272   clib_time_init (&tm->clib_time);
273
274   unformat_init_command_line (&i, argv);
275   error = test_bihash_main (tm);
276   unformat_free (&i);
277
278   if (error)
279     {
280       clib_error_report (error);
281       return 1;
282     }
283   return 0;
284 }
285 #endif /* CLIB_UNIX */