25b249fe71a10e75c02d6055c4cc38744ec5055d
[dpdk.git] / app / test / test_ring_perf.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2013 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
35
36 #include <stdio.h>
37 #include <inttypes.h>
38 #include <rte_ring.h>
39 #include <rte_cycles.h>
40 #include <rte_launch.h>
41
42 #include <cmdline_parse.h>
43
44 #include "test.h"
45
46 /*
47  * Ring
48  * ====
49  *
50  * Measures performance of various operations using rdtsc
51  *  * Empty ring dequeue
52  *  * Enqueue/dequeue of bursts in 1 threads
53  *  * Enqueue/dequeue of bursts in 2 threads
54  */
55
56 #define RING_NAME "RING_PERF"
57 #define RING_SIZE 4096
58 #define MAX_BURST 32
59
60 /* 
61  * the sizes to enqueue and dequeue in testing
62  * (marked volatile so they won't be seen as compile-time constants)
63  */
64 static const volatile unsigned bulk_sizes[] = { 8, 32 };
65
66 /* The ring structure used for tests */
67 static struct rte_ring *r;
68
69 struct lcore_pair {
70         unsigned c1, c2;
71 };
72
73 static volatile unsigned lcore_count = 0;
74
75 /**** Functions to analyse our core mask to get cores for different tests ***/
76
77 static int
78 get_two_hyperthreads(struct lcore_pair *lcp)
79 {
80         unsigned id1, id2;
81         unsigned c1, c2, s1, s2;
82         RTE_LCORE_FOREACH(id1) {
83                 /* inner loop just re-reads all id's. We could skip the first few
84                  * elements, but since number of cores is small there is little point
85                  */
86                 RTE_LCORE_FOREACH(id2) {
87                         if (id1 == id2)
88                                 continue;
89                         c1 = lcore_config[id1].core_id;
90                         c2 = lcore_config[id2].core_id;
91                         s1 = lcore_config[id1].socket_id;
92                         s2 = lcore_config[id2].socket_id;
93                         if ((c1 == c2) && (s1 == s2)){
94                                 lcp->c1 = id1;
95                                 lcp->c2 = id2;
96                                 return 0;
97                         }
98                 }
99         }
100         return 1;
101 }
102
103 static int
104 get_two_cores(struct lcore_pair *lcp)
105 {
106         unsigned id1, id2;
107         unsigned c1, c2, s1, s2;
108         RTE_LCORE_FOREACH(id1) {
109                 RTE_LCORE_FOREACH(id2) {
110                         if (id1 == id2)
111                                 continue;
112                         c1 = lcore_config[id1].core_id;
113                         c2 = lcore_config[id2].core_id;
114                         s1 = lcore_config[id1].socket_id;
115                         s2 = lcore_config[id2].socket_id;
116                         if ((c1 != c2) && (s1 == s2)){
117                                 lcp->c1 = id1;
118                                 lcp->c2 = id2;
119                                 return 0;
120                         }
121                 }
122         }
123         return 1;
124 }
125
126 static int
127 get_two_sockets(struct lcore_pair *lcp)
128 {
129         unsigned id1, id2;
130         unsigned s1, s2;
131         RTE_LCORE_FOREACH(id1) {
132                 RTE_LCORE_FOREACH(id2) {
133                         if (id1 == id2)
134                                 continue;
135                         s1 = lcore_config[id1].socket_id;
136                         s2 = lcore_config[id2].socket_id;
137                         if (s1 != s2){
138                                 lcp->c1 = id1;
139                                 lcp->c2 = id2;
140                                 return 0;
141                         }
142                 }
143         }
144         return 1;
145 }
146
147 /* Get cycle counts for dequeuing from an empty ring. Should be 2 or 3 cycles */
148 static void
149 test_empty_dequeue(void)
150 {
151         const unsigned iter_shift = 26;
152         const unsigned iterations = 1<<iter_shift;
153         unsigned i = 0;
154         void *burst[MAX_BURST];
155
156         const uint64_t sc_start = rte_rdtsc();
157         for (i = 0; i < iterations; i++)
158                 rte_ring_sc_dequeue_bulk(r, burst, bulk_sizes[0]);
159         const uint64_t sc_end = rte_rdtsc();
160
161         const uint64_t mc_start = rte_rdtsc();
162         for (i = 0; i < iterations; i++)
163                 rte_ring_mc_dequeue_bulk(r, burst, bulk_sizes[0]);
164         const uint64_t mc_end = rte_rdtsc();
165
166         printf("SC empty dequeue: %.2F\n",
167                         (double)(sc_end-sc_start) / iterations);
168         printf("MC empty dequeue: %.2F\n",
169                         (double)(mc_end-mc_start) / iterations);
170 }
171
172 /* 
173  * for the separate enqueue and dequeue threads they take in one param
174  * and return two. Input = burst size, output = cycle average for sp/sc & mp/mc
175  */
176 struct thread_params {
177         unsigned size;        /* input value, the burst size */
178         double spsc, mpmc;    /* output value, the single or multi timings */
179 };
180
181 /* 
182  * Function that uses rdtsc to measure timing for ring enqueue. Needs pair
183  * thread running dequeue_bulk function 
184  */
185 static int
186 enqueue_bulk(void *p)
187 {
188         const unsigned iter_shift = 23;
189         const unsigned iterations = 1<<iter_shift;
190         struct thread_params *params = p;
191         const unsigned size = params->size;
192         unsigned i;
193         void *burst[MAX_BURST] = {0};
194
195         if ( __sync_add_and_fetch(&lcore_count, 1) != 2 )
196                 while(lcore_count != 2)
197                         rte_pause();
198
199         const uint64_t sp_start = rte_rdtsc();
200         for (i = 0; i < iterations; i++)
201                 while (rte_ring_sp_enqueue_bulk(r, burst, size) != 0)
202                         rte_pause();
203         const uint64_t sp_end = rte_rdtsc();
204
205         const uint64_t mp_start = rte_rdtsc();
206         for (i = 0; i < iterations; i++)
207                 while (rte_ring_mp_enqueue_bulk(r, burst, size) != 0)
208                         rte_pause();
209         const uint64_t mp_end = rte_rdtsc();
210
211         params->spsc = ((double)(sp_end - sp_start))/(iterations*size);
212         params->mpmc = ((double)(mp_end - mp_start))/(iterations*size);
213         return 0;
214 }
215
216 /* 
217  * Function that uses rdtsc to measure timing for ring dequeue. Needs pair
218  * thread running enqueue_bulk function 
219  */
220 static int
221 dequeue_bulk(void *p)
222 {
223         const unsigned iter_shift = 23;
224         const unsigned iterations = 1<<iter_shift;
225         struct thread_params *params = p;
226         const unsigned size = params->size;
227         unsigned i;
228         void *burst[MAX_BURST] = {0};
229
230         if ( __sync_add_and_fetch(&lcore_count, 1) != 2 )
231                 while(lcore_count != 2)
232                         rte_pause();
233
234         const uint64_t sc_start = rte_rdtsc();
235         for (i = 0; i < iterations; i++)
236                 while (rte_ring_sc_dequeue_bulk(r, burst, size) != 0)
237                         rte_pause();
238         const uint64_t sc_end = rte_rdtsc();
239
240         const uint64_t mc_start = rte_rdtsc();
241         for (i = 0; i < iterations; i++)
242                 while (rte_ring_mc_dequeue_bulk(r, burst, size) != 0)
243                         rte_pause();
244         const uint64_t mc_end = rte_rdtsc();
245
246         params->spsc = ((double)(sc_end - sc_start))/(iterations*size);
247         params->mpmc = ((double)(mc_end - mc_start))/(iterations*size);
248         return 0;
249 }
250
251 /* 
252  * Function that calls the enqueue and dequeue bulk functions on pairs of cores.
253  * used to measure ring perf between hyperthreads, cores and sockets.
254  */
255 static void
256 run_on_core_pair(struct lcore_pair *cores,
257                 lcore_function_t f1, lcore_function_t f2)
258 {
259         struct thread_params param1 = {.size = 0}, param2 = {.size = 0};
260         unsigned i;
261         for (i = 0; i < sizeof(bulk_sizes)/sizeof(bulk_sizes[0]); i++) {
262                 lcore_count = 0;
263                 param1.size = param2.size = bulk_sizes[i];
264                 if (cores->c1 == rte_get_master_lcore()) {
265                         rte_eal_remote_launch(f2, &param2, cores->c2);
266                         f1(&param1);
267                         rte_eal_wait_lcore(cores->c2);
268                 } else {
269                         rte_eal_remote_launch(f1, &param1, cores->c1);
270                         rte_eal_remote_launch(f2, &param2, cores->c2);
271                         rte_eal_wait_lcore(cores->c1);
272                         rte_eal_wait_lcore(cores->c2);
273                 }
274                 printf("SP/SC bulk enq/dequeue (size: %u): %.2F\n", bulk_sizes[i],
275                                 param1.spsc + param2.spsc);
276                 printf("MP/MC bulk enq/dequeue (size: %u): %.2F\n", bulk_sizes[i],
277                                 param1.mpmc + param2.mpmc);
278         }
279 }
280
281 /* 
282  * Test function that determines how long an enqueue + dequeue of a single item
283  * takes on a single lcore. Result is for comparison with the bulk enq+deq.
284  */
285 static void
286 test_single_enqueue_dequeue(void)
287 {
288         const unsigned iter_shift = 24;
289         const unsigned iterations = 1<<iter_shift;
290         unsigned i = 0;
291         void *burst = NULL;
292
293         const uint64_t sc_start = rte_rdtsc();
294         for (i = 0; i < iterations; i++) {
295                 rte_ring_sp_enqueue(r, burst);
296                 rte_ring_sc_dequeue(r, &burst);
297         }
298         const uint64_t sc_end = rte_rdtsc();
299
300         const uint64_t mc_start = rte_rdtsc();
301         for (i = 0; i < iterations; i++) {
302                 rte_ring_mp_enqueue(r, burst);
303                 rte_ring_mc_dequeue(r, &burst);
304         }
305         const uint64_t mc_end = rte_rdtsc();
306
307         printf("SP/SC single enq/dequeue: %"PRIu64"\n",
308                         (sc_end-sc_start) >> iter_shift);
309         printf("MP/MC single enq/dequeue: %"PRIu64"\n",
310                         (mc_end-mc_start) >> iter_shift);
311 }
312
313 /* 
314  * Test that does both enqueue and dequeue on a core using the burst() API calls
315  * instead of the bulk() calls used in other tests. Results should be the same
316  * as for the bulk function called on a single lcore.
317  */
318 static void
319 test_burst_enqueue_dequeue(void)
320 {
321         const unsigned iter_shift = 23;
322         const unsigned iterations = 1<<iter_shift;
323         unsigned sz, i = 0;
324         void *burst[MAX_BURST] = {0};
325
326         for (sz = 0; sz < sizeof(bulk_sizes)/sizeof(bulk_sizes[0]); sz++) {
327                 const uint64_t sc_start = rte_rdtsc();
328                 for (i = 0; i < iterations; i++) {
329                         rte_ring_sp_enqueue_burst(r, burst, bulk_sizes[sz]);
330                         rte_ring_sc_dequeue_burst(r, burst, bulk_sizes[sz]);
331                 }
332                 const uint64_t sc_end = rte_rdtsc();
333
334                 const uint64_t mc_start = rte_rdtsc();
335                 for (i = 0; i < iterations; i++) {
336                         rte_ring_mp_enqueue_burst(r, burst, bulk_sizes[sz]);
337                         rte_ring_mc_dequeue_burst(r, burst, bulk_sizes[sz]);
338                 }
339                 const uint64_t mc_end = rte_rdtsc();
340
341                 uint64_t mc_avg = ((mc_end-mc_start) >> iter_shift) / bulk_sizes[sz];
342                 uint64_t sc_avg = ((sc_end-sc_start) >> iter_shift) / bulk_sizes[sz];
343
344                 printf("SP/SC burst enq/dequeue (size: %u): %"PRIu64"\n", bulk_sizes[sz],
345                                 sc_avg);
346                 printf("MP/MC burst enq/dequeue (size: %u): %"PRIu64"\n", bulk_sizes[sz],
347                                 mc_avg);
348         }
349 }
350
351 /* Times enqueue and dequeue on a single lcore */
352 static void
353 test_bulk_enqueue_dequeue(void)
354 {
355         const unsigned iter_shift = 23;
356         const unsigned iterations = 1<<iter_shift;
357         unsigned sz, i = 0;
358         void *burst[MAX_BURST] = {0};
359
360         for (sz = 0; sz < sizeof(bulk_sizes)/sizeof(bulk_sizes[0]); sz++) {
361                 const uint64_t sc_start = rte_rdtsc();
362                 for (i = 0; i < iterations; i++) {
363                         rte_ring_sp_enqueue_bulk(r, burst, bulk_sizes[sz]);
364                         rte_ring_sc_dequeue_bulk(r, burst, bulk_sizes[sz]);
365                 }
366                 const uint64_t sc_end = rte_rdtsc();
367
368                 const uint64_t mc_start = rte_rdtsc();
369                 for (i = 0; i < iterations; i++) {
370                         rte_ring_mp_enqueue_bulk(r, burst, bulk_sizes[sz]);
371                         rte_ring_mc_dequeue_bulk(r, burst, bulk_sizes[sz]);
372                 }
373                 const uint64_t mc_end = rte_rdtsc();
374
375                 double sc_avg = ((double)(sc_end-sc_start) /
376                                 (iterations * bulk_sizes[sz]));
377                 double mc_avg = ((double)(mc_end-mc_start) /
378                                 (iterations * bulk_sizes[sz]));
379
380                 printf("SP/SC bulk enq/dequeue (size: %u): %.2F\n", bulk_sizes[sz],
381                                 sc_avg);
382                 printf("MP/MC bulk enq/dequeue (size: %u): %.2F\n", bulk_sizes[sz],
383                                 mc_avg);
384         }
385 }
386
387 int
388 test_ring_perf(void)
389 {
390         struct lcore_pair cores;
391         r = rte_ring_create(RING_NAME, RING_SIZE, rte_socket_id(), 0);
392         if (r == NULL && (r = rte_ring_lookup(RING_NAME)) == NULL)
393                 return -1;
394
395         printf("### Testing single element and burst enq/deq ###\n");
396         test_single_enqueue_dequeue();
397         test_burst_enqueue_dequeue();
398
399         printf("\n### Testing empty dequeue ###\n");
400         test_empty_dequeue();
401
402         printf("\n### Testing using a single lcore ###\n");
403         test_bulk_enqueue_dequeue();
404
405         if (get_two_hyperthreads(&cores) == 0) {
406                 printf("\n### Testing using two hyperthreads ###\n");
407                 run_on_core_pair(&cores, enqueue_bulk, dequeue_bulk);
408         }
409         if (get_two_cores(&cores) == 0) {
410                 printf("\n### Testing using two physical cores ###\n");
411                 run_on_core_pair(&cores, enqueue_bulk, dequeue_bulk);
412         }
413         if (get_two_sockets(&cores) == 0) {
414                 printf("\n### Testing using two NUMA nodes ###\n");
415                 run_on_core_pair(&cores, enqueue_bulk, dequeue_bulk);
416         }
417         return 0;
418 }