1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2015 Intel Corporation
9 #include <rte_cycles.h>
10 #include <rte_malloc.h>
12 #include <rte_hash_crc.h>
13 #include <rte_jhash.h>
14 #include <rte_fbk_hash.h>
15 #include <rte_random.h>
16 #include <rte_string_fns.h>
20 #define MAX_ENTRIES (1 << 19)
21 #define KEYS_TO_ADD (MAX_ENTRIES * 3 / 4) /* 75% table utilization */
22 #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */
23 /* BUCKET_SIZE should be same as RTE_HASH_BUCKET_ENTRIES in rte_hash library */
25 #define NUM_BUCKETS (MAX_ENTRIES / BUCKET_SIZE)
26 #define MAX_KEYSIZE 64
27 #define NUM_KEYSIZES 10
28 #define NUM_SHUFFLES 10
39 static uint32_t hashtest_key_lens[] = {
40 /* standard key sizes */
42 /* IPv4 SRC + DST + protocol, unpadded */
44 /* IPv4 5-tuple, unpadded */
46 /* IPv6 5-tuple, unpadded */
48 /* IPv6 5-tuple, padded to 8-byte boundary */
52 struct rte_hash *h[NUM_KEYSIZES];
54 /* Array that stores if a slot is full */
55 uint8_t slot_taken[MAX_ENTRIES];
57 /* Array to store number of cycles per operation */
58 uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS][2][2];
60 /* Array to store all input keys */
61 uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE];
63 /* Array to store the precomputed hash for 'keys' */
64 hash_sig_t signatures[KEYS_TO_ADD];
66 /* Array to store how many busy entries have each bucket */
67 uint8_t buckets[NUM_BUCKETS];
69 /* Array to store the positions where keys are added */
70 int32_t positions[KEYS_TO_ADD];
72 /* Parameters used for hash table in unit test functions. */
73 static struct rte_hash_parameters ut_params = {
74 .entries = MAX_ENTRIES,
75 .hash_func = rte_jhash,
76 .hash_func_init_val = 0,
80 create_table(unsigned int with_data, unsigned int table_index,
81 unsigned int with_locks)
83 char name[RTE_HASH_NAMESIZE];
86 /* Table will store 8-byte data */
87 sprintf(name, "test_hash%d_data", hashtest_key_lens[table_index]);
89 sprintf(name, "test_hash%d", hashtest_key_lens[table_index]);
93 ut_params.extra_flag =
94 RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT
95 | RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
97 ut_params.extra_flag = 0;
99 ut_params.name = name;
100 ut_params.key_len = hashtest_key_lens[table_index];
101 ut_params.socket_id = rte_socket_id();
102 h[table_index] = rte_hash_find_existing(name);
103 if (h[table_index] != NULL)
105 * If table was already created, free it to create it again,
106 * so we force it is empty
108 rte_hash_free(h[table_index]);
109 h[table_index] = rte_hash_create(&ut_params);
110 if (h[table_index] == NULL) {
111 printf("Error creating table\n");
118 /* Shuffle the keys that have been added, so lookups will be totally random */
120 shuffle_input_keys(unsigned table_index)
124 uint8_t temp_key[MAX_KEYSIZE];
125 hash_sig_t temp_signature;
126 int32_t temp_position;
128 for (i = KEYS_TO_ADD - 1; i > 0; i--) {
129 swap_idx = rte_rand() % i;
131 memcpy(temp_key, keys[i], hashtest_key_lens[table_index]);
132 temp_signature = signatures[i];
133 temp_position = positions[i];
135 memcpy(keys[i], keys[swap_idx], hashtest_key_lens[table_index]);
136 signatures[i] = signatures[swap_idx];
137 positions[i] = positions[swap_idx];
139 memcpy(keys[swap_idx], temp_key, hashtest_key_lens[table_index]);
140 signatures[swap_idx] = temp_signature;
141 positions[swap_idx] = temp_position;
146 * Looks for random keys which
147 * ALL can fit in hash table (no errors)
150 get_input_keys(unsigned with_pushes, unsigned table_index)
153 unsigned bucket_idx, incr, success = 1;
156 const uint32_t bucket_bitmask = NUM_BUCKETS - 1;
158 /* Reset all arrays */
159 for (i = 0; i < MAX_ENTRIES; i++)
162 for (i = 0; i < NUM_BUCKETS; i++)
165 for (j = 0; j < hashtest_key_lens[table_index]; j++)
169 * Add only entries that are not duplicated and that fits in the table
170 * (cannot store more than BUCKET_SIZE entries in a bucket).
171 * Regardless a key has been added correctly or not (success),
172 * the next one to try will be increased by 1.
174 for (i = 0; i < KEYS_TO_ADD;) {
178 /* Overflow, need to increment the next byte */
181 for (j = 1; j < hashtest_key_lens[table_index]; j++) {
182 /* Do not increase next byte */
185 keys[i][j] = keys[i - 1][j];
187 keys[i][j] = keys[i][j];
188 /* Increase next byte by one */
191 keys[i][j] = keys[i-1][j] + 1;
193 keys[i][j] = keys[i][j] + 1;
202 signatures[i] = rte_hash_hash(h[table_index], keys[i]);
203 bucket_idx = signatures[i] & bucket_bitmask;
205 * If we are not inserting keys in secondary location,
206 * when bucket is full, do not try to insert the key
208 if (with_pushes == 0)
209 if (buckets[bucket_idx] == BUCKET_SIZE)
212 /* If key can be added, leave in successful key arrays "keys" */
213 ret = rte_hash_add_key_with_hash(h[table_index], keys[i],
216 /* If key is already added, ignore the entry and do not store */
220 /* Store the returned position and mark slot as taken */
223 buckets[bucket_idx]++;
230 /* Reset the table, so we can measure the time to add all the entries */
231 rte_hash_free(h[table_index]);
232 h[table_index] = rte_hash_create(&ut_params);
238 timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index)
241 const uint64_t start_tsc = rte_rdtsc();
245 for (i = 0; i < KEYS_TO_ADD; i++) {
246 data = (void *) ((uintptr_t) signatures[i]);
247 if (with_hash && with_data) {
248 ret = rte_hash_add_key_with_hash_data(h[table_index],
249 (const void *) keys[i],
250 signatures[i], data);
252 printf("H+D: Failed to add key number %u\n", i);
255 } else if (with_hash && !with_data) {
256 ret = rte_hash_add_key_with_hash(h[table_index],
257 (const void *) keys[i],
262 printf("H: Failed to add key number %u\n", i);
265 } else if (!with_hash && with_data) {
266 ret = rte_hash_add_key_data(h[table_index],
267 (const void *) keys[i],
270 printf("D: Failed to add key number %u\n", i);
274 ret = rte_hash_add_key(h[table_index], keys[i]);
278 printf("Failed to add key number %u\n", i);
284 const uint64_t end_tsc = rte_rdtsc();
285 const uint64_t time_taken = end_tsc - start_tsc;
287 cycles[table_index][ADD][with_hash][with_data] = time_taken/KEYS_TO_ADD;
293 timed_lookups(unsigned with_hash, unsigned with_data, unsigned table_index)
296 const uint64_t start_tsc = rte_rdtsc();
301 for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) {
302 for (j = 0; j < KEYS_TO_ADD; j++) {
303 if (with_hash && with_data) {
304 ret = rte_hash_lookup_with_hash_data(h[table_index],
305 (const void *) keys[j],
306 signatures[j], &ret_data);
308 printf("Key number %u was not found\n", j);
311 expected_data = (void *) ((uintptr_t) signatures[j]);
312 if (ret_data != expected_data) {
313 printf("Data returned for key number %u is %p,"
314 " but should be %p\n", j, ret_data,
318 } else if (with_hash && !with_data) {
319 ret = rte_hash_lookup_with_hash(h[table_index],
320 (const void *) keys[j],
322 if (ret < 0 || ret != positions[j]) {
323 printf("Key looked up in %d, should be in %d\n",
327 } else if (!with_hash && with_data) {
328 ret = rte_hash_lookup_data(h[table_index],
329 (const void *) keys[j], &ret_data);
331 printf("Key number %u was not found\n", j);
334 expected_data = (void *) ((uintptr_t) signatures[j]);
335 if (ret_data != expected_data) {
336 printf("Data returned for key number %u is %p,"
337 " but should be %p\n", j, ret_data,
342 ret = rte_hash_lookup(h[table_index], keys[j]);
343 if (ret < 0 || ret != positions[j]) {
344 printf("Key looked up in %d, should be in %d\n",
352 const uint64_t end_tsc = rte_rdtsc();
353 const uint64_t time_taken = end_tsc - start_tsc;
355 cycles[table_index][LOOKUP][with_hash][with_data] = time_taken/NUM_LOOKUPS;
361 timed_lookups_multi(unsigned with_data, unsigned table_index)
364 int32_t positions_burst[BURST_SIZE];
365 const void *keys_burst[BURST_SIZE];
366 void *expected_data[BURST_SIZE];
367 void *ret_data[BURST_SIZE];
371 const uint64_t start_tsc = rte_rdtsc();
373 for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) {
374 for (j = 0; j < KEYS_TO_ADD/BURST_SIZE; j++) {
375 for (k = 0; k < BURST_SIZE; k++)
376 keys_burst[k] = keys[j * BURST_SIZE + k];
378 ret = rte_hash_lookup_bulk_data(h[table_index],
379 (const void **) keys_burst,
383 if (ret != BURST_SIZE) {
384 printf("Expect to find %u keys,"
385 " but found %d\n", BURST_SIZE, ret);
388 for (k = 0; k < BURST_SIZE; k++) {
389 if ((hit_mask & (1ULL << k)) == 0) {
390 printf("Key number %u not found\n",
394 expected_data[k] = (void *) ((uintptr_t) signatures[j * BURST_SIZE + k]);
395 if (ret_data[k] != expected_data[k]) {
396 printf("Data returned for key number %u is %p,"
397 " but should be %p\n", j * BURST_SIZE + k,
398 ret_data[k], expected_data[k]);
403 rte_hash_lookup_bulk(h[table_index],
404 (const void **) keys_burst,
407 for (k = 0; k < BURST_SIZE; k++) {
408 if (positions_burst[k] != positions[j * BURST_SIZE + k]) {
409 printf("Key looked up in %d, should be in %d\n",
411 positions[j * BURST_SIZE + k]);
419 const uint64_t end_tsc = rte_rdtsc();
420 const uint64_t time_taken = end_tsc - start_tsc;
422 cycles[table_index][LOOKUP_MULTI][0][with_data] = time_taken/NUM_LOOKUPS;
428 timed_deletes(unsigned with_hash, unsigned with_data, unsigned table_index)
431 const uint64_t start_tsc = rte_rdtsc();
434 for (i = 0; i < KEYS_TO_ADD; i++) {
435 /* There are no delete functions with data, so just call two functions */
437 ret = rte_hash_del_key_with_hash(h[table_index],
438 (const void *) keys[i],
441 ret = rte_hash_del_key(h[table_index],
442 (const void *) keys[i]);
446 printf("Failed to delete key number %u\n", i);
451 const uint64_t end_tsc = rte_rdtsc();
452 const uint64_t time_taken = end_tsc - start_tsc;
454 cycles[table_index][DELETE][with_hash][with_data] = time_taken/KEYS_TO_ADD;
460 free_table(unsigned table_index)
462 rte_hash_free(h[table_index]);
466 reset_table(unsigned table_index)
468 rte_hash_reset(h[table_index]);
472 run_all_tbl_perf_tests(unsigned int with_pushes, unsigned int with_locks)
474 unsigned i, j, with_data, with_hash;
476 printf("Measuring performance, please wait");
479 for (with_data = 0; with_data <= 1; with_data++) {
480 for (i = 0; i < NUM_KEYSIZES; i++) {
481 if (create_table(with_data, i, with_locks) < 0)
484 if (get_input_keys(with_pushes, i) < 0)
486 for (with_hash = 0; with_hash <= 1; with_hash++) {
487 if (timed_adds(with_hash, with_data, i) < 0)
490 for (j = 0; j < NUM_SHUFFLES; j++)
491 shuffle_input_keys(i);
493 if (timed_lookups(with_hash, with_data, i) < 0)
496 if (timed_lookups_multi(with_data, i) < 0)
499 if (timed_deletes(with_hash, with_data, i) < 0)
502 /* Print a dot to show progress on operations */
512 printf("\nResults (in CPU cycles/operation)\n");
513 printf("-----------------------------------\n");
514 for (with_data = 0; with_data <= 1; with_data++) {
516 printf("\n Operations with 8-byte data\n");
518 printf("\n Operations without data\n");
519 for (with_hash = 0; with_hash <= 1; with_hash++) {
521 printf("\nWith pre-computed hash values\n");
523 printf("\nWithout pre-computed hash values\n");
525 printf("\n%-18s%-18s%-18s%-18s%-18s\n",
526 "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
527 for (i = 0; i < NUM_KEYSIZES; i++) {
528 printf("%-18d", hashtest_key_lens[i]);
529 for (j = 0; j < NUM_OPERATIONS; j++)
530 printf("%-18"PRIu64, cycles[i][j][with_hash][with_data]);
538 /* Control operation of performance testing of fbk hash. */
539 #define LOAD_FACTOR 0.667 /* How full to make the hash table. */
540 #define TEST_SIZE 1000000 /* How many operations to time. */
541 #define TEST_ITERATIONS 30 /* How many measurements to take. */
542 #define ENTRIES (1 << 15) /* How many entries. */
545 fbk_hash_perf_test(void)
547 struct rte_fbk_hash_params params = {
548 .name = "fbk_hash_test",
550 .entries_per_bucket = 4,
551 .socket_id = rte_socket_id(),
553 struct rte_fbk_hash_table *handle = NULL;
554 uint32_t *keys = NULL;
555 unsigned indexes[TEST_SIZE];
556 uint64_t lookup_time = 0;
563 handle = rte_fbk_hash_create(¶ms);
564 if (handle == NULL) {
565 printf("Error creating table\n");
569 keys = rte_zmalloc(NULL, ENTRIES * sizeof(*keys), 0);
571 printf("fbk hash: memory allocation for key store failed\n");
575 /* Generate random keys and values. */
576 for (i = 0; i < ENTRIES; i++) {
577 key = (uint32_t)rte_rand();
578 key = ((uint64_t)key << 32) | (uint64_t)rte_rand();
579 val = (uint16_t)rte_rand();
581 if (rte_fbk_hash_add_key(handle, key, val) == 0) {
585 if (added > (LOAD_FACTOR * ENTRIES))
589 for (i = 0; i < TEST_ITERATIONS; i++) {
593 /* Generate random indexes into keys[] array. */
594 for (j = 0; j < TEST_SIZE; j++)
595 indexes[j] = rte_rand() % added;
599 for (j = 0; j < TEST_SIZE; j++)
600 value += rte_fbk_hash_lookup(handle, keys[indexes[j]]);
603 lookup_time += (double)(end - begin);
606 printf("\n\n *** FBK Hash function performance test results ***\n");
608 * The use of the 'value' variable ensures that the hash lookup is not
609 * being optimised out by the compiler.
612 printf("Number of ticks per lookup = %g\n",
613 (double)lookup_time /
614 ((double)TEST_ITERATIONS * (double)TEST_SIZE));
616 rte_fbk_hash_free(handle);
624 unsigned int with_pushes, with_locks;
625 for (with_locks = 0; with_locks <= 1; with_locks++) {
627 printf("\nWith locks in the code\n");
629 printf("\nWithout locks in the code\n");
630 for (with_pushes = 0; with_pushes <= 1; with_pushes++) {
631 if (with_pushes == 0)
632 printf("\nALL ELEMENTS IN PRIMARY LOCATION\n");
634 printf("\nELEMENTS IN PRIMARY OR SECONDARY LOCATION\n");
635 if (run_all_tbl_perf_tests(with_pushes, with_locks) < 0)
639 if (fbk_hash_perf_test() < 0)
645 REGISTER_TEST_COMMAND(hash_perf_autotest, test_hash_perf);