1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2017 Intel Corporation
9 #include <rte_cycles.h>
10 #include <rte_malloc.h>
11 #include <rte_random.h>
12 #include <rte_memcpy.h>
13 #include <rte_thash.h>
14 #include <rte_member.h>
18 #define NUM_KEYSIZES 10
19 #define NUM_SHUFFLES 10
20 #define MAX_KEYSIZE 64
21 #define MAX_ENTRIES (1 << 19)
22 #define KEYS_TO_ADD (MAX_ENTRIES * 75 / 100) /* 75% table utilization */
23 #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */
24 #define VBF_SET_CNT 16
26 #define VBF_FALSE_RATE 0.03
28 static unsigned int test_socket_id;
48 struct member_perf_params {
49 struct rte_member_setsum *setsum[NUM_TYPE];
54 static uint32_t hashtest_key_lens[] = {
55 /* standard key sizes */
57 /* IPv4 SRC + DST + protocol, unpadded */
59 /* IPv4 5-tuple, unpadded */
61 /* IPv6 5-tuple, unpadded */
63 /* IPv6 5-tuple, padded to 8-byte boundary */
67 /* Array to store number of cycles per operation */
68 static uint64_t cycles[NUM_TYPE][NUM_KEYSIZES][NUM_OPERATIONS];
69 static uint64_t false_data[NUM_TYPE][NUM_KEYSIZES];
70 static uint64_t false_data_bulk[NUM_TYPE][NUM_KEYSIZES];
71 static uint64_t false_data_multi[NUM_TYPE][NUM_KEYSIZES];
72 static uint64_t false_data_multi_bulk[NUM_TYPE][NUM_KEYSIZES];
74 static uint64_t false_hit[NUM_TYPE][NUM_KEYSIZES];
76 static member_set_t data[NUM_TYPE][/* Array to store the data */KEYS_TO_ADD];
78 /* Array to store all input keys */
79 static uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE];
81 /* Shuffle the keys that have been added, so lookups will be totally random */
83 shuffle_input_keys(struct member_perf_params *params)
85 member_set_t temp_data;
88 uint8_t temp_key[MAX_KEYSIZE];
90 for (i = KEYS_TO_ADD - 1; i > 0; i--) {
91 swap_idx = rte_rand() % i;
92 memcpy(temp_key, keys[i], hashtest_key_lens[params->cycle]);
93 memcpy(keys[i], keys[swap_idx],
94 hashtest_key_lens[params->cycle]);
95 memcpy(keys[swap_idx], temp_key,
96 hashtest_key_lens[params->cycle]);
97 for (j = 0; j < NUM_TYPE; j++) {
98 temp_data = data[j][i];
99 data[j][i] = data[j][swap_idx];
100 data[j][swap_idx] = temp_data;
105 static int key_compare(const void *key1, const void *key2)
107 return memcmp(key1, key2, MAX_KEYSIZE);
110 struct rte_member_parameters member_params = {
111 .num_keys = MAX_ENTRIES, /* Total hash table entries. */
112 .key_len = 4, /* Length of hash key. */
114 /* num_set and false_positive_rate only relevant to vBF */
115 .num_set = VBF_SET_CNT,
116 .false_positive_rate = 0.03,
119 .socket_id = 0, /* NUMA Socket ID for memory. */
123 setup_keys_and_data(struct member_perf_params *params, unsigned int cycle,
129 params->key_size = hashtest_key_lens[cycle];
130 params->cycle = cycle;
132 /* Reset all arrays */
133 for (i = 0; i < params->key_size; i++)
136 /* Generate a list of keys, some of which may be duplicates */
137 for (i = 0; i < KEYS_TO_ADD; i++) {
138 for (j = 0; j < params->key_size; j++)
139 keys[i][j] = rte_rand() & 0xFF;
141 data[HT][i] = data[CACHE][i] = (rte_rand() & 0x7FFE) + 1;
142 data[VBF][i] = rte_rand() % VBF_SET_CNT + 1;
145 /* Remove duplicates from the keys array */
149 /* Sort the list of keys to make it easier to find duplicates */
150 qsort(keys, KEYS_TO_ADD, MAX_KEYSIZE, key_compare);
152 /* Sift through the list of keys and look for duplicates */
153 int num_duplicates = 0;
154 for (i = 0; i < KEYS_TO_ADD - 1; i++) {
155 if (memcmp(keys[i], keys[i + 1],
156 params->key_size) == 0) {
157 /* This key already exists, try again */
159 for (j = 0; j < params->key_size; j++)
160 keys[i][j] = rte_rand() & 0xFF;
163 } while (num_duplicates != 0);
165 /* Shuffle the random values again */
166 shuffle_input_keys(params);
168 /* For testing miss lookup, we insert half and lookup the other half */
169 unsigned int entry_cnt, bf_key_cnt;
171 entry_cnt = MAX_ENTRIES;
172 bf_key_cnt = KEYS_TO_ADD;
174 entry_cnt = MAX_ENTRIES / 2;
175 bf_key_cnt = KEYS_TO_ADD / 2;
177 member_params.false_positive_rate = VBF_FALSE_RATE;
178 member_params.key_len = params->key_size;
179 member_params.socket_id = test_socket_id;
180 member_params.num_keys = entry_cnt;
181 member_params.name = "test_member_ht";
182 member_params.is_cache = 0;
183 member_params.type = RTE_MEMBER_TYPE_HT;
184 params->setsum[HT] = rte_member_create(&member_params);
185 if (params->setsum[HT] == NULL)
186 fprintf(stderr, "ht create fail\n");
188 member_params.name = "test_member_cache";
189 member_params.is_cache = 1;
190 params->setsum[CACHE] = rte_member_create(&member_params);
191 if (params->setsum[CACHE] == NULL)
192 fprintf(stderr, "CACHE create fail\n");
194 member_params.name = "test_member_vbf";
195 member_params.type = RTE_MEMBER_TYPE_VBF;
196 member_params.num_keys = bf_key_cnt;
197 params->setsum[VBF] = rte_member_create(&member_params);
198 if (params->setsum[VBF] == NULL)
199 fprintf(stderr, "VBF create fail\n");
200 for (i = 0; i < NUM_TYPE; i++) {
201 if (params->setsum[i] == NULL)
209 timed_adds(struct member_perf_params *params, int type)
211 const uint64_t start_tsc = rte_rdtsc();
215 for (i = 0; i < KEYS_TO_ADD; i++) {
216 ret = rte_member_add(params->setsum[type], &keys[i],
219 printf("Error %d in rte_member_add - key=0x", ret);
220 for (a = 0; a < params->key_size; a++)
221 printf("%02x", keys[i][a]);
222 printf(" value=%d, type: %d\n", data[type][i], type);
228 const uint64_t end_tsc = rte_rdtsc();
229 const uint64_t time_taken = end_tsc - start_tsc;
231 cycles[type][params->cycle][ADD] = time_taken / KEYS_TO_ADD;
236 timed_lookups(struct member_perf_params *params, int type)
240 false_data[type][params->cycle] = 0;
242 const uint64_t start_tsc = rte_rdtsc();
246 for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) {
247 for (j = 0; j < KEYS_TO_ADD; j++) {
248 ret = rte_member_lookup(params->setsum[type], &keys[j],
251 printf("lookup wrong internally");
254 if (type == HT && result == RTE_MEMBER_NO_MATCH) {
255 printf("HT mode shouldn't have false negative");
258 if (result != data[type][j])
259 false_data[type][params->cycle]++;
263 const uint64_t end_tsc = rte_rdtsc();
264 const uint64_t time_taken = end_tsc - start_tsc;
266 cycles[type][params->cycle][LOOKUP] = time_taken / NUM_LOOKUPS;
272 timed_lookups_bulk(struct member_perf_params *params, int type)
274 unsigned int i, j, k;
275 member_set_t result[BURST_SIZE] = {0};
276 const void *keys_burst[BURST_SIZE];
279 false_data_bulk[type][params->cycle] = 0;
281 const uint64_t start_tsc = rte_rdtsc();
283 for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) {
284 for (j = 0; j < KEYS_TO_ADD / BURST_SIZE; j++) {
285 for (k = 0; k < BURST_SIZE; k++)
286 keys_burst[k] = keys[j * BURST_SIZE + k];
288 ret = rte_member_lookup_bulk(params->setsum[type],
293 printf("lookup bulk has wrong return value\n");
296 for (k = 0; k < BURST_SIZE; k++) {
297 uint32_t data_idx = j * BURST_SIZE + k;
298 if (type == HT && result[k] ==
299 RTE_MEMBER_NO_MATCH) {
300 printf("HT mode shouldn't have "
304 if (result[k] != data[type][data_idx])
305 false_data_bulk[type][params->cycle]++;
310 const uint64_t end_tsc = rte_rdtsc();
311 const uint64_t time_taken = end_tsc - start_tsc;
313 cycles[type][params->cycle][LOOKUP_BULK] = time_taken / NUM_LOOKUPS;
319 timed_lookups_multimatch(struct member_perf_params *params, int type)
322 member_set_t result[RTE_MEMBER_BUCKET_ENTRIES] = {0};
324 false_data_multi[type][params->cycle] = 0;
326 const uint64_t start_tsc = rte_rdtsc();
328 for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) {
329 for (j = 0; j < KEYS_TO_ADD; j++) {
330 ret = rte_member_lookup_multi(params->setsum[type],
331 &keys[j], RTE_MEMBER_BUCKET_ENTRIES, result);
332 if (type != CACHE && ret <= 0) {
333 printf("lookup multi has wrong return value %d,"
334 "type %d\n", ret, type);
336 if (type == HT && ret == 0) {
337 printf("HT mode shouldn't have false negative");
341 * For performance test purpose, we do not iterate all
342 * results here. We assume most likely each key can only
343 * find one match which is result[0].
345 if (result[0] != data[type][j])
346 false_data_multi[type][params->cycle]++;
350 const uint64_t end_tsc = rte_rdtsc();
351 const uint64_t time_taken = end_tsc - start_tsc;
353 cycles[type][params->cycle][LOOKUP_MULTI] = time_taken / NUM_LOOKUPS;
359 timed_lookups_multimatch_bulk(struct member_perf_params *params, int type)
361 unsigned int i, j, k;
362 member_set_t result[BURST_SIZE][RTE_MEMBER_BUCKET_ENTRIES] = {{0} };
363 const void *keys_burst[BURST_SIZE];
364 uint32_t match_count[BURST_SIZE];
367 false_data_multi_bulk[type][params->cycle] = 0;
369 const uint64_t start_tsc = rte_rdtsc();
371 for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) {
372 for (j = 0; j < KEYS_TO_ADD / BURST_SIZE; j++) {
373 for (k = 0; k < BURST_SIZE; k++)
374 keys_burst[k] = keys[j * BURST_SIZE + k];
376 ret = rte_member_lookup_multi_bulk(
377 params->setsum[type],
378 keys_burst, BURST_SIZE,
379 RTE_MEMBER_BUCKET_ENTRIES, match_count,
380 (member_set_t *)result);
382 printf("lookup multimatch bulk has wrong return"
386 for (k = 0; k < BURST_SIZE; k++) {
387 if (type != CACHE && match_count[k] == 0) {
388 printf("lookup multimatch bulk get "
389 "wrong match count\n");
392 if (type == HT && match_count[k] == 0) {
393 printf("HT mode shouldn't have "
397 uint32_t data_idx = j * BURST_SIZE + k;
398 if (result[k][0] != data[type][data_idx])
399 false_data_multi_bulk[type][params->cycle]++;
404 const uint64_t end_tsc = rte_rdtsc();
405 const uint64_t time_taken = end_tsc - start_tsc;
407 cycles[type][params->cycle][LOOKUP_MULTI_BULK] = time_taken /
414 timed_deletes(struct member_perf_params *params, int type)
421 const uint64_t start_tsc = rte_rdtsc();
422 for (i = 0; i < KEYS_TO_ADD; i++) {
423 ret = rte_member_delete(params->setsum[type], &keys[i],
425 if (type != CACHE && ret < 0) {
426 printf("delete error\n");
431 const uint64_t end_tsc = rte_rdtsc();
432 const uint64_t time_taken = end_tsc - start_tsc;
434 cycles[type][params->cycle][DELETE] = time_taken / KEYS_TO_ADD;
440 timed_miss_lookup(struct member_perf_params *params, int type)
445 false_hit[type][params->cycle] = 0;
447 for (i = 0; i < KEYS_TO_ADD / 2; i++) {
448 ret = rte_member_add(params->setsum[type], &keys[i],
452 printf("Error %d in rte_member_add - key=0x", ret);
453 for (a = 0; a < params->key_size; a++)
454 printf("%02x", keys[i][a]);
455 printf(" value=%d, type: %d\n", data[type][i], type);
461 const uint64_t start_tsc = rte_rdtsc();
464 for (i = 0; i < 2 * NUM_LOOKUPS / KEYS_TO_ADD; i++) {
465 for (j = KEYS_TO_ADD / 2; j < KEYS_TO_ADD; j++) {
466 ret = rte_member_lookup(params->setsum[type], &keys[j],
469 printf("lookup wrong internally");
472 if (result != RTE_MEMBER_NO_MATCH)
473 false_hit[type][params->cycle]++;
477 const uint64_t end_tsc = rte_rdtsc();
478 const uint64_t time_taken = end_tsc - start_tsc;
480 cycles[type][params->cycle][LOOKUP_MISS] = time_taken / NUM_LOOKUPS;
486 perform_frees(struct member_perf_params *params)
489 for (i = 0; i < NUM_TYPE; i++) {
490 if (params->setsum[i] != NULL) {
491 rte_member_free(params->setsum[i]);
492 params->setsum[i] = NULL;
498 exit_with_fail(const char *testname, struct member_perf_params *params,
499 unsigned int i, unsigned int j)
501 printf("<<<<<Test %s failed at keysize %d iteration %d type %d>>>>>\n",
502 testname, hashtest_key_lens[params->cycle], i, j);
503 perform_frees(params);
508 run_all_tbl_perf_tests(void)
510 unsigned int i, j, k;
511 struct member_perf_params params;
513 printf("Measuring performance, please wait\n");
516 test_socket_id = rte_socket_id();
518 for (i = 0; i < NUM_KEYSIZES; i++) {
519 if (setup_keys_and_data(¶ms, i, 0) < 0) {
520 printf("Could not create keys/data/table\n");
523 for (j = 0; j < NUM_TYPE; j++) {
525 if (timed_adds(¶ms, j) < 0)
526 return exit_with_fail("timed_adds", ¶ms,
529 for (k = 0; k < NUM_SHUFFLES; k++)
530 shuffle_input_keys(¶ms);
532 if (timed_lookups(¶ms, j) < 0)
533 return exit_with_fail("timed_lookups", ¶ms,
536 if (timed_lookups_bulk(¶ms, j) < 0)
537 return exit_with_fail("timed_lookups_bulk",
540 if (timed_lookups_multimatch(¶ms, j) < 0)
541 return exit_with_fail("timed_lookups_multi",
544 if (timed_lookups_multimatch_bulk(¶ms, j) < 0)
545 return exit_with_fail("timed_lookups_multi_bulk",
548 if (timed_deletes(¶ms, j) < 0)
549 return exit_with_fail("timed_deletes", ¶ms,
552 /* Print a dot to show progress on operations */
557 perform_frees(¶ms);
560 /* Test false positive rate using un-inserted keys */
561 for (i = 0; i < NUM_KEYSIZES; i++) {
562 if (setup_keys_and_data(¶ms, i, 1) < 0) {
563 printf("Could not create keys/data/table\n");
566 for (j = 0; j < NUM_TYPE; j++) {
567 if (timed_miss_lookup(¶ms, j) < 0)
568 return exit_with_fail("timed_miss_lookup",
571 perform_frees(¶ms);
574 printf("\nResults (in CPU cycles/operation)\n");
575 printf("-----------------------------------\n");
576 printf("\n%-18s%-18s%-18s%-18s%-18s%-18s%-18s%-18s%-18s\n",
577 "Keysize", "type", "Add", "Lookup", "Lookup_bulk",
578 "lookup_multi", "lookup_multi_bulk", "Delete",
580 for (i = 0; i < NUM_KEYSIZES; i++) {
581 for (j = 0; j < NUM_TYPE; j++) {
582 printf("%-18d", hashtest_key_lens[i]);
584 for (k = 0; k < NUM_OPERATIONS; k++)
585 printf("%-18"PRIu64, cycles[j][i][k]);
590 printf("\nFalse results rate (and false positive rate)\n");
591 printf("-----------------------------------\n");
592 printf("\n%-18s%-18s%-18s%-18s%-18s%-18s%-18s\n",
593 "Keysize", "type", "fr_single", "fr_bulk", "fr_multi",
594 "fr_multi_bulk", "false_positive_rate");
595 /* Key size not influence False rate so just print out one key size */
596 for (i = 0; i < 1; i++) {
597 for (j = 0; j < NUM_TYPE; j++) {
598 printf("%-18d", hashtest_key_lens[i]);
600 printf("%-18f", (float)false_data[j][i] / NUM_LOOKUPS);
601 printf("%-18f", (float)false_data_bulk[j][i] /
603 printf("%-18f", (float)false_data_multi[j][i] /
605 printf("%-18f", (float)false_data_multi_bulk[j][i] /
607 printf("%-18f", (float)false_hit[j][i] /
616 test_member_perf(void)
619 if (run_all_tbl_perf_tests() < 0)
625 REGISTER_TEST_COMMAND(member_perf_autotest, test_member_perf);