blob: 19ac4edf2e2638dfe96975a10b26b4e0abba2b6b [file] [log] [blame]
Pierre Pfister953f5512018-01-12 09:41:16 +01001/*
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
16#include <vppinfra/time.h>
17#include <vppinfra/cache.h>
18#include <vppinfra/error.h>
19
20#include <vppinfra/heap.h>
21#include <vppinfra/format.h>
22#include <vppinfra/random.h>
23#include <vppinfra/hash.h>
24
25#include <vppinfra/flowhash_8_8.h>
26
27/* Not actually tested here. But included for compilation purposes. */
28#include <vppinfra/flowhash_24_16.h>
29
30typedef struct
31{
32 u64 seed;
33 u32 fixed_entries;
34 u32 collision_buckets;
35 u32 nitems;
36 u32 iterations;
37 u32 prefetch;
38 int non_random_keys;
39 uword *key_hash;
40 flowhash_lkey_8_8_t *keys;
41 flowhash_8_8_t *hash;
42 clib_time_t clib_time;
43 unformat_input_t *input;
44} test_main_t;
45
46test_main_t test_main;
47
48static clib_error_t *
49test_flowhash (test_main_t * tm)
50{
51 f64 before, delta;
52 u64 total;
53 u32 overflow;
54 int i, j;
55 uword *p;
56 tm->hash = flowhash_alloc_8_8 (tm->fixed_entries, tm->collision_buckets);
57 if (tm->hash == NULL)
58 return clib_error_return (0, "Could not alloc hash");
59
60 fformat (stdout, "Allocated hash memory size: %llu\n",
61 flowhash_memory_size (tm->hash));
62
63 fformat (stdout, "Pick %lld unique %s keys...\n",
64 tm->nitems, tm->non_random_keys ? "non-random" : "random");
65
66 for (i = 0; i < tm->nitems; i++)
67 {
68 flowhash_lkey_8_8_t rndkey;
69 if (tm->non_random_keys == 0)
70 {
71 again:
72 rndkey.as_u64[0] = random_u64 (&tm->seed);
73 if ((p = hash_get (tm->key_hash, rndkey.as_u64[0])))
74 goto again;
75 }
76 else
77 rndkey.as_u64[0] = (u64) (i + 1) << 16;
78
79 hash_set (tm->key_hash, rndkey.as_u64[0], i + 1);
80 vec_add1 (tm->keys, rndkey);
81 }
82
83 hash_free (tm->key_hash);
84
85 /* Additions */
86 overflow = 0;
87 before = clib_time_now (&tm->clib_time);
88 fformat (stdout, "Adding %u items...\n", tm->nitems);
89 for (i = 0; i < tm->nitems; i++)
90 {
91 u32 hash = flowhash_hash_8_8 (&tm->keys[i]);
92 u32 ei;
93 flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
94 if (flowhash_is_overflow (ei))
95 overflow++;
96
97 /* Set value (No matter if success) */
98 flowhash_value (tm->hash, ei)->as_u64[0] = i + 1;
99
100 /* Save value until time > 1 */
101 flowhash_timeout (tm->hash, ei) = 1;
102 }
103
104 delta = clib_time_now (&tm->clib_time) - before;
105 total = tm->nitems;
106 fformat (stdout, "%lld additions in %.6f seconds\n", total, delta);
107 if (delta > 0)
108 fformat (stdout, "%.f additions per second\n", ((f64) total) / delta);
109
110 fformat (stdout, "%u elements in table\n", flowhash_elts_8_8 (tm->hash, 1));
111 fformat (stdout, "Flowhash counters:\n");
112 fformat (stdout, " collision-lookup: %lu\n",
113 tm->hash->collision_lookup_counter);
114 fformat (stdout, " not-enough-buckets: %lu\n",
115 tm->hash->not_enough_buckets_counter);
116 fformat (stdout, " overflows: %lu\n", overflow);
117
118 /* Lookups (very similar to additions) */
119 overflow = 0;
120 before = clib_time_now (&tm->clib_time);
121 fformat (stdout, "Looking up %u items %u times...\n", tm->nitems,
122 tm->iterations);
123
124 for (j = 0; j < tm->iterations; j++)
125 {
126 i = 0;
127 if (tm->prefetch)
128 for (; i < tm->nitems - tm->prefetch; i++)
129 {
130 u32 ei;
131 u32 hash = flowhash_hash_8_8 (&tm->keys[i + tm->prefetch]);
132 flowhash_prefetch (tm->hash, hash);
133 hash = flowhash_hash_8_8 (&tm->keys[i]);
134 flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
135 if (flowhash_is_overflow (ei))
136 overflow++;
137 else if (flowhash_timeout (tm->hash, ei) != 1)
138 clib_warning ("Key not found: %lld\n", tm->keys[i].as_u64[0]);
139 else if (flowhash_value (tm->hash, ei)->as_u64[0] != i + 1)
140 clib_warning ("Value mismatch for key %lld\n",
141 tm->keys[i].as_u64[0]);
142 }
143
144 for (; i < tm->nitems; i++)
145 {
146 u32 ei;
147 u32 hash = flowhash_hash_8_8 (&tm->keys[i]);
148 flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
149 if (flowhash_is_overflow (ei))
150 overflow++;
151 else if (flowhash_timeout (tm->hash, ei) != 1)
152 clib_warning ("Key not found: %lld\n", tm->keys[i].as_u64[0]);
153 else if (flowhash_value (tm->hash, ei)->as_u64[0] != i + 1)
154 clib_warning ("Value mismatch for key %lld\n",
155 tm->keys[i].as_u64[0]);
156 }
157 }
158
159 delta = clib_time_now (&tm->clib_time) - before;
160 total = tm->nitems * tm->iterations;
161 fformat (stdout, "%lld lookups in %.6f seconds\n", total, delta);
162 if (delta > 0)
163 fformat (stdout, "%.f lookups per second\n", ((f64) total) / delta);
164
165 /* Delete */
166 for (i = 0; i < tm->nitems; i++)
167 {
168 u32 hash = flowhash_hash_8_8 (&tm->keys[i]);
169 u32 ei;
170 flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
171 flowhash_timeout (tm->hash, ei) = 0;
172 }
173
174 fformat (stdout, "%u elements in table\n", flowhash_elts_8_8 (tm->hash, 1));
175
176 vec_free (tm->keys);
177 flowhash_free_8_8 (tm->hash);
178
179 return NULL;
180}
181
182clib_error_t *
183test_flowhash_main (test_main_t * tm)
184{
185 unformat_input_t *i = tm->input;
186 clib_error_t *error;
187
188 while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
189 {
190 if (unformat (i, "seed %u", &tm->seed))
191 ;
192 else if (unformat (i, "fixed-entries %d", &tm->fixed_entries))
193 ;
194 else if (unformat (i, "collision-buckets %d", &tm->collision_buckets))
195 ;
196 else if (unformat (i, "non-random-keys"))
197 tm->non_random_keys = 1;
198 else if (unformat (i, "nitems %d", &tm->nitems))
199 ;
200 else if (unformat (i, "prefetch %d", &tm->prefetch))
201 ;
202 else if (unformat (i, "iterations %d", &tm->iterations))
203 ;
204 else
205 return clib_error_return (0, "unknown input '%U'",
206 format_unformat_error, i);
207 }
208
209 error = test_flowhash (tm);
210 return error;
211}
212
213#ifdef CLIB_UNIX
214int
215main (int argc, char *argv[])
216{
217
218 unformat_input_t i;
219 clib_error_t *error;
220 test_main_t *tm = &test_main;
221
222 clib_mem_init (0, 3ULL << 30);
223
224 tm->fixed_entries = 8 << 20;
225 tm->collision_buckets = 1 << 20;
226 tm->seed = 0xdeadf00l;
227 tm->iterations = 1;
228 tm->input = &i;
229 tm->nitems = 1000;
230 tm->non_random_keys = 0;
231 tm->key_hash = hash_create (0, sizeof (uword));
232 tm->prefetch = 0;
233 clib_time_init (&tm->clib_time);
234
235 unformat_init_command_line (&i, argv);
236 error = test_flowhash_main (tm);
237 unformat_free (&i);
238
239 if (error)
240 {
241 clib_error_report (error);
242 return 1;
243 }
244 return 0;
245
246 return 0;
247}
248#endif /* CLIB_UNIX */
249
250
251/*
252 * fd.io coding-style-patch-verification: ON
253 *
254 * Local Variables:
255 * eval: (c-set-style "gnu")
256 * End:
257 */