New upstream version 16.11.9
[deb_dpdk.git] / app / test / test_hash_perf.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stdio.h>
35 #include <inttypes.h>
36
37 #include <rte_lcore.h>
38 #include <rte_cycles.h>
39 #include <rte_malloc.h>
40 #include <rte_hash.h>
41 #include <rte_hash_crc.h>
42 #include <rte_jhash.h>
43 #include <rte_fbk_hash.h>
44 #include <rte_random.h>
45 #include <rte_string_fns.h>
46
47 #include "test.h"
48
49 #define MAX_ENTRIES (1 << 19)
50 #define KEYS_TO_ADD (MAX_ENTRIES * 3 / 4) /* 75% table utilization */
51 #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */
52 /* BUCKET_SIZE should be same as RTE_HASH_BUCKET_ENTRIES in rte_hash library */
53 #define BUCKET_SIZE 8
54 #define NUM_BUCKETS (MAX_ENTRIES / BUCKET_SIZE)
55 #define MAX_KEYSIZE 64
56 #define NUM_KEYSIZES 10
57 #define NUM_SHUFFLES 10
58 #define BURST_SIZE 16
59
60 enum operations {
61         ADD = 0,
62         LOOKUP,
63         LOOKUP_MULTI,
64         DELETE,
65         NUM_OPERATIONS
66 };
67
68 static uint32_t hashtest_key_lens[] = {
69         /* standard key sizes */
70         4, 8, 16, 32, 48, 64,
71         /* IPv4 SRC + DST + protocol, unpadded */
72         9,
73         /* IPv4 5-tuple, unpadded */
74         13,
75         /* IPv6 5-tuple, unpadded */
76         37,
77         /* IPv6 5-tuple, padded to 8-byte boundary */
78         40
79 };
80
81 struct rte_hash *h[NUM_KEYSIZES];
82
83 /* Array that stores if a slot is full */
84 uint8_t slot_taken[MAX_ENTRIES];
85
86 /* Array to store number of cycles per operation */
87 uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS][2][2];
88
89 /* Array to store all input keys */
90 uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE];
91
92 /* Array to store the precomputed hash for 'keys' */
93 hash_sig_t signatures[KEYS_TO_ADD];
94
95 /* Array to store how many busy entries have each bucket */
96 uint8_t buckets[NUM_BUCKETS];
97
98 /* Array to store the positions where keys are added */
99 int32_t positions[KEYS_TO_ADD];
100
101 /* Parameters used for hash table in unit test functions. */
102 static struct rte_hash_parameters ut_params = {
103         .entries = MAX_ENTRIES,
104         .hash_func = rte_jhash,
105         .hash_func_init_val = 0,
106 };
107
108 static int
109 create_table(unsigned with_data, unsigned table_index)
110 {
111         char name[RTE_HASH_NAMESIZE];
112
113         if (with_data)
114                 /* Table will store 8-byte data */
115                 sprintf(name, "test_hash%d_data", hashtest_key_lens[table_index]);
116         else
117                 sprintf(name, "test_hash%d", hashtest_key_lens[table_index]);
118
119         ut_params.name = name;
120         ut_params.key_len = hashtest_key_lens[table_index];
121         ut_params.socket_id = rte_socket_id();
122         h[table_index] = rte_hash_find_existing(name);
123         if (h[table_index] != NULL)
124                 /*
125                  * If table was already created, free it to create it again,
126                  * so we force it is empty
127                  */
128                 rte_hash_free(h[table_index]);
129         h[table_index] = rte_hash_create(&ut_params);
130         if (h[table_index] == NULL) {
131                 printf("Error creating table\n");
132                 return -1;
133         }
134         return 0;
135
136 }
137
138 /* Shuffle the keys that have been added, so lookups will be totally random */
139 static void
140 shuffle_input_keys(unsigned table_index)
141 {
142         unsigned i;
143         uint32_t swap_idx;
144         uint8_t temp_key[MAX_KEYSIZE];
145         hash_sig_t temp_signature;
146         int32_t temp_position;
147
148         for (i = KEYS_TO_ADD - 1; i > 0; i--) {
149                 swap_idx = rte_rand() % i;
150
151                 memcpy(temp_key, keys[i], hashtest_key_lens[table_index]);
152                 temp_signature = signatures[i];
153                 temp_position = positions[i];
154
155                 memcpy(keys[i], keys[swap_idx], hashtest_key_lens[table_index]);
156                 signatures[i] = signatures[swap_idx];
157                 positions[i] = positions[swap_idx];
158
159                 memcpy(keys[swap_idx], temp_key, hashtest_key_lens[table_index]);
160                 signatures[swap_idx] = temp_signature;
161                 positions[swap_idx] = temp_position;
162         }
163 }
164
165 /*
166  * Looks for random keys which
167  * ALL can fit in hash table (no errors)
168  */
169 static int
170 get_input_keys(unsigned with_pushes, unsigned table_index)
171 {
172         unsigned i, j;
173         unsigned bucket_idx, incr, success = 1;
174         uint8_t k = 0;
175         int32_t ret;
176         const uint32_t bucket_bitmask = NUM_BUCKETS - 1;
177
178         /* Reset all arrays */
179         for (i = 0; i < MAX_ENTRIES; i++)
180                 slot_taken[i] = 0;
181
182         for (i = 0; i < NUM_BUCKETS; i++)
183                 buckets[i] = 0;
184
185         for (j = 0; j < hashtest_key_lens[table_index]; j++)
186                 keys[0][j] = 0;
187
188         /*
189          * Add only entries that are not duplicated and that fits in the table
190          * (cannot store more than BUCKET_SIZE entries in a bucket).
191          * Regardless a key has been added correctly or not (success),
192          * the next one to try will be increased by 1.
193          */
194         for (i = 0; i < KEYS_TO_ADD;) {
195                 incr = 0;
196                 if (i != 0) {
197                         keys[i][0] = ++k;
198                         /* Overflow, need to increment the next byte */
199                         if (keys[i][0] == 0)
200                                 incr = 1;
201                         for (j = 1; j < hashtest_key_lens[table_index]; j++) {
202                                 /* Do not increase next byte */
203                                 if (incr == 0)
204                                         if (success == 1)
205                                                 keys[i][j] = keys[i - 1][j];
206                                         else
207                                                 keys[i][j] = keys[i][j];
208                                 /* Increase next byte by one */
209                                 else {
210                                         if (success == 1)
211                                                 keys[i][j] = keys[i-1][j] + 1;
212                                         else
213                                                 keys[i][j] = keys[i][j] + 1;
214                                         if (keys[i][j] == 0)
215                                                 incr = 1;
216                                         else
217                                                 incr = 0;
218                                 }
219                         }
220                 }
221                 success = 0;
222                 signatures[i] = rte_hash_hash(h[table_index], keys[i]);
223                 bucket_idx = signatures[i] & bucket_bitmask;
224                 /*
225                  * If we are not inserting keys in secondary location,
226                  * when bucket is full, do not try to insert the key
227                  */
228                 if (with_pushes == 0)
229                         if (buckets[bucket_idx] == BUCKET_SIZE)
230                                 continue;
231
232                 /* If key can be added, leave in successful key arrays "keys" */
233                 ret = rte_hash_add_key_with_hash(h[table_index], keys[i],
234                                                 signatures[i]);
235                 if (ret >= 0) {
236                         /* If key is already added, ignore the entry and do not store */
237                         if (slot_taken[ret])
238                                 continue;
239                         else {
240                                 /* Store the returned position and mark slot as taken */
241                                 slot_taken[ret] = 1;
242                                 positions[i] = ret;
243                                 buckets[bucket_idx]++;
244                                 success = 1;
245                                 i++;
246                         }
247                 }
248         }
249
250         /* Reset the table, so we can measure the time to add all the entries */
251         rte_hash_free(h[table_index]);
252         h[table_index] = rte_hash_create(&ut_params);
253
254         return 0;
255 }
256
257 static int
258 timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index)
259 {
260         unsigned i;
261         const uint64_t start_tsc = rte_rdtsc();
262         void *data;
263         int32_t ret;
264
265         for (i = 0; i < KEYS_TO_ADD; i++) {
266                 data = (void *) ((uintptr_t) signatures[i]);
267                 if (with_hash && with_data) {
268                         ret = rte_hash_add_key_with_hash_data(h[table_index],
269                                                 (const void *) keys[i],
270                                                 signatures[i], data);
271                         if (ret < 0) {
272                                 printf("Failed to add key number %u\n", ret);
273                                 return -1;
274                         }
275                 } else if (with_hash && !with_data) {
276                         ret = rte_hash_add_key_with_hash(h[table_index],
277                                                 (const void *) keys[i],
278                                                 signatures[i]);
279                         if (ret >= 0)
280                                 positions[i] = ret;
281                         else {
282                                 printf("Failed to add key number %u\n", ret);
283                                 return -1;
284                         }
285                 } else if (!with_hash && with_data) {
286                         ret = rte_hash_add_key_data(h[table_index],
287                                                 (const void *) keys[i],
288                                                 data);
289                         if (ret < 0) {
290                                 printf("Failed to add key number %u\n", ret);
291                                 return -1;
292                         }
293                 } else {
294                         ret = rte_hash_add_key(h[table_index], keys[i]);
295                         if (ret >= 0)
296                                 positions[i] = ret;
297                         else {
298                                 printf("Failed to add key number %u\n", ret);
299                                 return -1;
300                         }
301                 }
302         }
303
304         const uint64_t end_tsc = rte_rdtsc();
305         const uint64_t time_taken = end_tsc - start_tsc;
306
307         cycles[table_index][ADD][with_hash][with_data] = time_taken/KEYS_TO_ADD;
308
309         return 0;
310 }
311
312 static int
313 timed_lookups(unsigned with_hash, unsigned with_data, unsigned table_index)
314 {
315         unsigned i, j;
316         const uint64_t start_tsc = rte_rdtsc();
317         void *ret_data;
318         void *expected_data;
319         int32_t ret;
320
321         for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) {
322                 for (j = 0; j < KEYS_TO_ADD; j++) {
323                         if (with_hash && with_data) {
324                                 ret = rte_hash_lookup_with_hash_data(h[table_index],
325                                                         (const void *) keys[j],
326                                                         signatures[j], &ret_data);
327                                 if (ret < 0) {
328                                         printf("Key number %u was not found\n", j);
329                                         return -1;
330                                 }
331                                 expected_data = (void *) ((uintptr_t) signatures[j]);
332                                 if (ret_data != expected_data) {
333                                         printf("Data returned for key number %u is %p,"
334                                                " but should be %p\n", j, ret_data,
335                                                 expected_data);
336                                         return -1;
337                                 }
338                         } else if (with_hash && !with_data) {
339                                 ret = rte_hash_lookup_with_hash(h[table_index],
340                                                         (const void *) keys[j],
341                                                         signatures[j]);
342                                 if (ret < 0 || ret != positions[j]) {
343                                         printf("Key looked up in %d, should be in %d\n",
344                                                 ret, positions[j]);
345                                         return -1;
346                                 }
347                         } else if (!with_hash && with_data) {
348                                 ret = rte_hash_lookup_data(h[table_index],
349                                                         (const void *) keys[j], &ret_data);
350                                 if (ret < 0) {
351                                         printf("Key number %u was not found\n", j);
352                                         return -1;
353                                 }
354                                 expected_data = (void *) ((uintptr_t) signatures[j]);
355                                 if (ret_data != expected_data) {
356                                         printf("Data returned for key number %u is %p,"
357                                                " but should be %p\n", j, ret_data,
358                                                 expected_data);
359                                         return -1;
360                                 }
361                         } else {
362                                 ret = rte_hash_lookup(h[table_index], keys[j]);
363                                 if (ret < 0 || ret != positions[j]) {
364                                         printf("Key looked up in %d, should be in %d\n",
365                                                 ret, positions[j]);
366                                         return -1;
367                                 }
368                         }
369                 }
370         }
371
372         const uint64_t end_tsc = rte_rdtsc();
373         const uint64_t time_taken = end_tsc - start_tsc;
374
375         cycles[table_index][LOOKUP][with_hash][with_data] = time_taken/NUM_LOOKUPS;
376
377         return 0;
378 }
379
380 static int
381 timed_lookups_multi(unsigned with_data, unsigned table_index)
382 {
383         unsigned i, j, k;
384         int32_t positions_burst[BURST_SIZE];
385         const void *keys_burst[BURST_SIZE];
386         void *expected_data[BURST_SIZE];
387         void *ret_data[BURST_SIZE];
388         uint64_t hit_mask;
389         int ret;
390
391         const uint64_t start_tsc = rte_rdtsc();
392
393         for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) {
394                 for (j = 0; j < KEYS_TO_ADD/BURST_SIZE; j++) {
395                         for (k = 0; k < BURST_SIZE; k++)
396                                 keys_burst[k] = keys[j * BURST_SIZE + k];
397                         if (with_data) {
398                                 ret = rte_hash_lookup_bulk_data(h[table_index],
399                                         (const void **) keys_burst,
400                                         BURST_SIZE,
401                                         &hit_mask,
402                                         ret_data);
403                                 if (ret != BURST_SIZE) {
404                                         printf("Expect to find %u keys,"
405                                                " but found %d\n", BURST_SIZE, ret);
406                                         return -1;
407                                 }
408                                 for (k = 0; k < BURST_SIZE; k++) {
409                                         if ((hit_mask & (1ULL << k))  == 0) {
410                                                 printf("Key number %u not found\n",
411                                                         j * BURST_SIZE + k);
412                                                 return -1;
413                                         }
414                                         expected_data[k] = (void *) ((uintptr_t) signatures[j * BURST_SIZE + k]);
415                                         if (ret_data[k] != expected_data[k]) {
416                                                 printf("Data returned for key number %u is %p,"
417                                                        " but should be %p\n", j * BURST_SIZE + k,
418                                                         ret_data[k], expected_data[k]);
419                                                 return -1;
420                                         }
421                                 }
422                         } else {
423                                 rte_hash_lookup_bulk(h[table_index],
424                                                 (const void **) keys_burst,
425                                                 BURST_SIZE,
426                                                 positions_burst);
427                                 for (k = 0; k < BURST_SIZE; k++) {
428                                         if (positions_burst[k] != positions[j * BURST_SIZE + k]) {
429                                                 printf("Key looked up in %d, should be in %d\n",
430                                                         positions_burst[k],
431                                                         positions[j * BURST_SIZE + k]);
432                                                 return -1;
433                                         }
434                                 }
435                         }
436                 }
437         }
438
439         const uint64_t end_tsc = rte_rdtsc();
440         const uint64_t time_taken = end_tsc - start_tsc;
441
442         cycles[table_index][LOOKUP_MULTI][0][with_data] = time_taken/NUM_LOOKUPS;
443
444         return 0;
445 }
446
447 static int
448 timed_deletes(unsigned with_hash, unsigned with_data, unsigned table_index)
449 {
450         unsigned i;
451         const uint64_t start_tsc = rte_rdtsc();
452         int32_t ret;
453
454         for (i = 0; i < KEYS_TO_ADD; i++) {
455                 /* There are no delete functions with data, so just call two functions */
456                 if (with_hash)
457                         ret = rte_hash_del_key_with_hash(h[table_index],
458                                                         (const void *) keys[i],
459                                                         signatures[i]);
460                 else
461                         ret = rte_hash_del_key(h[table_index],
462                                                         (const void *) keys[i]);
463                 if (ret >= 0)
464                         positions[i] = ret;
465                 else {
466                         printf("Failed to add key number %u\n", ret);
467                         return -1;
468                 }
469         }
470
471         const uint64_t end_tsc = rte_rdtsc();
472         const uint64_t time_taken = end_tsc - start_tsc;
473
474         cycles[table_index][DELETE][with_hash][with_data] = time_taken/KEYS_TO_ADD;
475
476         return 0;
477 }
478
479 static void
480 free_table(unsigned table_index)
481 {
482         rte_hash_free(h[table_index]);
483 }
484
485 static void
486 reset_table(unsigned table_index)
487 {
488         rte_hash_reset(h[table_index]);
489 }
490
491 static int
492 run_all_tbl_perf_tests(unsigned with_pushes)
493 {
494         unsigned i, j, with_data, with_hash;
495
496         printf("Measuring performance, please wait");
497         fflush(stdout);
498
499         for (with_data = 0; with_data <= 1; with_data++) {
500                 for (i = 0; i < NUM_KEYSIZES; i++) {
501                         if (create_table(with_data, i) < 0)
502                                 return -1;
503
504                         if (get_input_keys(with_pushes, i) < 0)
505                                 return -1;
506                         for (with_hash = 0; with_hash <= 1; with_hash++) {
507                                 if (timed_adds(with_hash, with_data, i) < 0)
508                                         return -1;
509
510                                 for (j = 0; j < NUM_SHUFFLES; j++)
511                                         shuffle_input_keys(i);
512
513                                 if (timed_lookups(with_hash, with_data, i) < 0)
514                                         return -1;
515
516                                 if (timed_lookups_multi(with_data, i) < 0)
517                                         return -1;
518
519                                 if (timed_deletes(with_hash, with_data, i) < 0)
520                                         return -1;
521
522                                 /* Print a dot to show progress on operations */
523                                 printf(".");
524                                 fflush(stdout);
525
526                                 reset_table(i);
527                         }
528                         free_table(i);
529                 }
530         }
531
532         printf("\nResults (in CPU cycles/operation)\n");
533         printf("-----------------------------------\n");
534         for (with_data = 0; with_data <= 1; with_data++) {
535                 if (with_data)
536                         printf("\n Operations with 8-byte data\n");
537                 else
538                         printf("\n Operations without data\n");
539                 for (with_hash = 0; with_hash <= 1; with_hash++) {
540                         if (with_hash)
541                                 printf("\nWith pre-computed hash values\n");
542                         else
543                                 printf("\nWithout pre-computed hash values\n");
544
545                         printf("\n%-18s%-18s%-18s%-18s%-18s\n",
546                         "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
547                         for (i = 0; i < NUM_KEYSIZES; i++) {
548                                 printf("%-18d", hashtest_key_lens[i]);
549                                 for (j = 0; j < NUM_OPERATIONS; j++)
550                                         printf("%-18"PRIu64, cycles[i][j][with_hash][with_data]);
551                                 printf("\n");
552                         }
553                 }
554         }
555         return 0;
556 }
557
558 /* Control operation of performance testing of fbk hash. */
559 #define LOAD_FACTOR 0.667       /* How full to make the hash table. */
560 #define TEST_SIZE 1000000       /* How many operations to time. */
561 #define TEST_ITERATIONS 30      /* How many measurements to take. */
562 #define ENTRIES (1 << 15)       /* How many entries. */
563
564 static int
565 fbk_hash_perf_test(void)
566 {
567         struct rte_fbk_hash_params params = {
568                 .name = "fbk_hash_test",
569                 .entries = ENTRIES,
570                 .entries_per_bucket = 4,
571                 .socket_id = rte_socket_id(),
572         };
573         struct rte_fbk_hash_table *handle = NULL;
574         uint32_t *keys = NULL;
575         unsigned indexes[TEST_SIZE];
576         uint64_t lookup_time = 0;
577         unsigned added = 0;
578         unsigned value = 0;
579         uint32_t key;
580         uint16_t val;
581         unsigned i, j;
582
583         handle = rte_fbk_hash_create(&params);
584         if (handle == NULL) {
585                 printf("Error creating table\n");
586                 return -1;
587         }
588
589         keys = rte_zmalloc(NULL, ENTRIES * sizeof(*keys), 0);
590         if (keys == NULL) {
591                 printf("fbk hash: memory allocation for key store failed\n");
592                 return -1;
593         }
594
595         /* Generate random keys and values. */
596         for (i = 0; i < ENTRIES; i++) {
597                 key = (uint32_t)rte_rand();
598                 key = ((uint64_t)key << 32) | (uint64_t)rte_rand();
599                 val = (uint16_t)rte_rand();
600
601                 if (rte_fbk_hash_add_key(handle, key, val) == 0) {
602                         keys[added] = key;
603                         added++;
604                 }
605                 if (added > (LOAD_FACTOR * ENTRIES))
606                         break;
607         }
608
609         for (i = 0; i < TEST_ITERATIONS; i++) {
610                 uint64_t begin;
611                 uint64_t end;
612
613                 /* Generate random indexes into keys[] array. */
614                 for (j = 0; j < TEST_SIZE; j++)
615                         indexes[j] = rte_rand() % added;
616
617                 begin = rte_rdtsc();
618                 /* Do lookups */
619                 for (j = 0; j < TEST_SIZE; j++)
620                         value += rte_fbk_hash_lookup(handle, keys[indexes[j]]);
621
622                 end = rte_rdtsc();
623                 lookup_time += (double)(end - begin);
624         }
625
626         printf("\n\n *** FBK Hash function performance test results ***\n");
627         /*
628          * The use of the 'value' variable ensures that the hash lookup is not
629          * being optimised out by the compiler.
630          */
631         if (value != 0)
632                 printf("Number of ticks per lookup = %g\n",
633                         (double)lookup_time /
634                         ((double)TEST_ITERATIONS * (double)TEST_SIZE));
635
636         rte_fbk_hash_free(handle);
637
638         return 0;
639 }
640
641 static int
642 test_hash_perf(void)
643 {
644         unsigned with_pushes;
645
646         for (with_pushes = 0; with_pushes <= 1; with_pushes++) {
647                 if (with_pushes == 0)
648                         printf("\nALL ELEMENTS IN PRIMARY LOCATION\n");
649                 else
650                         printf("\nELEMENTS IN PRIMARY OR SECONDARY LOCATION\n");
651                 if (run_all_tbl_perf_tests(with_pushes) < 0)
652                         return -1;
653         }
654         if (fbk_hash_perf_test() < 0)
655                 return -1;
656
657         return 0;
658 }
659
660 REGISTER_TEST_COMMAND(hash_perf_autotest, test_hash_perf);