]> git.proxmox.com Git - mirror_ovs.git/blame - tests/test-cmap.c
check-kmod: Remove all OVS modules in this target.
[mirror_ovs.git] / tests / test-cmap.c
CommitLineData
0e666160
BP
1/*
2 * Copyright (c) 2008, 2009, 2010, 2013, 2014 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* A non-exhaustive test for some of the functions and macros declared in
18 * cmap.h. */
19
20#include <config.h>
3f636c7e 21#undef NDEBUG
0e666160 22#include "cmap.h"
3f636c7e 23#include <assert.h>
0e666160
BP
24#include <getopt.h>
25#include <string.h>
3f636c7e 26#include "bitmap.h"
0e666160
BP
27#include "command-line.h"
28#include "fat-rwlock.h"
29#include "hash.h"
30#include "hmap.h"
31#include "ovstest.h"
32#include "ovs-thread.h"
33#include "random.h"
34#include "timeval.h"
35#include "util.h"
36
0e666160
BP
37/* Sample cmap element. */
38struct element {
39 int value;
40 struct cmap_node node;
41};
42
43typedef size_t hash_func(int value);
44
45static int
46compare_ints(const void *a_, const void *b_)
47{
48 const int *a = a_;
49 const int *b = b_;
50 return *a < *b ? -1 : *a > *b;
51}
52
53/* Verifies that 'cmap' contains exactly the 'n' values in 'values'. */
54static void
55check_cmap(struct cmap *cmap, const int values[], size_t n,
56 hash_func *hash)
57{
cd50723c 58 int *sort_values, *cmap_values, *cmap_values2;
a532e683 59 const struct element *e;
52a524eb 60 size_t i, batch_size;
0e666160 61
cd50723c
DDP
62 struct cmap_position pos = { 0, 0, 0 };
63 struct cmap_node *node;
64
0e666160
BP
65 /* Check that all the values are there in iteration. */
66 sort_values = xmalloc(sizeof *sort_values * n);
67 cmap_values = xmalloc(sizeof *sort_values * n);
cd50723c 68 cmap_values2 = xmalloc(sizeof *sort_values * n);
0e666160 69
cd50723c 70 /* Here we test cursor iteration */
0e666160 71 i = 0;
a532e683 72 CMAP_FOR_EACH (e, node, cmap) {
0e666160
BP
73 assert(i < n);
74 cmap_values[i++] = e->value;
75 }
76 assert(i == n);
77
cd50723c
DDP
78 /* Here we test iteration with cmap_next_position() */
79 i = 0;
80 while ((node = cmap_next_position(cmap, &pos))) {
d72eff6c
GS
81 struct element *e = NULL;
82 e = OBJECT_CONTAINING(node, e, node);
cd50723c
DDP
83
84 assert(i < n);
85 cmap_values2[i++] = e->value;
86 }
87 assert(i == n);
88
0e666160
BP
89 memcpy(sort_values, values, sizeof *sort_values * n);
90 qsort(sort_values, n, sizeof *sort_values, compare_ints);
91 qsort(cmap_values, n, sizeof *cmap_values, compare_ints);
cd50723c 92 qsort(cmap_values2, n, sizeof *cmap_values2, compare_ints);
0e666160
BP
93
94 for (i = 0; i < n; i++) {
95 assert(sort_values[i] == cmap_values[i]);
cd50723c 96 assert(sort_values[i] == cmap_values2[i]);
0e666160
BP
97 }
98
cd50723c 99 free(cmap_values2);
0e666160
BP
100 free(cmap_values);
101 free(sort_values);
102
103 /* Check that all the values are there in lookup. */
104 for (i = 0; i < n; i++) {
105 size_t count = 0;
106
107 CMAP_FOR_EACH_WITH_HASH (e, node, hash(values[i]), cmap) {
108 count += e->value == values[i];
109 }
110 assert(count == 1);
111 }
112
52a524eb
JR
113 /* Check that all the values are there in batched lookup. */
114 batch_size = n % BITMAP_ULONG_BITS + 1;
115 for (i = 0; i < n; i += batch_size) {
116 unsigned long map;
117 uint32_t hashes[sizeof map * CHAR_BIT];
118 const struct cmap_node *nodes[sizeof map * CHAR_BIT];
119 size_t count = 0;
120 int k, j;
121
122 j = MIN(n - i, batch_size); /* Actual batch size. */
123 map = ~0UL >> (BITMAP_ULONG_BITS - j);
124
125 for (k = 0; k < j; k++) {
126 hashes[k] = hash(values[i + k]);
127 }
128 map = cmap_find_batch(cmap, map, hashes, nodes);
129
3ee6026a 130 ULLONG_FOR_EACH_1(k, map) {
52a524eb
JR
131 struct element *e;
132
133 CMAP_NODE_FOR_EACH (e, node, nodes[k]) {
134 count += e->value == values[i + k];
135 }
136 }
137 assert(count == j); /* j elements in a batch. */
138 }
139
9d933fc7
JR
140 /* Check that cmap_first() returns NULL only when cmap_is_empty(). */
141 assert(!cmap_first(cmap) == cmap_is_empty(cmap));
142
0e666160
BP
143 /* Check counters. */
144 assert(cmap_is_empty(cmap) == !n);
145 assert(cmap_count(cmap) == n);
146}
147
148static void
149shuffle(int *p, size_t n)
150{
151 for (; n > 1; n--, p++) {
152 int *q = &p[random_range(n)];
153 int tmp = *p;
154 *p = *q;
155 *q = tmp;
156 }
157}
158
159/* Prints the values in 'cmap', plus 'name' as a title. */
160static void OVS_UNUSED
161print_cmap(const char *name, struct cmap *cmap)
162{
163 struct cmap_cursor cursor;
164 struct element *e;
165
166 printf("%s:", name);
a532e683 167 CMAP_CURSOR_FOR_EACH (e, node, &cursor, cmap) {
0e666160
BP
168 printf(" %d", e->value);
169 }
170 printf("\n");
171}
172
173/* Prints the 'n' values in 'values', plus 'name' as a title. */
174static void OVS_UNUSED
175print_ints(const char *name, const int *values, size_t n)
176{
177 size_t i;
178
179 printf("%s:", name);
180 for (i = 0; i < n; i++) {
181 printf(" %d", values[i]);
182 }
183 printf("\n");
184}
185
186static size_t
187identity_hash(int value)
188{
189 return value;
190}
191
192static size_t
193good_hash(int value)
194{
195 return hash_int(value, 0x1234abcd);
196}
197
198static size_t
199constant_hash(int value OVS_UNUSED)
200{
201 return 123;
202}
203
204/* Tests basic cmap insertion and deletion. */
205static void
9d933fc7 206test_cmap_insert_replace_delete(hash_func *hash)
0e666160
BP
207{
208 enum { N_ELEMS = 1000 };
209
210 struct element elements[N_ELEMS];
9d933fc7 211 struct element copies[N_ELEMS];
0e666160
BP
212 int values[N_ELEMS];
213 struct cmap cmap;
214 size_t i;
215
216 cmap_init(&cmap);
217 for (i = 0; i < N_ELEMS; i++) {
218 elements[i].value = i;
219 cmap_insert(&cmap, &elements[i].node, hash(i));
220 values[i] = i;
221 check_cmap(&cmap, values, i + 1, hash);
222 }
223 shuffle(values, N_ELEMS);
224 for (i = 0; i < N_ELEMS; i++) {
9d933fc7
JR
225 copies[values[i]].value = values[i];
226 cmap_replace(&cmap, &elements[values[i]].node,
227 &copies[values[i]].node, hash(values[i]));
228 check_cmap(&cmap, values, N_ELEMS, hash);
229 }
230 shuffle(values, N_ELEMS);
231 for (i = 0; i < N_ELEMS; i++) {
232 cmap_remove(&cmap, &copies[values[i]].node, hash(values[i]));
0e666160
BP
233 check_cmap(&cmap, values + (i + 1), N_ELEMS - (i + 1), hash);
234 }
235 cmap_destroy(&cmap);
236}
237
238static void
239run_test(void (*function)(hash_func *))
240{
241 hash_func *hash_funcs[] = { identity_hash, good_hash, constant_hash };
242 size_t i;
243
244 for (i = 0; i < ARRAY_SIZE(hash_funcs); i++) {
245 function(hash_funcs[i]);
246 printf(".");
247 fflush(stdout);
248 }
249}
250
251static void
1636c761 252run_tests(struct ovs_cmdl_context *ctx)
0e666160
BP
253{
254 int n;
255 int i;
256
1636c761 257 n = ctx->argc >= 2 ? atoi(ctx->argv[1]) : 100;
0e666160 258 for (i = 0; i < n; i++) {
9d933fc7 259 run_test(test_cmap_insert_replace_delete);
0e666160
BP
260 }
261 printf("\n");
262}
263\f
264static int n_elems; /* Number of elements to insert. */
265static int n_threads; /* Number of threads to search and mutate. */
266static uint32_t mutation_frac; /* % mutations, as fraction of UINT32_MAX. */
52a524eb
JR
267static int n_batch; /* Number of elements in each batch. */
268
269#define N_BATCH_MAX BITMAP_ULONG_BITS
0e666160
BP
270
271static void benchmark_cmap(void);
52a524eb 272static void benchmark_cmap_batched(void);
0e666160
BP
273static void benchmark_hmap(void);
274
275static int
276elapsed(const struct timeval *start)
277{
278 struct timeval end;
279
280 xgettimeofday(&end);
281 return timeval_to_msec(&end) - timeval_to_msec(start);
282}
283
284static void
1636c761 285run_benchmarks(struct ovs_cmdl_context *ctx)
0e666160 286{
1636c761
RB
287 n_elems = strtol(ctx->argv[1], NULL, 10);
288 n_threads = strtol(ctx->argv[2], NULL, 10);
289 mutation_frac = strtod(ctx->argv[3], NULL) / 100.0 * UINT32_MAX;
290 n_batch = ctx->argc > 4 ? strtol(ctx->argv[4], NULL, 10) : 1;
0e666160 291
52a524eb
JR
292 if (n_batch > N_BATCH_MAX) {
293 n_batch = N_BATCH_MAX;
294 }
295 printf("Benchmarking with n=%d, %d threads, %.2f%% mutations, batch size %d:\n",
296 n_elems, n_threads, (double) mutation_frac / UINT32_MAX * 100.,
297 n_batch);
0e666160 298
52a524eb
JR
299 if (n_batch > 0) {
300 benchmark_cmap_batched();
301 }
302 putchar('\n');
0e666160
BP
303 benchmark_cmap();
304 putchar('\n');
305 benchmark_hmap();
306}
307\f
308/* cmap benchmark. */
309
310static struct element *
311find(const struct cmap *cmap, int value)
312{
313 struct element *e;
314
315 CMAP_FOR_EACH_WITH_HASH (e, node, hash_int(value, 0), cmap) {
316 if (e->value == value) {
317 return e;
318 }
319 }
320 return NULL;
321}
322
323struct cmap_aux {
324 struct ovs_mutex mutex;
325 struct cmap *cmap;
326};
327
328static void *
329search_cmap(void *aux_)
330{
331 struct cmap_aux *aux = aux_;
332 size_t i;
333
44a48152
JR
334 if (mutation_frac) {
335 for (i = 0; i < n_elems; i++) {
336 struct element *e;
0e666160 337
44a48152
JR
338 if (random_uint32() < mutation_frac) {
339 ovs_mutex_lock(&aux->mutex);
340 e = find(aux->cmap, i);
341 if (e) {
342 cmap_remove(aux->cmap, &e->node, hash_int(e->value, 0));
343 }
344 ovs_mutex_unlock(&aux->mutex);
345 } else {
346 ignore(find(aux->cmap, i));
0e666160 347 }
44a48152
JR
348 }
349 } else {
350 for (i = 0; i < n_elems; i++) {
0e666160
BP
351 ignore(find(aux->cmap, i));
352 }
353 }
354 return NULL;
355}
356
357static void
358benchmark_cmap(void)
359{
360 struct element *elements;
361 struct cmap cmap;
362 struct element *e;
0e666160
BP
363 struct timeval start;
364 pthread_t *threads;
365 struct cmap_aux aux;
366 size_t i;
367
368 elements = xmalloc(n_elems * sizeof *elements);
369
370 /* Insertions. */
371 xgettimeofday(&start);
372 cmap_init(&cmap);
373 for (i = 0; i < n_elems; i++) {
374 elements[i].value = i;
375 cmap_insert(&cmap, &elements[i].node, hash_int(i, 0));
376 }
377 printf("cmap insert: %5d ms\n", elapsed(&start));
378
379 /* Iteration. */
380 xgettimeofday(&start);
a532e683 381 CMAP_FOR_EACH (e, node, &cmap) {
0e666160
BP
382 ignore(e);
383 }
384 printf("cmap iterate: %5d ms\n", elapsed(&start));
385
386 /* Search and mutation. */
387 xgettimeofday(&start);
388 aux.cmap = &cmap;
389 ovs_mutex_init(&aux.mutex);
390 threads = xmalloc(n_threads * sizeof *threads);
391 for (i = 0; i < n_threads; i++) {
392 threads[i] = ovs_thread_create("search", search_cmap, &aux);
393 }
394 for (i = 0; i < n_threads; i++) {
395 xpthread_join(threads[i], NULL);
396 }
397 free(threads);
398 printf("cmap search: %5d ms\n", elapsed(&start));
399
400 /* Destruction. */
401 xgettimeofday(&start);
a532e683 402 CMAP_FOR_EACH (e, node, &cmap) {
0e666160
BP
403 cmap_remove(&cmap, &e->node, hash_int(e->value, 0));
404 }
405 cmap_destroy(&cmap);
406 printf("cmap destroy: %5d ms\n", elapsed(&start));
407
408 free(elements);
409}
52a524eb
JR
410
411static size_t
412find_batch(const struct cmap *cmap, const int value)
413{
414 size_t i, ret;
415 const size_t end = MIN(n_batch, n_elems - value);
416 unsigned long map = ~0;
417 uint32_t hashes[N_BATCH_MAX];
418 const struct cmap_node *nodes[N_BATCH_MAX];
419
420 if (mutation_frac) {
421 for (i = 0; i < end; i++) {
422 if (random_uint32() < mutation_frac) {
423 break;
424 }
425 hashes[i] = hash_int(value + i, 0);
426 }
427 } else {
428 for (i = 0; i < end; i++) {
429 hashes[i] = hash_int(value + i, 0);
430 }
431 }
432
433 ret = i;
434
435 map >>= BITMAP_ULONG_BITS - i; /* Clear excess bits. */
436 map = cmap_find_batch(cmap, map, hashes, nodes);
437
3ee6026a 438 ULLONG_FOR_EACH_1(i, map) {
52a524eb
JR
439 struct element *e;
440
441 CMAP_NODE_FOR_EACH (e, node, nodes[i]) {
442 if (OVS_LIKELY(e->value == value + i)) {
443 ignore(e); /* Found result. */
444 break;
445 }
446 }
447 }
448 return ret;
449}
450
451static void *
452search_cmap_batched(void *aux_)
453{
454 struct cmap_aux *aux = aux_;
455 size_t i = 0, j;
456
457 for (;;) {
458 struct element *e;
459
460 j = find_batch(aux->cmap, i);
461 i += j;
462 if (i >= n_elems) {
463 break;
464 }
465 if (j < n_batch) {
466 ovs_mutex_lock(&aux->mutex);
467 e = find(aux->cmap, i);
468 if (e) {
469 cmap_remove(aux->cmap, &e->node, hash_int(e->value, 0));
470 }
471 ovs_mutex_unlock(&aux->mutex);
472 }
473 }
474
475 return NULL;
476}
477
478static void
479benchmark_cmap_batched(void)
480{
481 struct element *elements;
482 struct cmap cmap;
483 struct element *e;
484 struct timeval start;
485 pthread_t *threads;
486 struct cmap_aux aux;
487 size_t i;
488
489 elements = xmalloc(n_elems * sizeof *elements);
490
491 /* Insertions. */
492 xgettimeofday(&start);
493 cmap_init(&cmap);
494 for (i = 0; i < n_elems; i++) {
495 elements[i].value = i;
496 cmap_insert(&cmap, &elements[i].node, hash_int(i, 0));
497 }
498 printf("cmap insert: %5d ms\n", elapsed(&start));
499
500 /* Iteration. */
501 xgettimeofday(&start);
502 CMAP_FOR_EACH (e, node, &cmap) {
503 ignore(e);
504 }
505 printf("cmap iterate: %5d ms\n", elapsed(&start));
506
507 /* Search and mutation. */
508 xgettimeofday(&start);
509 aux.cmap = &cmap;
510 ovs_mutex_init(&aux.mutex);
511 threads = xmalloc(n_threads * sizeof *threads);
512 for (i = 0; i < n_threads; i++) {
513 threads[i] = ovs_thread_create("search", search_cmap_batched, &aux);
514 }
515 for (i = 0; i < n_threads; i++) {
516 xpthread_join(threads[i], NULL);
517 }
518 free(threads);
519 printf("batch search: %5d ms\n", elapsed(&start));
520
521 /* Destruction. */
522 xgettimeofday(&start);
523 CMAP_FOR_EACH (e, node, &cmap) {
524 cmap_remove(&cmap, &e->node, hash_int(e->value, 0));
525 }
526 cmap_destroy(&cmap);
527 printf("cmap destroy: %5d ms\n", elapsed(&start));
528
529 free(elements);
530}
0e666160
BP
531\f
532/* hmap benchmark. */
533struct helement {
534 int value;
535 struct hmap_node node;
536};
537
538static struct helement *
539hfind(const struct hmap *hmap, int value)
540{
541 struct helement *e;
542
543 HMAP_FOR_EACH_WITH_HASH (e, node, hash_int(value, 0), hmap) {
544 if (e->value == value) {
545 return e;
546 }
547 }
548 return NULL;
549}
550
551struct hmap_aux {
552 struct hmap *hmap;
553 struct fat_rwlock fatlock;
554};
555
556static void *
557search_hmap(void *aux_)
558{
559 struct hmap_aux *aux = aux_;
560 size_t i;
561
44a48152
JR
562 if (mutation_frac) {
563 for (i = 0; i < n_elems; i++) {
0e666160
BP
564 if (random_uint32() < mutation_frac) {
565 struct helement *e;
566
567 fat_rwlock_wrlock(&aux->fatlock);
568 e = hfind(aux->hmap, i);
569 if (e) {
570 hmap_remove(aux->hmap, &e->node);
571 }
572 fat_rwlock_unlock(&aux->fatlock);
573 } else {
574 fat_rwlock_rdlock(&aux->fatlock);
575 ignore(hfind(aux->hmap, i));
576 fat_rwlock_unlock(&aux->fatlock);
577 }
44a48152
JR
578 }
579 } else {
580 for (i = 0; i < n_elems; i++) {
581 ignore(hfind(aux->hmap, i));
0e666160
BP
582 }
583 }
584 return NULL;
585}
586
587static void
588benchmark_hmap(void)
589{
590 struct helement *elements;
591 struct hmap hmap;
592 struct helement *e, *next;
593 struct timeval start;
594 pthread_t *threads;
595 struct hmap_aux aux;
596 size_t i;
597
598 elements = xmalloc(n_elems * sizeof *elements);
599
600 xgettimeofday(&start);
601 hmap_init(&hmap);
602 for (i = 0; i < n_elems; i++) {
603 elements[i].value = i;
604 hmap_insert(&hmap, &elements[i].node, hash_int(i, 0));
605 }
606
607 printf("hmap insert: %5d ms\n", elapsed(&start));
608
609 xgettimeofday(&start);
610 HMAP_FOR_EACH (e, node, &hmap) {
611 ignore(e);
612 }
613 printf("hmap iterate: %5d ms\n", elapsed(&start));
614
615 xgettimeofday(&start);
616 aux.hmap = &hmap;
617 fat_rwlock_init(&aux.fatlock);
618 threads = xmalloc(n_threads * sizeof *threads);
619 for (i = 0; i < n_threads; i++) {
620 threads[i] = ovs_thread_create("search", search_hmap, &aux);
621 }
622 for (i = 0; i < n_threads; i++) {
623 xpthread_join(threads[i], NULL);
624 }
625 free(threads);
626 printf("hmap search: %5d ms\n", elapsed(&start));
627
628 /* Destruction. */
629 xgettimeofday(&start);
630 HMAP_FOR_EACH_SAFE (e, next, node, &hmap) {
631 hmap_remove(&hmap, &e->node);
632 }
633 hmap_destroy(&hmap);
634 printf("hmap destroy: %5d ms\n", elapsed(&start));
635
636 free(elements);
637}
638\f
5f383751 639static const struct ovs_cmdl_command commands[] = {
451de37e
AW
640 {"check", NULL, 0, 1, run_tests},
641 {"benchmark", NULL, 3, 4, run_benchmarks},
642 {NULL, NULL, 0, 0, NULL},
0e666160
BP
643};
644
645static void
1636c761 646test_cmap_main(int argc, char *argv[])
0e666160 647{
1636c761
RB
648 struct ovs_cmdl_context ctx = {
649 .argc = argc - optind,
650 .argv = argv + optind,
651 };
652
0e666160 653 set_program_name(argv[0]);
1636c761 654 ovs_cmdl_run_command(&ctx, commands);
0e666160
BP
655}
656
657OVSTEST_REGISTER("test-cmap", test_cmap_main);