mbuf: fix performance with 128-byte cache line
[dpdk.git] / app / test / test_hash_scaling.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 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 <rte_cycles.h>
35 #include <rte_hash.h>
36 #include <rte_hash_crc.h>
37 #include <rte_spinlock.h>
38 #include <rte_launch.h>
39
40 #include "test.h"
41
42 /*
43  * Check condition and return an error if true. Assumes that "handle" is the
44  * name of the hash structure pointer to be freed.
45  */
46 #define RETURN_IF_ERROR(cond, str, ...) do {                            \
47         if (cond) {                                                     \
48                 printf("ERROR line %d: " str "\n", __LINE__,            \
49                                                         ##__VA_ARGS__); \
50                 if (handle)                                             \
51                         rte_hash_free(handle);                          \
52                 return -1;                                              \
53         }                                                               \
54 } while (0)
55
56 enum locking_mode_t {
57         NORMAL_LOCK,
58         LOCK_ELISION,
59         NULL_LOCK
60 };
61
62 struct {
63         uint32_t num_iterations;
64         struct rte_hash *h;
65         rte_spinlock_t *lock;
66         int locking_mode;
67 } tbl_scaling_test_params;
68
69 static rte_atomic64_t gcycles;
70
71 static int test_hash_scaling_worker(__attribute__((unused)) void *arg)
72 {
73         uint64_t i, key;
74         uint32_t thr_id = rte_sys_gettid();
75         uint64_t begin, cycles = 0;
76
77         switch (tbl_scaling_test_params.locking_mode) {
78
79         case NORMAL_LOCK:
80
81                 for (i = 0; i < tbl_scaling_test_params.num_iterations; i++) {
82                         /*      different threads get different keys because
83                                 we use the thread-id in the key computation
84                          */
85                         key = rte_hash_crc(&i, sizeof(i), thr_id);
86                         begin = rte_rdtsc_precise();
87                         rte_spinlock_lock(tbl_scaling_test_params.lock);
88                         rte_hash_add_key(tbl_scaling_test_params.h, &key);
89                         rte_spinlock_unlock(tbl_scaling_test_params.lock);
90                         cycles += rte_rdtsc_precise() - begin;
91                 }
92                 break;
93
94         case LOCK_ELISION:
95
96                 for (i = 0; i < tbl_scaling_test_params.num_iterations; i++) {
97                         key = rte_hash_crc(&i, sizeof(i), thr_id);
98                         begin = rte_rdtsc_precise();
99                         rte_spinlock_lock_tm(tbl_scaling_test_params.lock);
100                         rte_hash_add_key(tbl_scaling_test_params.h, &key);
101                         rte_spinlock_unlock_tm(tbl_scaling_test_params.lock);
102                         cycles += rte_rdtsc_precise() - begin;
103                 }
104                 break;
105
106         default:
107
108                 for (i = 0; i < tbl_scaling_test_params.num_iterations; i++) {
109                         key = rte_hash_crc(&i, sizeof(i), thr_id);
110                         begin = rte_rdtsc_precise();
111                         rte_hash_add_key(tbl_scaling_test_params.h, &key);
112                         cycles += rte_rdtsc_precise() - begin;
113                 }
114         }
115
116         rte_atomic64_add(&gcycles, cycles);
117
118         return 0;
119 }
120
121 /*
122  * Do scalability perf tests.
123  */
124 static int
125 test_hash_scaling(int locking_mode)
126 {
127         static unsigned calledCount =    1;
128         uint32_t num_iterations = 1024*1024;
129         uint64_t i, key;
130         struct rte_hash_parameters hash_params = {
131                 .entries = num_iterations*2,
132                 .key_len = sizeof(key),
133                 .hash_func = rte_hash_crc,
134                 .hash_func_init_val = 0,
135                 .socket_id = rte_socket_id(),
136                 .extra_flag = RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT
137         };
138         struct rte_hash *handle;
139         char name[RTE_HASH_NAMESIZE];
140         rte_spinlock_t lock;
141
142         rte_spinlock_init(&lock);
143
144         snprintf(name, 32, "test%u", calledCount++);
145         hash_params.name = name;
146
147         handle = rte_hash_create(&hash_params);
148         RETURN_IF_ERROR(handle == NULL, "hash creation failed");
149
150         tbl_scaling_test_params.num_iterations =
151                 num_iterations/rte_lcore_count();
152         tbl_scaling_test_params.h = handle;
153         tbl_scaling_test_params.lock = &lock;
154         tbl_scaling_test_params.locking_mode = locking_mode;
155
156         rte_atomic64_init(&gcycles);
157         rte_atomic64_clear(&gcycles);
158
159         /* fill up to initial size */
160         for (i = 0; i < num_iterations; i++) {
161                 key = rte_hash_crc(&i, sizeof(i), 0xabcdabcd);
162                 rte_hash_add_key(tbl_scaling_test_params.h, &key);
163         }
164
165         rte_eal_mp_remote_launch(test_hash_scaling_worker, NULL, CALL_MASTER);
166         rte_eal_mp_wait_lcore();
167
168         unsigned long long int cycles_per_operation =
169                 rte_atomic64_read(&gcycles)/
170                 (tbl_scaling_test_params.num_iterations*rte_lcore_count());
171         const char *lock_name;
172
173         switch (locking_mode) {
174         case NORMAL_LOCK:
175                 lock_name = "normal spinlock";
176                 break;
177         case LOCK_ELISION:
178                 lock_name = "lock elision";
179                 break;
180         default:
181                 lock_name = "null lock";
182         }
183         printf("--------------------------------------------------------\n");
184         printf("Cores: %d; %s mode ->  cycles per operation: %llu\n",
185                 rte_lcore_count(), lock_name, cycles_per_operation);
186         printf("--------------------------------------------------------\n");
187         /* CSV output */
188         printf(">>>%d,%s,%llu\n", rte_lcore_count(), lock_name,
189                 cycles_per_operation);
190
191         rte_hash_free(handle);
192         return 0;
193 }
194
195 static int
196 test_hash_scaling_main(void)
197 {
198         int r = 0;
199
200         if (rte_lcore_count() == 1)
201                 r = test_hash_scaling(NULL_LOCK);
202
203         if (r == 0)
204                 r = test_hash_scaling(NORMAL_LOCK);
205
206         if (!rte_tm_supported()) {
207                 printf("Hardware transactional memory (lock elision) is NOT supported\n");
208                 return r;
209         }
210         printf("Hardware transactional memory (lock elision) is supported\n");
211
212         if (r == 0)
213                 r = test_hash_scaling(LOCK_ELISION);
214
215         return r;
216 }
217
218
219 static struct test_command hash_scaling_cmd = {
220         .command = "hash_scaling_autotest",
221         .callback = test_hash_scaling_main,
222 };
223 REGISTER_TEST_COMMAND(hash_scaling_cmd);