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 */
24 #define NUM_BUCKETS (MAX_ENTRIES / BUCKET_SIZE)
25 #define MAX_KEYSIZE 64
26 #define NUM_KEYSIZES 10
27 #define NUM_SHUFFLES 10
38 static uint32_t hashtest_key_lens[] = {
39 /* standard key sizes */
41 /* IPv4 SRC + DST + protocol, unpadded */
43 /* IPv4 5-tuple, unpadded */
45 /* IPv6 5-tuple, unpadded */
47 /* IPv6 5-tuple, padded to 8-byte boundary */
51 struct rte_hash *h[NUM_KEYSIZES];
53 /* Array that stores if a slot is full */
54 uint8_t slot_taken[MAX_ENTRIES];
56 /* Array to store number of cycles per operation */
57 uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS][2][2];
59 /* Array to store all input keys */
60 uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE];
62 /* Array to store the precomputed hash for 'keys' */
63 hash_sig_t signatures[KEYS_TO_ADD];
65 /* Array to store how many busy entries have each bucket */
66 uint8_t buckets[NUM_BUCKETS];
68 /* Array to store the positions where keys are added */
69 int32_t positions[KEYS_TO_ADD];
71 /* Parameters used for hash table in unit test functions. */
72 static struct rte_hash_parameters ut_params = {
73 .entries = MAX_ENTRIES,
74 .hash_func = rte_jhash,
75 .hash_func_init_val = 0,
79 create_table(unsigned with_data, unsigned table_index)
81 char name[RTE_HASH_NAMESIZE];
84 /* Table will store 8-byte data */
85 sprintf(name, "test_hash%d_data", hashtest_key_lens[table_index]);
87 sprintf(name, "test_hash%d", hashtest_key_lens[table_index]);
89 ut_params.name = name;
90 ut_params.key_len = hashtest_key_lens[table_index];
91 ut_params.socket_id = rte_socket_id();
92 h[table_index] = rte_hash_find_existing(name);
93 if (h[table_index] != NULL)
95 * If table was already created, free it to create it again,
96 * so we force it is empty
98 rte_hash_free(h[table_index]);
99 h[table_index] = rte_hash_create(&ut_params);
100 if (h[table_index] == NULL) {
101 printf("Error creating table\n");
108 /* Shuffle the keys that have been added, so lookups will be totally random */
110 shuffle_input_keys(unsigned table_index)
114 uint8_t temp_key[MAX_KEYSIZE];
115 hash_sig_t temp_signature;
116 int32_t temp_position;
118 for (i = KEYS_TO_ADD - 1; i > 0; i--) {
119 swap_idx = rte_rand() % i;
121 memcpy(temp_key, keys[i], hashtest_key_lens[table_index]);
122 temp_signature = signatures[i];
123 temp_position = positions[i];
125 memcpy(keys[i], keys[swap_idx], hashtest_key_lens[table_index]);
126 signatures[i] = signatures[swap_idx];
127 positions[i] = positions[swap_idx];
129 memcpy(keys[swap_idx], temp_key, hashtest_key_lens[table_index]);
130 signatures[swap_idx] = temp_signature;
131 positions[swap_idx] = temp_position;
136 * Looks for random keys which
137 * ALL can fit in hash table (no errors)
140 get_input_keys(unsigned with_pushes, unsigned table_index)
143 unsigned bucket_idx, incr, success = 1;
146 const uint32_t bucket_bitmask = NUM_BUCKETS - 1;
148 /* Reset all arrays */
149 for (i = 0; i < MAX_ENTRIES; i++)
152 for (i = 0; i < NUM_BUCKETS; i++)
155 for (j = 0; j < hashtest_key_lens[table_index]; j++)
159 * Add only entries that are not duplicated and that fits in the table
160 * (cannot store more than BUCKET_SIZE entries in a bucket).
161 * Regardless a key has been added correctly or not (success),
162 * the next one to try will be increased by 1.
164 for (i = 0; i < KEYS_TO_ADD;) {
168 /* Overflow, need to increment the next byte */
171 for (j = 1; j < hashtest_key_lens[table_index]; j++) {
172 /* Do not increase next byte */
175 keys[i][j] = keys[i - 1][j];
177 keys[i][j] = keys[i][j];
178 /* Increase next byte by one */
181 keys[i][j] = keys[i-1][j] + 1;
183 keys[i][j] = keys[i][j] + 1;
192 signatures[i] = rte_hash_hash(h[table_index], keys[i]);
193 bucket_idx = signatures[i] & bucket_bitmask;
195 * If we are not inserting keys in secondary location,
196 * when bucket is full, do not try to insert the key
198 if (with_pushes == 0)
199 if (buckets[bucket_idx] == BUCKET_SIZE)
202 /* If key can be added, leave in successful key arrays "keys" */
203 ret = rte_hash_add_key_with_hash(h[table_index], keys[i],
206 /* If key is already added, ignore the entry and do not store */
210 /* Store the returned position and mark slot as taken */
213 buckets[bucket_idx]++;
220 /* Reset the table, so we can measure the time to add all the entries */
221 rte_hash_free(h[table_index]);
222 h[table_index] = rte_hash_create(&ut_params);
228 timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index)
231 const uint64_t start_tsc = rte_rdtsc();
235 for (i = 0; i < KEYS_TO_ADD; i++) {
236 data = (void *) ((uintptr_t) signatures[i]);
237 if (with_hash && with_data) {
238 ret = rte_hash_add_key_with_hash_data(h[table_index],
239 (const void *) keys[i],
240 signatures[i], data);
242 printf("Failed to add key number %u\n", ret);
245 } else if (with_hash && !with_data) {
246 ret = rte_hash_add_key_with_hash(h[table_index],
247 (const void *) keys[i],
252 printf("Failed to add key number %u\n", ret);
255 } else if (!with_hash && with_data) {
256 ret = rte_hash_add_key_data(h[table_index],
257 (const void *) keys[i],
260 printf("Failed to add key number %u\n", ret);
264 ret = rte_hash_add_key(h[table_index], keys[i]);
268 printf("Failed to add key number %u\n", ret);
274 const uint64_t end_tsc = rte_rdtsc();
275 const uint64_t time_taken = end_tsc - start_tsc;
277 cycles[table_index][ADD][with_hash][with_data] = time_taken/KEYS_TO_ADD;
283 timed_lookups(unsigned with_hash, unsigned with_data, unsigned table_index)
286 const uint64_t start_tsc = rte_rdtsc();
291 for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) {
292 for (j = 0; j < KEYS_TO_ADD; j++) {
293 if (with_hash && with_data) {
294 ret = rte_hash_lookup_with_hash_data(h[table_index],
295 (const void *) keys[j],
296 signatures[j], &ret_data);
298 printf("Key number %u was not found\n", j);
301 expected_data = (void *) ((uintptr_t) signatures[j]);
302 if (ret_data != expected_data) {
303 printf("Data returned for key number %u is %p,"
304 " but should be %p\n", j, ret_data,
308 } else if (with_hash && !with_data) {
309 ret = rte_hash_lookup_with_hash(h[table_index],
310 (const void *) keys[j],
312 if (ret < 0 || ret != positions[j]) {
313 printf("Key looked up in %d, should be in %d\n",
317 } else if (!with_hash && with_data) {
318 ret = rte_hash_lookup_data(h[table_index],
319 (const void *) keys[j], &ret_data);
321 printf("Key number %u was not found\n", j);
324 expected_data = (void *) ((uintptr_t) signatures[j]);
325 if (ret_data != expected_data) {
326 printf("Data returned for key number %u is %p,"
327 " but should be %p\n", j, ret_data,
332 ret = rte_hash_lookup(h[table_index], keys[j]);
333 if (ret < 0 || ret != positions[j]) {
334 printf("Key looked up in %d, should be in %d\n",
342 const uint64_t end_tsc = rte_rdtsc();
343 const uint64_t time_taken = end_tsc - start_tsc;
345 cycles[table_index][LOOKUP][with_hash][with_data] = time_taken/NUM_LOOKUPS;
351 timed_lookups_multi(unsigned with_data, unsigned table_index)
354 int32_t positions_burst[BURST_SIZE];
355 const void *keys_burst[BURST_SIZE];
356 void *expected_data[BURST_SIZE];
357 void *ret_data[BURST_SIZE];
361 const uint64_t start_tsc = rte_rdtsc();
363 for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) {
364 for (j = 0; j < KEYS_TO_ADD/BURST_SIZE; j++) {
365 for (k = 0; k < BURST_SIZE; k++)
366 keys_burst[k] = keys[j * BURST_SIZE + k];
368 ret = rte_hash_lookup_bulk_data(h[table_index],
369 (const void **) keys_burst,
373 if (ret != BURST_SIZE) {
374 printf("Expect to find %u keys,"
375 " but found %d\n", BURST_SIZE, ret);
378 for (k = 0; k < BURST_SIZE; k++) {
379 if ((hit_mask & (1ULL << k)) == 0) {
380 printf("Key number %u not found\n",
384 expected_data[k] = (void *) ((uintptr_t) signatures[j * BURST_SIZE + k]);
385 if (ret_data[k] != expected_data[k]) {
386 printf("Data returned for key number %u is %p,"
387 " but should be %p\n", j * BURST_SIZE + k,
388 ret_data[k], expected_data[k]);
393 rte_hash_lookup_bulk(h[table_index],
394 (const void **) keys_burst,
397 for (k = 0; k < BURST_SIZE; k++) {
398 if (positions_burst[k] != positions[j * BURST_SIZE + k]) {
399 printf("Key looked up in %d, should be in %d\n",
401 positions[j * BURST_SIZE + k]);
409 const uint64_t end_tsc = rte_rdtsc();
410 const uint64_t time_taken = end_tsc - start_tsc;
412 cycles[table_index][LOOKUP_MULTI][0][with_data] = time_taken/NUM_LOOKUPS;
418 timed_deletes(unsigned with_hash, unsigned with_data, unsigned table_index)
421 const uint64_t start_tsc = rte_rdtsc();
424 for (i = 0; i < KEYS_TO_ADD; i++) {
425 /* There are no delete functions with data, so just call two functions */
427 ret = rte_hash_del_key_with_hash(h[table_index],
428 (const void *) keys[i],
431 ret = rte_hash_del_key(h[table_index],
432 (const void *) keys[i]);
436 printf("Failed to add key number %u\n", ret);
441 const uint64_t end_tsc = rte_rdtsc();
442 const uint64_t time_taken = end_tsc - start_tsc;
444 cycles[table_index][DELETE][with_hash][with_data] = time_taken/KEYS_TO_ADD;
450 free_table(unsigned table_index)
452 rte_hash_free(h[table_index]);
456 reset_table(unsigned table_index)
458 rte_hash_reset(h[table_index]);
462 run_all_tbl_perf_tests(unsigned with_pushes)
464 unsigned i, j, with_data, with_hash;
466 printf("Measuring performance, please wait");
469 for (with_data = 0; with_data <= 1; with_data++) {
470 for (i = 0; i < NUM_KEYSIZES; i++) {
471 if (create_table(with_data, i) < 0)
474 if (get_input_keys(with_pushes, i) < 0)
476 for (with_hash = 0; with_hash <= 1; with_hash++) {
477 if (timed_adds(with_hash, with_data, i) < 0)
480 for (j = 0; j < NUM_SHUFFLES; j++)
481 shuffle_input_keys(i);
483 if (timed_lookups(with_hash, with_data, i) < 0)
486 if (timed_lookups_multi(with_data, i) < 0)
489 if (timed_deletes(with_hash, with_data, i) < 0)
492 /* Print a dot to show progress on operations */
502 printf("\nResults (in CPU cycles/operation)\n");
503 printf("-----------------------------------\n");
504 for (with_data = 0; with_data <= 1; with_data++) {
506 printf("\n Operations with 8-byte data\n");
508 printf("\n Operations without data\n");
509 for (with_hash = 0; with_hash <= 1; with_hash++) {
511 printf("\nWith pre-computed hash values\n");
513 printf("\nWithout pre-computed hash values\n");
515 printf("\n%-18s%-18s%-18s%-18s%-18s\n",
516 "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
517 for (i = 0; i < NUM_KEYSIZES; i++) {
518 printf("%-18d", hashtest_key_lens[i]);
519 for (j = 0; j < NUM_OPERATIONS; j++)
520 printf("%-18"PRIu64, cycles[i][j][with_hash][with_data]);
528 /* Control operation of performance testing of fbk hash. */
529 #define LOAD_FACTOR 0.667 /* How full to make the hash table. */
530 #define TEST_SIZE 1000000 /* How many operations to time. */
531 #define TEST_ITERATIONS 30 /* How many measurements to take. */
532 #define ENTRIES (1 << 15) /* How many entries. */
535 fbk_hash_perf_test(void)
537 struct rte_fbk_hash_params params = {
538 .name = "fbk_hash_test",
540 .entries_per_bucket = 4,
541 .socket_id = rte_socket_id(),
543 struct rte_fbk_hash_table *handle = NULL;
544 uint32_t *keys = NULL;
545 unsigned indexes[TEST_SIZE];
546 uint64_t lookup_time = 0;
553 handle = rte_fbk_hash_create(¶ms);
554 if (handle == NULL) {
555 printf("Error creating table\n");
559 keys = rte_zmalloc(NULL, ENTRIES * sizeof(*keys), 0);
561 printf("fbk hash: memory allocation for key store failed\n");
565 /* Generate random keys and values. */
566 for (i = 0; i < ENTRIES; i++) {
567 key = (uint32_t)rte_rand();
568 key = ((uint64_t)key << 32) | (uint64_t)rte_rand();
569 val = (uint16_t)rte_rand();
571 if (rte_fbk_hash_add_key(handle, key, val) == 0) {
575 if (added > (LOAD_FACTOR * ENTRIES))
579 for (i = 0; i < TEST_ITERATIONS; i++) {
583 /* Generate random indexes into keys[] array. */
584 for (j = 0; j < TEST_SIZE; j++)
585 indexes[j] = rte_rand() % added;
589 for (j = 0; j < TEST_SIZE; j++)
590 value += rte_fbk_hash_lookup(handle, keys[indexes[j]]);
593 lookup_time += (double)(end - begin);
596 printf("\n\n *** FBK Hash function performance test results ***\n");
598 * The use of the 'value' variable ensures that the hash lookup is not
599 * being optimised out by the compiler.
602 printf("Number of ticks per lookup = %g\n",
603 (double)lookup_time /
604 ((double)TEST_ITERATIONS * (double)TEST_SIZE));
606 rte_fbk_hash_free(handle);
614 unsigned with_pushes;
616 for (with_pushes = 0; with_pushes <= 1; with_pushes++) {
617 if (with_pushes == 0)
618 printf("\nALL ELEMENTS IN PRIMARY LOCATION\n");
620 printf("\nELEMENTS IN PRIMARY OR SECONDARY LOCATION\n");
621 if (run_all_tbl_perf_tests(with_pushes) < 0)
624 if (fbk_hash_perf_test() < 0)
630 REGISTER_TEST_COMMAND(hash_perf_autotest, test_hash_perf);