1 /* Simple speed tests for hashtables. */
2 #include <ccan/htable/htable_type.h>
3 #include <ccan/htable/htable.c>
4 #include <ccan/hash/hash.h>
11 static size_t hashcount;
16 /* Some contents. Doubles as consistency check. */
20 static const unsigned int *objkey(const struct object *obj)
25 static size_t hash_obj(const unsigned int *key)
28 return hashl(key, 1, 0);
31 static bool cmp(const unsigned int *key1, const unsigned int *key2)
33 return *key1 == *key2;
36 HTABLE_DEFINE_TYPE(struct object, objkey, hash_obj, cmp, obj);
38 static unsigned int popcount(unsigned long val)
40 #if HAVE_BUILTIN_POPCOUNTL
41 return __builtin_popcountl(val);
43 if (sizeof(long) == sizeof(u64)) {
45 v = (v & 0x5555555555555555ULL)
46 + ((v >> 1) & 0x5555555555555555ULL);
47 v = (v & 0x3333333333333333ULL)
48 + ((v >> 1) & 0x3333333333333333ULL);
49 v = (v & 0x0F0F0F0F0F0F0F0FULL)
50 + ((v >> 1) & 0x0F0F0F0F0F0F0F0FULL);
51 v = (v & 0x00FF00FF00FF00FFULL)
52 + ((v >> 1) & 0x00FF00FF00FF00FFULL);
53 v = (v & 0x0000FFFF0000FFFFULL)
54 + ((v >> 1) & 0x0000FFFF0000FFFFULL);
55 v = (v & 0x00000000FFFFFFFFULL)
56 + ((v >> 1) & 0x00000000FFFFFFFFULL);
59 val = (val & 0x55555555ULL) + ((val >> 1) & 0x55555555ULL);
60 val = (val & 0x33333333ULL) + ((val >> 1) & 0x33333333ULL);
61 val = (val & 0x0F0F0F0FULL) + ((val >> 1) & 0x0F0F0F0FULL);
62 val = (val & 0x00FF00FFULL) + ((val >> 1) & 0x00FF00FFULL);
63 val = (val & 0x0000FFFFULL) + ((val >> 1) & 0x0000FFFFULL);
68 static size_t perfect(const struct htable *ht)
70 size_t i, placed_perfect = 0;
72 for (i = 0; i < ((size_t)1 << ht->bits); i++) {
73 if (!entry_is_valid(ht->table[i]))
75 if (hash_bucket(ht, ht->rehash(get_raw_ptr(ht, ht->table[i]),
79 return placed_perfect;
82 static size_t count_deleted(const struct htable *ht)
84 size_t i, delete_markers = 0;
86 for (i = 0; i < ((size_t)1 << ht->bits); i++) {
87 if (ht->table[i] == HTABLE_DELETED)
90 return delete_markers;
93 /* Nanoseconds per operation */
94 static size_t normalize(const struct timeval *start,
95 const struct timeval *stop,
100 timersub(stop, start, &diff);
102 /* Floating point is more accurate here. */
103 return (double)(diff.tv_sec * 1000000 + diff.tv_usec)
107 static size_t worst_run(struct htable *ht, size_t *deleted)
109 size_t longest = 0, len = 0, this_del = 0, i;
112 /* This doesn't take into account end-wrap, but gives an idea. */
113 for (i = 0; i < ((size_t)1 << ht->bits); i++) {
116 if (ht->table[i] == HTABLE_DELETED)
130 int main(int argc, char *argv[])
133 size_t i, j, num, deleted;
134 struct timeval start, stop;
135 struct htable_obj *ht;
137 bool make_dumb = false;
139 if (argv[1] && strcmp(argv[1], "--dumb") == 0) {
143 num = argv[1] ? atoi(argv[1]) : 1000000;
144 objs = calloc(num, sizeof(objs[0]));
146 for (i = 0; i < num; i++) {
148 objs[i].self = &objs[i];
151 ht = htable_obj_new();
154 printf("Initial insert: ");
156 gettimeofday(&start, NULL);
157 for (i = 0; i < num; i++)
158 htable_obj_add(ht, objs[i].self);
159 gettimeofday(&stop, NULL);
160 printf(" %zu ns\n", normalize(&start, &stop, num));
161 printf("Details: hash size %u, mask bits %u, perfect %.0f%%\n",
162 1U << htr->bits, popcount(htr->common_mask),
163 perfect(htr) * 100.0 / htr->elems);
166 /* Screw with mask, to hobble us. */
167 update_common(htr, (void *)~htr->common_bits);
168 printf("Details: DUMB MODE: mask bits %u\n",
169 popcount(htr->common_mask));
172 printf("Initial lookup (match): ");
174 gettimeofday(&start, NULL);
175 for (i = 0; i < num; i++)
176 if (htable_obj_get(ht, &i)->self != objs[i].self)
178 gettimeofday(&stop, NULL);
179 printf(" %zu ns\n", normalize(&start, &stop, num));
181 printf("Initial lookup (miss): ");
183 gettimeofday(&start, NULL);
184 for (i = 0; i < num; i++) {
185 unsigned int n = i + num;
186 if (htable_obj_get(ht, &n))
189 gettimeofday(&stop, NULL);
190 printf(" %zu ns\n", normalize(&start, &stop, num));
192 /* Lookups in order are very cache-friendly for judy; try random */
193 printf("Initial lookup (random): ");
195 gettimeofday(&start, NULL);
196 for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num)
197 if (htable_obj_get(ht, &j)->self != &objs[j])
199 gettimeofday(&stop, NULL);
200 printf(" %zu ns\n", normalize(&start, &stop, num));
203 printf("Initial delete all: ");
205 gettimeofday(&start, NULL);
206 for (i = 0; i < num; i++)
207 if (!htable_obj_del(ht, objs[i].self))
209 gettimeofday(&stop, NULL);
210 printf(" %zu ns\n", normalize(&start, &stop, num));
211 printf("Details: rehashes %zu\n", hashcount);
213 printf("Initial re-inserting: ");
215 gettimeofday(&start, NULL);
216 for (i = 0; i < num; i++)
217 htable_obj_add(ht, objs[i].self);
218 gettimeofday(&stop, NULL);
219 printf(" %zu ns\n", normalize(&start, &stop, num));
222 printf("Deleting first half: ");
224 gettimeofday(&start, NULL);
225 for (i = 0; i < num; i+=2)
226 if (!htable_obj_del(ht, objs[i].self))
228 gettimeofday(&stop, NULL);
229 printf(" %zu ns\n", normalize(&start, &stop, num));
231 printf("Details: rehashes %zu, delete markers %zu\n",
232 hashcount, count_deleted(htr));
234 printf("Adding (a different) half: ");
237 for (i = 0; i < num; i+=2)
240 gettimeofday(&start, NULL);
241 for (i = 0; i < num; i+=2)
242 htable_obj_add(ht, objs[i].self);
243 gettimeofday(&stop, NULL);
244 printf(" %zu ns\n", normalize(&start, &stop, num));
246 printf("Details: delete markers %zu, perfect %.0f%%\n",
247 count_deleted(htr), perfect(htr) * 100.0 / htr->elems);
249 printf("Lookup after half-change (match): ");
251 gettimeofday(&start, NULL);
252 for (i = 1; i < num; i+=2)
253 if (htable_obj_get(ht, &i)->self != objs[i].self)
255 for (i = 0; i < num; i+=2) {
256 unsigned int n = i + num;
257 if (htable_obj_get(ht, &n)->self != objs[i].self)
260 gettimeofday(&stop, NULL);
261 printf(" %zu ns\n", normalize(&start, &stop, num));
263 printf("Lookup after half-change (miss): ");
265 gettimeofday(&start, NULL);
266 for (i = 0; i < num; i++) {
267 unsigned int n = i + num * 2;
268 if (htable_obj_get(ht, &n))
271 gettimeofday(&stop, NULL);
272 printf(" %zu ns\n", normalize(&start, &stop, num));
274 /* Hashtables with delete markers can fill with markers over time.
275 * so do some changes to see how it operates in long-term. */
276 for (i = 0; i < 5; i++) {
278 /* We don't measure this: jmap is different. */
279 printf("Details: initial churn\n");
281 printf("Churning %s time: ",
288 gettimeofday(&start, NULL);
289 for (j = 0; j < num; j++) {
290 if (!htable_obj_del(ht, &objs[j]))
292 objs[j].key = num*i+j;
293 if (!htable_obj_add(ht, &objs[j]))
296 gettimeofday(&stop, NULL);
298 printf(" %zu ns\n", normalize(&start, &stop, num));
301 /* Spread out the keys more to try to make it harder. */
302 printf("Details: reinserting with spread\n");
303 for (i = 0; i < num; i++) {
304 if (!htable_obj_del(ht, objs[i].self))
306 objs[i].key = num * 5 + i * 9;
307 if (!htable_obj_add(ht, objs[i].self))
310 printf("Details: delete markers %zu, perfect %.0f%%\n",
311 count_deleted(htr), perfect(htr) * 100.0 / htr->elems);
312 i = worst_run(htr, &deleted);
313 printf("Details: worst run %zu (%zu deleted)\n", i, deleted);
315 printf("Lookup after churn & spread (match): ");
317 gettimeofday(&start, NULL);
318 for (i = 0; i < num; i++) {
319 unsigned int n = num * 5 + i * 9;
320 if (htable_obj_get(ht, &n)->self != objs[i].self)
323 gettimeofday(&stop, NULL);
324 printf(" %zu ns\n", normalize(&start, &stop, num));
326 printf("Lookup after churn & spread (miss): ");
328 gettimeofday(&start, NULL);
329 for (i = 0; i < num; i++) {
330 unsigned int n = num * 6 + i * 9;
331 if (htable_obj_get(ht, &n))
334 gettimeofday(&stop, NULL);
335 printf(" %zu ns\n", normalize(&start, &stop, num));
337 printf("Lookup after churn & spread (random): ");
339 gettimeofday(&start, NULL);
340 for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num) {
341 unsigned int n = num * 5 + j * 9;
342 if (htable_obj_get(ht, &n)->self != &objs[j])
345 gettimeofday(&stop, NULL);
346 printf(" %zu ns\n", normalize(&start, &stop, num));
349 printf("Deleting half after churn & spread: ");
351 gettimeofday(&start, NULL);
352 for (i = 0; i < num; i+=2)
353 if (!htable_obj_del(ht, objs[i].self))
355 gettimeofday(&stop, NULL);
356 printf(" %zu ns\n", normalize(&start, &stop, num));
358 printf("Adding (a different) half after churn & spread: ");
361 for (i = 0; i < num; i+=2)
362 objs[i].key = num*6+i*9;
364 gettimeofday(&start, NULL);
365 for (i = 0; i < num; i+=2)
366 htable_obj_add(ht, objs[i].self);
367 gettimeofday(&stop, NULL);
368 printf(" %zu ns\n", normalize(&start, &stop, num));
370 printf("Details: delete markers %zu, perfect %.0f%%\n",
371 count_deleted(htr), perfect(htr) * 100.0 / htr->elems);