]> git.proxmox.com Git - ceph.git/blob - ceph/src/spdk/dpdk/app/test/test_member_perf.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / dpdk / app / test / test_member_perf.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2017 Intel Corporation
3 */
4
5 #include <stdio.h>
6 #include <inttypes.h>
7
8 #include <rte_lcore.h>
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>
15
16 #include "test.h"
17
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
25 #define BURST_SIZE 64
26 #define VBF_FALSE_RATE 0.03
27
28 static unsigned int test_socket_id;
29
30 enum sstype {
31 HT = 0,
32 CACHE,
33 VBF,
34 NUM_TYPE
35 };
36
37 enum operations {
38 ADD = 0,
39 LOOKUP,
40 LOOKUP_BULK,
41 LOOKUP_MULTI,
42 LOOKUP_MULTI_BULK,
43 DELETE,
44 LOOKUP_MISS,
45 NUM_OPERATIONS
46 };
47
48 struct member_perf_params {
49 struct rte_member_setsum *setsum[NUM_TYPE];
50 uint32_t key_size;
51 unsigned int cycle;
52 };
53
54 static uint32_t hashtest_key_lens[] = {
55 /* standard key sizes */
56 4, 8, 16, 32, 48, 64,
57 /* IPv4 SRC + DST + protocol, unpadded */
58 9,
59 /* IPv4 5-tuple, unpadded */
60 13,
61 /* IPv6 5-tuple, unpadded */
62 37,
63 /* IPv6 5-tuple, padded to 8-byte boundary */
64 40
65 };
66
67 /* Array to store number of cycles per operation */
68 uint64_t cycles[NUM_TYPE][NUM_KEYSIZES][NUM_OPERATIONS];
69 uint64_t false_data[NUM_TYPE][NUM_KEYSIZES];
70 uint64_t false_data_bulk[NUM_TYPE][NUM_KEYSIZES];
71 uint64_t false_data_multi[NUM_TYPE][NUM_KEYSIZES];
72 uint64_t false_data_multi_bulk[NUM_TYPE][NUM_KEYSIZES];
73
74 uint64_t false_hit[NUM_TYPE][NUM_KEYSIZES];
75
76 member_set_t data[NUM_TYPE][/* Array to store the data */KEYS_TO_ADD];
77
78 /* Array to store all input keys */
79 uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE];
80
81 /* Shuffle the keys that have been added, so lookups will be totally random */
82 static void
83 shuffle_input_keys(struct member_perf_params *params)
84 {
85 member_set_t temp_data;
86 unsigned int i, j;
87 uint32_t swap_idx;
88 uint8_t temp_key[MAX_KEYSIZE];
89
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;
101 }
102 }
103 }
104
105 static int key_compare(const void *key1, const void *key2)
106 {
107 return memcmp(key1, key2, MAX_KEYSIZE);
108 }
109
110 struct rte_member_parameters member_params = {
111 .num_keys = MAX_ENTRIES, /* Total hash table entries. */
112 .key_len = 4, /* Length of hash key. */
113
114 /* num_set and false_positive_rate only relevant to vBF */
115 .num_set = VBF_SET_CNT,
116 .false_positive_rate = 0.03,
117 .prim_hash_seed = 0,
118 .sec_hash_seed = 1,
119 .socket_id = 0, /* NUMA Socket ID for memory. */
120 };
121
122 static int
123 setup_keys_and_data(struct member_perf_params *params, unsigned int cycle,
124 int miss)
125 {
126 unsigned int i, j;
127 int num_duplicates;
128
129 params->key_size = hashtest_key_lens[cycle];
130 params->cycle = cycle;
131
132 /* Reset all arrays */
133 for (i = 0; i < params->key_size; i++)
134 keys[0][i] = 0;
135
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;
140
141 data[HT][i] = data[CACHE][i] = (rte_rand() & 0x7FFE) + 1;
142 data[VBF][i] = rte_rand() % VBF_SET_CNT + 1;
143 }
144
145 /* Remove duplicates from the keys array */
146 do {
147 num_duplicates = 0;
148
149 /* Sort the list of keys to make it easier to find duplicates */
150 qsort(keys, KEYS_TO_ADD, MAX_KEYSIZE, key_compare);
151
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 */
158 num_duplicates++;
159 for (j = 0; j < params->key_size; j++)
160 keys[i][j] = rte_rand() & 0xFF;
161 }
162 }
163 } while (num_duplicates != 0);
164
165 /* Shuffle the random values again */
166 shuffle_input_keys(params);
167
168 /* For testing miss lookup, we insert half and lookup the other half */
169 unsigned int entry_cnt, bf_key_cnt;
170 if (!miss) {
171 entry_cnt = MAX_ENTRIES;
172 bf_key_cnt = KEYS_TO_ADD;
173 } else {
174 entry_cnt = MAX_ENTRIES / 2;
175 bf_key_cnt = KEYS_TO_ADD / 2;
176 }
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");
187
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");
193
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)
202 return -1;
203 }
204
205 return 0;
206 }
207
208 static int
209 timed_adds(struct member_perf_params *params, int type)
210 {
211 const uint64_t start_tsc = rte_rdtsc();
212 unsigned int i, a;
213 int32_t ret;
214
215 for (i = 0; i < KEYS_TO_ADD; i++) {
216 ret = rte_member_add(params->setsum[type], &keys[i],
217 data[type][i]);
218 if (ret < 0) {
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);
223
224 return -1;
225 }
226 }
227
228 const uint64_t end_tsc = rte_rdtsc();
229 const uint64_t time_taken = end_tsc - start_tsc;
230
231 cycles[type][params->cycle][ADD] = time_taken / KEYS_TO_ADD;
232 return 0;
233 }
234
235 static int
236 timed_lookups(struct member_perf_params *params, int type)
237 {
238 unsigned int i, j;
239
240 false_data[type][params->cycle] = 0;
241
242 const uint64_t start_tsc = rte_rdtsc();
243 member_set_t result;
244 int ret;
245
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],
249 &result);
250 if (ret < 0) {
251 printf("lookup wrong internally");
252 return -1;
253 }
254 if (type == HT && result == RTE_MEMBER_NO_MATCH) {
255 printf("HT mode shouldn't have false negative");
256 return -1;
257 }
258 if (result != data[type][j])
259 false_data[type][params->cycle]++;
260 }
261 }
262
263 const uint64_t end_tsc = rte_rdtsc();
264 const uint64_t time_taken = end_tsc - start_tsc;
265
266 cycles[type][params->cycle][LOOKUP] = time_taken / NUM_LOOKUPS;
267
268 return 0;
269 }
270
271 static int
272 timed_lookups_bulk(struct member_perf_params *params, int type)
273 {
274 unsigned int i, j, k;
275 member_set_t result[BURST_SIZE] = {0};
276 const void *keys_burst[BURST_SIZE];
277 int ret;
278
279 false_data_bulk[type][params->cycle] = 0;
280
281 const uint64_t start_tsc = rte_rdtsc();
282
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];
287
288 ret = rte_member_lookup_bulk(params->setsum[type],
289 keys_burst,
290 BURST_SIZE,
291 result);
292 if (ret <= 0) {
293 printf("lookup bulk has wrong return value\n");
294 return -1;
295 }
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 "
301 "false negative");
302 return -1;
303 }
304 if (result[k] != data[type][data_idx])
305 false_data_bulk[type][params->cycle]++;
306 }
307 }
308 }
309
310 const uint64_t end_tsc = rte_rdtsc();
311 const uint64_t time_taken = end_tsc - start_tsc;
312
313 cycles[type][params->cycle][LOOKUP_BULK] = time_taken / NUM_LOOKUPS;
314
315 return 0;
316 }
317
318 static int
319 timed_lookups_multimatch(struct member_perf_params *params, int type)
320 {
321 unsigned int i, j;
322 member_set_t result[RTE_MEMBER_BUCKET_ENTRIES] = {0};
323 int ret;
324 false_data_multi[type][params->cycle] = 0;
325
326 const uint64_t start_tsc = rte_rdtsc();
327
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);
335 }
336 if (type == HT && ret == 0) {
337 printf("HT mode shouldn't have false negative");
338 return -1;
339 }
340 /*
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].
344 */
345 if (result[0] != data[type][j])
346 false_data_multi[type][params->cycle]++;
347 }
348 }
349
350 const uint64_t end_tsc = rte_rdtsc();
351 const uint64_t time_taken = end_tsc - start_tsc;
352
353 cycles[type][params->cycle][LOOKUP_MULTI] = time_taken / NUM_LOOKUPS;
354
355 return 0;
356 }
357
358 static int
359 timed_lookups_multimatch_bulk(struct member_perf_params *params, int type)
360 {
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];
365 int ret;
366
367 false_data_multi_bulk[type][params->cycle] = 0;
368
369 const uint64_t start_tsc = rte_rdtsc();
370
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];
375
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);
381 if (ret < 0) {
382 printf("lookup multimatch bulk has wrong return"
383 " value\n");
384 return -1;
385 }
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");
390 return -1;
391 }
392 if (type == HT && match_count[k] == 0) {
393 printf("HT mode shouldn't have "
394 "false negative");
395 return -1;
396 }
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]++;
400 }
401 }
402 }
403
404 const uint64_t end_tsc = rte_rdtsc();
405 const uint64_t time_taken = end_tsc - start_tsc;
406
407 cycles[type][params->cycle][LOOKUP_MULTI_BULK] = time_taken /
408 NUM_LOOKUPS;
409
410 return 0;
411 }
412
413 static int
414 timed_deletes(struct member_perf_params *params, int type)
415 {
416 unsigned int i;
417 int32_t ret;
418
419 if (type == VBF)
420 return 0;
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],
424 data[type][i]);
425 if (type != CACHE && ret < 0) {
426 printf("delete error\n");
427 return -1;
428 }
429 }
430
431 const uint64_t end_tsc = rte_rdtsc();
432 const uint64_t time_taken = end_tsc - start_tsc;
433
434 cycles[type][params->cycle][DELETE] = time_taken / KEYS_TO_ADD;
435
436 return 0;
437 }
438
439 static int
440 timed_miss_lookup(struct member_perf_params *params, int type)
441 {
442 unsigned int i, j;
443 int ret;
444
445 false_hit[type][params->cycle] = 0;
446
447 for (i = 0; i < KEYS_TO_ADD / 2; i++) {
448 ret = rte_member_add(params->setsum[type], &keys[i],
449 data[type][i]);
450 if (ret < 0) {
451 unsigned int a;
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);
456
457 return -1;
458 }
459 }
460
461 const uint64_t start_tsc = rte_rdtsc();
462 member_set_t result;
463
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],
467 &result);
468 if (ret < 0) {
469 printf("lookup wrong internally");
470 return -1;
471 }
472 if (result != RTE_MEMBER_NO_MATCH)
473 false_hit[type][params->cycle]++;
474 }
475 }
476
477 const uint64_t end_tsc = rte_rdtsc();
478 const uint64_t time_taken = end_tsc - start_tsc;
479
480 cycles[type][params->cycle][LOOKUP_MISS] = time_taken / NUM_LOOKUPS;
481
482 return 0;
483 }
484
485 static void
486 perform_frees(struct member_perf_params *params)
487 {
488 int i;
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;
493 }
494 }
495 }
496
497 static int
498 exit_with_fail(const char *testname, struct member_perf_params *params,
499 unsigned int i, unsigned int j)
500 {
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);
504 return -1;
505 }
506
507 static int
508 run_all_tbl_perf_tests(void)
509 {
510 unsigned int i, j, k;
511 struct member_perf_params params;
512
513 printf("Measuring performance, please wait\n");
514 fflush(stdout);
515
516 test_socket_id = rte_socket_id();
517
518 for (i = 0; i < NUM_KEYSIZES; i++) {
519 if (setup_keys_and_data(&params, i, 0) < 0) {
520 printf("Could not create keys/data/table\n");
521 return -1;
522 }
523 for (j = 0; j < NUM_TYPE; j++) {
524
525 if (timed_adds(&params, j) < 0)
526 return exit_with_fail("timed_adds", &params,
527 i, j);
528
529 for (k = 0; k < NUM_SHUFFLES; k++)
530 shuffle_input_keys(&params);
531
532 if (timed_lookups(&params, j) < 0)
533 return exit_with_fail("timed_lookups", &params,
534 i, j);
535
536 if (timed_lookups_bulk(&params, j) < 0)
537 return exit_with_fail("timed_lookups_bulk",
538 &params, i, j);
539
540 if (timed_lookups_multimatch(&params, j) < 0)
541 return exit_with_fail("timed_lookups_multi",
542 &params, i, j);
543
544 if (timed_lookups_multimatch_bulk(&params, j) < 0)
545 return exit_with_fail("timed_lookups_multi_bulk",
546 &params, i, j);
547
548 if (timed_deletes(&params, j) < 0)
549 return exit_with_fail("timed_deletes", &params,
550 i, j);
551
552 /* Print a dot to show progress on operations */
553 }
554 printf(".");
555 fflush(stdout);
556
557 perform_frees(&params);
558 }
559
560 /* Test false positive rate using un-inserted keys */
561 for (i = 0; i < NUM_KEYSIZES; i++) {
562 if (setup_keys_and_data(&params, i, 1) < 0) {
563 printf("Could not create keys/data/table\n");
564 return -1;
565 }
566 for (j = 0; j < NUM_TYPE; j++) {
567 if (timed_miss_lookup(&params, j) < 0)
568 return exit_with_fail("timed_miss_lookup",
569 &params, i, j);
570 }
571 perform_frees(&params);
572 }
573
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",
579 "miss_lookup");
580 for (i = 0; i < NUM_KEYSIZES; i++) {
581 for (j = 0; j < NUM_TYPE; j++) {
582 printf("%-18d", hashtest_key_lens[i]);
583 printf("%-18d", j);
584 for (k = 0; k < NUM_OPERATIONS; k++)
585 printf("%-18"PRIu64, cycles[j][i][k]);
586 printf("\n");
587 }
588 }
589
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]);
599 printf("%-18d", j);
600 printf("%-18f", (float)false_data[j][i] / NUM_LOOKUPS);
601 printf("%-18f", (float)false_data_bulk[j][i] /
602 NUM_LOOKUPS);
603 printf("%-18f", (float)false_data_multi[j][i] /
604 NUM_LOOKUPS);
605 printf("%-18f", (float)false_data_multi_bulk[j][i] /
606 NUM_LOOKUPS);
607 printf("%-18f", (float)false_hit[j][i] /
608 NUM_LOOKUPS);
609 printf("\n");
610 }
611 }
612 return 0;
613 }
614
615 static int
616 test_member_perf(void)
617 {
618
619 if (run_all_tbl_perf_tests() < 0)
620 return -1;
621
622 return 0;
623 }
624
625 REGISTER_TEST_COMMAND(member_perf_autotest, test_member_perf);