2 * Copyright (c) 2008, 2009, 2010, 2013, 2014 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /* A non-exhaustive test for some of the functions and macros declared in
25 #include "command-line.h"
26 #include "fat-rwlock.h"
30 #include "ovs-thread.h"
38 /* Sample cmap element. */
41 struct cmap_node node;
44 typedef size_t hash_func(int value);
47 compare_ints(const void *a_, const void *b_)
51 return *a < *b ? -1 : *a > *b;
54 /* Verifies that 'cmap' contains exactly the 'n' values in 'values'. */
56 check_cmap(struct cmap *cmap, const int values[], size_t n,
59 int *sort_values, *cmap_values, *cmap_values2;
60 const struct element *e;
63 struct cmap_position pos = { 0, 0, 0 };
64 struct cmap_node *node;
66 /* Check that all the values are there in iteration. */
67 sort_values = xmalloc(sizeof *sort_values * n);
68 cmap_values = xmalloc(sizeof *sort_values * n);
69 cmap_values2 = xmalloc(sizeof *sort_values * n);
71 /* Here we test cursor iteration */
73 CMAP_FOR_EACH (e, node, cmap) {
75 cmap_values[i++] = e->value;
79 /* Here we test iteration with cmap_next_position() */
81 while ((node = cmap_next_position(cmap, &pos))) {
82 struct element *e = NULL;
83 e = OBJECT_CONTAINING(node, e, node);
86 cmap_values2[i++] = e->value;
90 memcpy(sort_values, values, sizeof *sort_values * n);
91 qsort(sort_values, n, sizeof *sort_values, compare_ints);
92 qsort(cmap_values, n, sizeof *cmap_values, compare_ints);
93 qsort(cmap_values2, n, sizeof *cmap_values2, compare_ints);
95 for (i = 0; i < n; i++) {
96 assert(sort_values[i] == cmap_values[i]);
97 assert(sort_values[i] == cmap_values2[i]);
104 /* Check that all the values are there in lookup. */
105 for (i = 0; i < n; i++) {
108 CMAP_FOR_EACH_WITH_HASH (e, node, hash(values[i]), cmap) {
109 count += e->value == values[i];
114 /* Check that all the values are there in batched lookup. */
115 batch_size = n % BITMAP_ULONG_BITS + 1;
116 for (i = 0; i < n; i += batch_size) {
118 uint32_t hashes[sizeof map * CHAR_BIT];
119 const struct cmap_node *nodes[sizeof map * CHAR_BIT];
123 j = MIN(n - i, batch_size); /* Actual batch size. */
124 map = ~0UL >> (BITMAP_ULONG_BITS - j);
126 for (k = 0; k < j; k++) {
127 hashes[k] = hash(values[i + k]);
129 map = cmap_find_batch(cmap, map, hashes, nodes);
131 ULONG_FOR_EACH_1(k, map) {
134 CMAP_NODE_FOR_EACH (e, node, nodes[k]) {
135 count += e->value == values[i + k];
138 assert(count == j); /* j elements in a batch. */
141 /* Check that cmap_first() returns NULL only when cmap_is_empty(). */
142 assert(!cmap_first(cmap) == cmap_is_empty(cmap));
144 /* Check counters. */
145 assert(cmap_is_empty(cmap) == !n);
146 assert(cmap_count(cmap) == n);
150 shuffle(int *p, size_t n)
152 for (; n > 1; n--, p++) {
153 int *q = &p[random_range(n)];
160 /* Prints the values in 'cmap', plus 'name' as a title. */
161 static void OVS_UNUSED
162 print_cmap(const char *name, struct cmap *cmap)
164 struct cmap_cursor cursor;
168 CMAP_CURSOR_FOR_EACH (e, node, &cursor, cmap) {
169 printf(" %d", e->value);
174 /* Prints the 'n' values in 'values', plus 'name' as a title. */
175 static void OVS_UNUSED
176 print_ints(const char *name, const int *values, size_t n)
181 for (i = 0; i < n; i++) {
182 printf(" %d", values[i]);
188 identity_hash(int value)
196 return hash_int(value, 0x1234abcd);
200 constant_hash(int value OVS_UNUSED)
205 /* Tests basic cmap insertion and deletion. */
207 test_cmap_insert_replace_delete(hash_func *hash)
209 enum { N_ELEMS = 1000 };
211 struct element elements[N_ELEMS];
212 struct element copies[N_ELEMS];
218 for (i = 0; i < N_ELEMS; i++) {
219 elements[i].value = i;
220 cmap_insert(&cmap, &elements[i].node, hash(i));
222 check_cmap(&cmap, values, i + 1, hash);
224 shuffle(values, N_ELEMS);
225 for (i = 0; i < N_ELEMS; i++) {
226 copies[values[i]].value = values[i];
227 cmap_replace(&cmap, &elements[values[i]].node,
228 &copies[values[i]].node, hash(values[i]));
229 check_cmap(&cmap, values, N_ELEMS, hash);
231 shuffle(values, N_ELEMS);
232 for (i = 0; i < N_ELEMS; i++) {
233 cmap_remove(&cmap, &copies[values[i]].node, hash(values[i]));
234 check_cmap(&cmap, values + (i + 1), N_ELEMS - (i + 1), hash);
240 run_test(void (*function)(hash_func *))
242 hash_func *hash_funcs[] = { identity_hash, good_hash, constant_hash };
245 for (i = 0; i < ARRAY_SIZE(hash_funcs); i++) {
246 function(hash_funcs[i]);
253 run_tests(int argc, char *argv[])
258 n = argc >= 2 ? atoi(argv[1]) : 100;
259 for (i = 0; i < n; i++) {
260 run_test(test_cmap_insert_replace_delete);
265 static int n_elems; /* Number of elements to insert. */
266 static int n_threads; /* Number of threads to search and mutate. */
267 static uint32_t mutation_frac; /* % mutations, as fraction of UINT32_MAX. */
268 static int n_batch; /* Number of elements in each batch. */
270 #define N_BATCH_MAX BITMAP_ULONG_BITS
272 static void benchmark_cmap(void);
273 static void benchmark_cmap_batched(void);
274 static void benchmark_hmap(void);
277 elapsed(const struct timeval *start)
282 return timeval_to_msec(&end) - timeval_to_msec(start);
286 run_benchmarks(int argc, char *argv[] OVS_UNUSED)
288 n_elems = strtol(argv[1], NULL, 10);
289 n_threads = strtol(argv[2], NULL, 10);
290 mutation_frac = strtod(argv[3], NULL) / 100.0 * UINT32_MAX;
291 n_batch = argc > 4 ? strtol(argv[4], NULL, 10) : 1;
293 if (n_batch > N_BATCH_MAX) {
294 n_batch = N_BATCH_MAX;
296 printf("Benchmarking with n=%d, %d threads, %.2f%% mutations, batch size %d:\n",
297 n_elems, n_threads, (double) mutation_frac / UINT32_MAX * 100.,
301 benchmark_cmap_batched();
309 /* cmap benchmark. */
311 static struct element *
312 find(const struct cmap *cmap, int value)
316 CMAP_FOR_EACH_WITH_HASH (e, node, hash_int(value, 0), cmap) {
317 if (e->value == value) {
325 struct ovs_mutex mutex;
330 search_cmap(void *aux_)
332 struct cmap_aux *aux = aux_;
336 for (i = 0; i < n_elems; i++) {
339 if (random_uint32() < mutation_frac) {
340 ovs_mutex_lock(&aux->mutex);
341 e = find(aux->cmap, i);
343 cmap_remove(aux->cmap, &e->node, hash_int(e->value, 0));
345 ovs_mutex_unlock(&aux->mutex);
347 ignore(find(aux->cmap, i));
351 for (i = 0; i < n_elems; i++) {
352 ignore(find(aux->cmap, i));
361 struct element *elements;
364 struct timeval start;
369 elements = xmalloc(n_elems * sizeof *elements);
372 xgettimeofday(&start);
374 for (i = 0; i < n_elems; i++) {
375 elements[i].value = i;
376 cmap_insert(&cmap, &elements[i].node, hash_int(i, 0));
378 printf("cmap insert: %5d ms\n", elapsed(&start));
381 xgettimeofday(&start);
382 CMAP_FOR_EACH (e, node, &cmap) {
385 printf("cmap iterate: %5d ms\n", elapsed(&start));
387 /* Search and mutation. */
388 xgettimeofday(&start);
390 ovs_mutex_init(&aux.mutex);
391 threads = xmalloc(n_threads * sizeof *threads);
392 for (i = 0; i < n_threads; i++) {
393 threads[i] = ovs_thread_create("search", search_cmap, &aux);
395 for (i = 0; i < n_threads; i++) {
396 xpthread_join(threads[i], NULL);
399 printf("cmap search: %5d ms\n", elapsed(&start));
402 xgettimeofday(&start);
403 CMAP_FOR_EACH (e, node, &cmap) {
404 cmap_remove(&cmap, &e->node, hash_int(e->value, 0));
407 printf("cmap destroy: %5d ms\n", elapsed(&start));
413 find_batch(const struct cmap *cmap, const int value)
416 const size_t end = MIN(n_batch, n_elems - value);
417 unsigned long map = ~0;
418 uint32_t hashes[N_BATCH_MAX];
419 const struct cmap_node *nodes[N_BATCH_MAX];
422 for (i = 0; i < end; i++) {
423 if (random_uint32() < mutation_frac) {
426 hashes[i] = hash_int(value + i, 0);
429 for (i = 0; i < end; i++) {
430 hashes[i] = hash_int(value + i, 0);
436 map >>= BITMAP_ULONG_BITS - i; /* Clear excess bits. */
437 map = cmap_find_batch(cmap, map, hashes, nodes);
439 ULONG_FOR_EACH_1(i, map) {
442 CMAP_NODE_FOR_EACH (e, node, nodes[i]) {
443 if (OVS_LIKELY(e->value == value + i)) {
444 ignore(e); /* Found result. */
453 search_cmap_batched(void *aux_)
455 struct cmap_aux *aux = aux_;
461 j = find_batch(aux->cmap, i);
467 ovs_mutex_lock(&aux->mutex);
468 e = find(aux->cmap, i);
470 cmap_remove(aux->cmap, &e->node, hash_int(e->value, 0));
472 ovs_mutex_unlock(&aux->mutex);
480 benchmark_cmap_batched(void)
482 struct element *elements;
485 struct timeval start;
490 elements = xmalloc(n_elems * sizeof *elements);
493 xgettimeofday(&start);
495 for (i = 0; i < n_elems; i++) {
496 elements[i].value = i;
497 cmap_insert(&cmap, &elements[i].node, hash_int(i, 0));
499 printf("cmap insert: %5d ms\n", elapsed(&start));
502 xgettimeofday(&start);
503 CMAP_FOR_EACH (e, node, &cmap) {
506 printf("cmap iterate: %5d ms\n", elapsed(&start));
508 /* Search and mutation. */
509 xgettimeofday(&start);
511 ovs_mutex_init(&aux.mutex);
512 threads = xmalloc(n_threads * sizeof *threads);
513 for (i = 0; i < n_threads; i++) {
514 threads[i] = ovs_thread_create("search", search_cmap_batched, &aux);
516 for (i = 0; i < n_threads; i++) {
517 xpthread_join(threads[i], NULL);
520 printf("batch search: %5d ms\n", elapsed(&start));
523 xgettimeofday(&start);
524 CMAP_FOR_EACH (e, node, &cmap) {
525 cmap_remove(&cmap, &e->node, hash_int(e->value, 0));
528 printf("cmap destroy: %5d ms\n", elapsed(&start));
533 /* hmap benchmark. */
536 struct hmap_node node;
539 static struct helement *
540 hfind(const struct hmap *hmap, int value)
544 HMAP_FOR_EACH_WITH_HASH (e, node, hash_int(value, 0), hmap) {
545 if (e->value == value) {
554 struct fat_rwlock fatlock;
558 search_hmap(void *aux_)
560 struct hmap_aux *aux = aux_;
564 for (i = 0; i < n_elems; i++) {
565 if (random_uint32() < mutation_frac) {
568 fat_rwlock_wrlock(&aux->fatlock);
569 e = hfind(aux->hmap, i);
571 hmap_remove(aux->hmap, &e->node);
573 fat_rwlock_unlock(&aux->fatlock);
575 fat_rwlock_rdlock(&aux->fatlock);
576 ignore(hfind(aux->hmap, i));
577 fat_rwlock_unlock(&aux->fatlock);
581 for (i = 0; i < n_elems; i++) {
582 ignore(hfind(aux->hmap, i));
591 struct helement *elements;
593 struct helement *e, *next;
594 struct timeval start;
599 elements = xmalloc(n_elems * sizeof *elements);
601 xgettimeofday(&start);
603 for (i = 0; i < n_elems; i++) {
604 elements[i].value = i;
605 hmap_insert(&hmap, &elements[i].node, hash_int(i, 0));
608 printf("hmap insert: %5d ms\n", elapsed(&start));
610 xgettimeofday(&start);
611 HMAP_FOR_EACH (e, node, &hmap) {
614 printf("hmap iterate: %5d ms\n", elapsed(&start));
616 xgettimeofday(&start);
618 fat_rwlock_init(&aux.fatlock);
619 threads = xmalloc(n_threads * sizeof *threads);
620 for (i = 0; i < n_threads; i++) {
621 threads[i] = ovs_thread_create("search", search_hmap, &aux);
623 for (i = 0; i < n_threads; i++) {
624 xpthread_join(threads[i], NULL);
627 printf("hmap search: %5d ms\n", elapsed(&start));
630 xgettimeofday(&start);
631 HMAP_FOR_EACH_SAFE (e, next, node, &hmap) {
632 hmap_remove(&hmap, &e->node);
635 printf("hmap destroy: %5d ms\n", elapsed(&start));
640 static const struct command commands[] = {
641 {"check", 0, 1, run_tests},
642 {"benchmark", 3, 4, run_benchmarks},
647 test_cmap_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
649 set_program_name(argv[0]);
650 run_command(argc - optind, argv + optind, commands);
653 OVSTEST_REGISTER("test-cmap", test_cmap_main);