API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_queue_perf.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018 Linaro Limited
3 * Copyright (c) 2021-2025 Nokia
4 */
5
14#include <stdio.h>
15#include <string.h>
16#include <stdint.h>
17#include <inttypes.h>
18#include <stdlib.h>
19#include <getopt.h>
20
21#include <odp_api.h>
22#include <odp/helper/odph_api.h>
23
24#include <export_results.h>
25
26#define MAX_QUEUES (32 * 1024)
27
28typedef enum {
29 TEST_MODE_LOOP = 0,
30 TEST_MODE_PAIR,
31} test_mode_t;
32
33typedef struct test_options_t {
34 uint32_t num_queue;
35 uint32_t num_event;
36 uint32_t num_round;
37 uint32_t max_burst;
38 uint32_t num_cpu;
39 odp_nonblocking_t nonblock;
40 test_mode_t mode;
41 odp_bool_t private_queues;
42 odp_bool_t single;
43 odp_bool_t extra_features_enabled;
44 uint64_t wait_ns;
45 uint8_t *memcpy_src;
46 uint64_t memcpy_bytes;
47
48} test_options_t;
49
50typedef struct test_stat_t {
51 uint64_t rounds;
52 uint64_t events;
53 uint64_t nsec;
54 uint64_t cycles;
55 uint64_t deq_retry;
56 uint64_t enq_retry;
57
58} test_stat_t;
59
60typedef struct test_global_t test_global_t;
61
62typedef struct {
63 test_global_t *global;
64 odp_barrier_t *barrier;
65 test_options_t *options;
66 test_stat_t stats;
67 uint32_t src_queue_id[MAX_QUEUES];
68 uint32_t dst_queue_id[MAX_QUEUES];
69 odp_queue_t src_queue_tbl[MAX_QUEUES];
70 odp_queue_t dst_queue_tbl[MAX_QUEUES];
71 uint32_t num_queues;
72 int thr_idx;
73} thread_args_t;
74
75typedef struct test_global_t {
76 odp_barrier_t barrier;
77 test_options_t options;
78 odp_instance_t instance;
79 odp_shm_t shm;
80 odp_pool_t pool;
81 odp_atomic_u32_t workers_finished;
82 odp_queue_t queue[MAX_QUEUES];
83 odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
84 thread_args_t thread_args[ODP_THREAD_COUNT_MAX];
85 odp_shm_t memcpy_shm;
86 uint8_t *memcpy_data;
87 test_common_options_t common_options;
88
89} test_global_t;
90
91static void print_usage(void)
92{
93 printf("\n"
94 "Plain queue performance test\n"
95 "\n"
96 "Usage: odp_queue_perf [options]\n"
97 "\n"
98 " -m, --mode <arg> Test mode:\n"
99 " 0: Loop: events are enqueued back to the same queue they\n"
100 " were dequeued from (default)\n"
101 " 1: Pair: queues are paired and events are always moved\n"
102 " between the queues when doing dequeue/enqueue. Requires\n"
103 " an even number of both queues and workers.\n"
104 " -c, --num_cpu Number of worker threads (default 1)\n"
105 " -q, --num_queue Number of queues (default 1)\n"
106 " -e, --num_event Number of events per queue (default 1)\n"
107 " -b, --burst_size Maximum number of events per operation (default 1). When 0,\n"
108 " single event enqueue/dequeue functions are used instead\n"
109 " of multi event variants.\n"
110 " -p, --private Use separate queues for each worker\n"
111 " -r, --num_round Number of rounds\n"
112 " -l, --lockfree Lock-free queues\n"
113 " -w, --waitfree Wait-free queues\n"
114 " -s, --single Single producer/consumer queues\n"
115 " -M, --memcpy <num> Number of bytes to memcpy per dequeue burst before\n"
116 " enqueueing events. Default: 0.\n"
117 " -W, --wait_ns <ns> Number of nsecs to wait per dequeue burst before.\n"
118 " enqueueing events. Default: 0.\n"
119 " -h, --help This help\n"
120 "\n");
121}
122
123static int parse_options(int argc, char *argv[], test_options_t *test_options)
124{
125 int opt, num_cpu;
126 int ret = 0;
127
128 static const struct option longopts[] = {
129 {"num_cpu", required_argument, NULL, 'c'},
130 {"num_queue", required_argument, NULL, 'q'},
131 {"num_event", required_argument, NULL, 'e'},
132 {"burst_size", required_argument, NULL, 'b'},
133 {"mode", required_argument, NULL, 'm'},
134 {"private", no_argument, NULL, 'p'},
135 {"num_round", required_argument, NULL, 'r'},
136 {"lockfree", no_argument, NULL, 'l'},
137 {"waitfree", no_argument, NULL, 'w'},
138 {"single", no_argument, NULL, 's'},
139 {"wait_ns", required_argument, NULL, 'W'},
140 {"memcpy", required_argument, NULL, 'M'},
141 {"help", no_argument, NULL, 'h'},
142 {NULL, 0, NULL, 0}
143 };
144
145 static const char *shortopts = "+c:q:e:b:m:M:pr:lwW:sh";
146
147 test_options->num_cpu = 1;
148 test_options->num_queue = 1;
149 test_options->num_event = 1;
150 test_options->max_burst = 1;
151 test_options->mode = TEST_MODE_LOOP;
152 test_options->num_round = 1000;
153 test_options->nonblock = ODP_BLOCKING;
154 test_options->single = false;
155 test_options->private_queues = false;
156
157 while (1) {
158 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
159
160 if (opt == -1)
161 break;
162
163 switch (opt) {
164 case 'c':
165 test_options->num_cpu = atoi(optarg);
166 break;
167 case 'q':
168 test_options->num_queue = atoi(optarg);
169 break;
170 case 'e':
171 test_options->num_event = atoi(optarg);
172 break;
173 case 'b':
174 test_options->max_burst = atoi(optarg);
175 break;
176 case 'm':
177 if (atoi(optarg) == TEST_MODE_PAIR)
178 test_options->mode = TEST_MODE_PAIR;
179 break;
180 case 'r':
181 test_options->num_round = atoi(optarg);
182 break;
183 case 'l':
184 test_options->nonblock = ODP_NONBLOCKING_LF;
185 break;
186 case 'w':
187 test_options->nonblock = ODP_NONBLOCKING_WF;
188 break;
189 case 'p':
190 test_options->private_queues = true;
191 break;
192 case 's':
193 test_options->single = true;
194 break;
195 case 'W':
196 test_options->wait_ns = atoll(optarg);
197 break;
198 case 'M':
199 test_options->memcpy_bytes = atoll(optarg);
200 break;
201 case 'h':
202 /* fall through */
203 default:
204 print_usage();
205 ret = -1;
206 break;
207 }
208 }
209
210 if (test_options->num_queue > MAX_QUEUES || test_options->num_queue == 0) {
211 ODPH_ERR("Invalid number of queues %u. Test maximum %u.\n",
212 test_options->num_queue, MAX_QUEUES);
213 return -1;
214 }
215
216 num_cpu = test_options->num_cpu;
217 if (num_cpu == 0)
218 num_cpu = odp_cpumask_default_worker(NULL, 0);
219
220 if (test_options->private_queues) {
221 if ((int)test_options->num_queue < num_cpu) {
222 ODPH_ERR("Not enough queues for %d workers.\n", num_cpu);
223 return -1;
224 }
225 if (test_options->num_queue % num_cpu)
226 ODPH_ERR("Warn: %" PRIu32 " queues shared unevenly amongst %" PRIu32 " "
227 "workers.\n", test_options->num_queue, num_cpu);
228 }
229
230 if (test_options->single && !test_options->private_queues) {
231 if ((test_options->mode == TEST_MODE_LOOP && num_cpu != 1) ||
232 (test_options->mode == TEST_MODE_PAIR && num_cpu != 2)) {
233 ODPH_ERR("Multiple producers/consumers not allowed with single prod/cons queues.\n");
234 return -1;
235 }
236 }
237
238 if (test_options->mode == TEST_MODE_PAIR && (test_options->num_queue % 2 || num_cpu % 2)) {
239 ODPH_ERR("Pair mode requires an even number of queues and workers.\n");
240 return -1;
241 }
242
243 if (test_options->wait_ns || test_options->memcpy_bytes)
244 test_options->extra_features_enabled = true;
245
246 return ret;
247}
248
249static int create_queues(test_global_t *global)
250{
251 odp_pool_capability_t pool_capa;
252 odp_queue_capability_t queue_capa;
253 odp_pool_param_t pool_param;
254 odp_queue_param_t queue_param;
255 odp_pool_t pool;
256 uint32_t i, j, max_size, max_num;
257 test_options_t *test_options = &global->options;
258 odp_nonblocking_t nonblock = test_options->nonblock;
259 uint32_t num_queue = test_options->num_queue;
260 uint32_t num_event = test_options->num_event;
261 uint32_t num_round = test_options->num_round;
262 uint32_t tot_event = num_queue * num_event;
263 uint32_t queue_size = test_options->mode == TEST_MODE_PAIR ? 2 * num_event : num_event;
264 int ret = 0;
265 odp_queue_t *queue = global->queue;
266 odp_event_t event[tot_event];
267
268 printf("\nTesting %s queues\n",
269 nonblock == ODP_BLOCKING ? "NORMAL" :
270 (nonblock == ODP_NONBLOCKING_LF ? "LOCKFREE" :
271 (nonblock == ODP_NONBLOCKING_WF ? "WAITFREE" : "???")));
272 printf(" mode %s\n", test_options->mode == TEST_MODE_LOOP ?
273 "loop" : "pair");
274 printf(" private queues %s\n", test_options->private_queues ? "yes" : "no");
275 printf(" single prod/cons %s\n", test_options->single ? "yes" : "no");
276 printf(" num rounds %u\n", num_round);
277 printf(" num queues %u\n", num_queue);
278 printf(" num events per queue %u\n", num_event);
279 printf(" queue size %u\n", queue_size);
280 printf(" max burst size %u\n", test_options->max_burst);
281 printf(" wait %" PRIu64 " ns\n", test_options->wait_ns);
282 printf(" memcpy %" PRIu64 " bytes\n", test_options->memcpy_bytes);
283
284 for (i = 0; i < num_queue; i++)
285 queue[i] = ODP_QUEUE_INVALID;
286
287 for (i = 0; i < tot_event; i++)
288 event[i] = ODP_EVENT_INVALID;
289
290 if (odp_queue_capability(&queue_capa)) {
291 ODPH_ERR("Queue capa failed.\n");
292 return -1;
293 }
294
295 if (odp_pool_capability(&pool_capa)) {
296 ODPH_ERR("Pool capa failed.\n");
297 return -1;
298 }
299
300 if (nonblock == ODP_BLOCKING) {
301 if (num_queue > queue_capa.plain.max_num) {
302 ODPH_ERR("Max queues supported %u.\n", queue_capa.plain.max_num);
303 return -1;
304 }
305
306 max_size = queue_capa.plain.max_size;
307 if (max_size && queue_size > max_size) {
308 ODPH_ERR("Max queue size supported %u.\n", max_size);
309 return -1;
310 }
311 } else if (nonblock == ODP_NONBLOCKING_LF) {
312 if (queue_capa.plain.lockfree.max_num == 0) {
313 ODPH_ERR("Lockfree queues not supported.\n");
314 return -1;
315 }
316
317 if (num_queue > queue_capa.plain.lockfree.max_num) {
318 ODPH_ERR("Max lockfree queues supported %u.\n",
319 queue_capa.plain.lockfree.max_num);
320 return -1;
321 }
322
323 max_size = queue_capa.plain.lockfree.max_size;
324 if (max_size && queue_size > max_size) {
325 ODPH_ERR("Max lockfree queue size supported %u.\n", max_size);
326 return -1;
327 }
328 } else if (nonblock == ODP_NONBLOCKING_WF) {
329 if (queue_capa.plain.waitfree.max_num == 0) {
330 ODPH_ERR("Waitfree queues not supported.\n");
331 return -1;
332 }
333
334 if (num_queue > queue_capa.plain.waitfree.max_num) {
335 ODPH_ERR("Max waitfree queues supported %u.\n",
336 queue_capa.plain.waitfree.max_num);
337 return -1;
338 }
339
340 max_size = queue_capa.plain.waitfree.max_size;
341 if (max_size && queue_size > max_size) {
342 ODPH_ERR("Max waitfree queue size supported %u.\n", max_size);
343 return -1;
344 }
345 } else {
346 ODPH_ERR("Bad queue blocking type.\n");
347 return -1;
348 }
349
350 max_num = pool_capa.buf.max_num;
351
352 if (max_num && tot_event > max_num) {
353 ODPH_ERR("Max events supported %u.\n", max_num);
354 return -1;
355 }
356
357 odp_pool_param_init(&pool_param);
358 pool_param.type = ODP_POOL_BUFFER;
359 pool_param.buf.num = tot_event;
360
361 pool = odp_pool_create("queue perf pool", &pool_param);
362
363 if (pool == ODP_POOL_INVALID) {
364 ODPH_ERR("Pool create failed.\n");
365 return -1;
366 }
367
368 global->pool = pool;
369
370 odp_queue_param_init(&queue_param);
371 queue_param.type = ODP_QUEUE_TYPE_PLAIN;
372 queue_param.nonblocking = nonblock;
373 queue_param.size = queue_size;
374
375 if (test_options->single) {
376 queue_param.enq_mode = ODP_QUEUE_OP_MT_UNSAFE;
377 queue_param.deq_mode = ODP_QUEUE_OP_MT_UNSAFE;
378 }
379
380 for (i = 0; i < num_queue; i++) {
381 queue[i] = odp_queue_create(NULL, &queue_param);
382
383 if (queue[i] == ODP_QUEUE_INVALID) {
384 ODPH_ERR("Queue create failed %u.\n", i);
385 return -1;
386 }
387 }
388
389 for (i = 0; i < tot_event; i++) {
390 event[i] = odp_buffer_to_event(odp_buffer_alloc(pool));
391
392 if (event[i] == ODP_EVENT_INVALID) {
393 ODPH_ERR("Event alloc failed %u.\n", i);
394 ret = -1;
395 goto free_events;
396 }
397 }
398
399 for (i = 0; i < num_queue; i++) {
400 for (j = 0; j < num_event; j++) {
401 uint32_t id = i * num_event + j;
402
403 if (odp_queue_enq(queue[i], event[id])) {
404 ODPH_ERR("Queue enq failed %u/%u.\n", i, j);
405 ret = -1;
406 goto free_events;
407 }
408
409 event[id] = ODP_EVENT_INVALID;
410 }
411 }
412
413free_events:
414 /* Free events that were not stored into queues */
415 for (i = 0; i < tot_event; i++) {
416 if (event[i] != ODP_EVENT_INVALID)
417 odp_event_free(event[i]);
418 }
419
420 if (ret)
421 ODPH_ERR("Initializing test queues failed.\n");
422
423 return ret;
424}
425
426static int destroy_queues(test_global_t *global)
427{
428 uint32_t i;
429 int ret = 0;
430 test_options_t *test_options = &global->options;
431 uint32_t num_queue = test_options->num_queue;
432 odp_queue_t *queue = global->queue;
433 odp_pool_t pool = global->pool;
434
435 for (i = 0; i < num_queue; i++) {
436 if (queue[i] == ODP_QUEUE_INVALID)
437 break;
438
439 while (1) {
440 odp_event_t ev = odp_queue_deq(queue[i]);
441
442 if (ev == ODP_EVENT_INVALID)
443 break;
444 odp_event_free(ev);
445 }
446
447 if (odp_queue_destroy(queue[i])) {
448 ODPH_ERR("Queue destroy failed %u.\n", i);
449 ret = -1;
450 break;
451 }
452 }
453
454 if (pool != ODP_POOL_INVALID && odp_pool_destroy(pool)) {
455 ODPH_ERR("Pool destroy failed.\n");
456 ret = -1;
457 }
458
459 return ret;
460}
461
462static inline uint32_t next_queues(odp_queue_t *src_queue, odp_queue_t *dst_queue,
463 odp_queue_t src_queue_tbl[], odp_queue_t dst_queue_tbl[],
464 uint32_t num_queue, uint32_t queue_idx)
465{
466 *src_queue = src_queue_tbl[queue_idx];
467 *dst_queue = dst_queue_tbl[queue_idx];
468
469 queue_idx++;
470 if (queue_idx == num_queue)
471 queue_idx = 0;
472
473 return queue_idx;
474}
475
476static int reserve_memcpy_memory(test_global_t *global)
477{
478 uint64_t total_bytes;
479 uint8_t *src;
480 uint8_t *dst;
481 const test_options_t *options = &global->options;
482
483 global->memcpy_shm = ODP_SHM_INVALID;
484 if (options->memcpy_bytes == 0)
485 return 0;
486
487 total_bytes = 2 * options->memcpy_bytes * options->num_cpu;
488
489 global->memcpy_shm = odp_shm_reserve("memcpy_shm", total_bytes, ODP_CACHE_LINE_SIZE, 0);
490 if (global->memcpy_shm == ODP_SHM_INVALID) {
491 ODPH_ERR("Reserving %" PRIu64 " bytes for memcpy failed.\n", total_bytes);
492 return -1;
493 }
494
495 global->memcpy_data = odp_shm_addr(global->memcpy_shm);
496 if (global->memcpy_data == NULL) {
497 ODPH_ERR("Shared mem addr for memcpy failed.\n");
498 return -1;
499 }
500
501 for (uint32_t t = 0; t < options->num_cpu; t++) {
502 src = &global->memcpy_data[t * 2 * options->memcpy_bytes];
503 dst = src + options->memcpy_bytes;
504 memset(src, 0xA5, options->memcpy_bytes);
505 memset(dst, 0x00, options->memcpy_bytes);
506 }
507
508 return 0;
509}
510
511static inline void process_features(const test_options_t *test_options)
512{
513 if (test_options->extra_features_enabled) {
514 if (test_options->wait_ns)
515 odp_time_wait_ns(test_options->wait_ns);
516
517 if (test_options->memcpy_bytes) {
518 const uint64_t bytes = test_options->memcpy_bytes;
519 uint8_t *memcpy_src = test_options->memcpy_src;
520 uint8_t *memcpy_dst = memcpy_src + bytes;
521
522 memcpy(memcpy_dst, memcpy_src, bytes);
523 }
524 }
525}
526
527static inline void run_single_event(odp_queue_t src_queue_tbl[], odp_queue_t dst_queue_tbl[],
528 uint32_t num_queue, uint32_t num_round, test_stat_t *stat,
529 const test_options_t *test_options)
530{
531 odp_queue_t src_queue, dst_queue;
532 odp_event_t ev;
533 uint32_t queue_idx = 0;
534
535 for (uint32_t i = 0; i < num_round; i++) {
536 while (1) {
537 queue_idx = next_queues(&src_queue, &dst_queue, src_queue_tbl,
538 dst_queue_tbl, num_queue, queue_idx);
539 ev = odp_queue_deq(src_queue);
540 if (odp_unlikely(ev == ODP_EVENT_INVALID)) {
541 stat->deq_retry++;
542 continue;
543 }
544 break;
545 };
546
547 process_features(test_options);
548
549 while (1) {
550 int ret = odp_queue_enq(dst_queue, ev);
551
552 if (odp_unlikely(ret != 0)) {
553 stat->enq_retry++;
554 continue;
555 }
556 break;
557 };
558 stat->events++;
559 }
560 stat->rounds = num_round;
561}
562
563static inline void run_single_event_cleanup(odp_queue_t src_queue_tbl[],
564 odp_queue_t dst_queue_tbl[],
565 uint32_t num_queue, uint32_t num_workers,
566 odp_atomic_u32_t *workers_finished)
567{
568 odp_queue_t src_queue, dst_queue;
569 odp_event_t ev;
570 uint32_t queue_idx = 0;
571
572 while (odp_atomic_load_u32(workers_finished) < num_workers) {
573 queue_idx = next_queues(&src_queue, &dst_queue, src_queue_tbl, dst_queue_tbl,
574 num_queue, queue_idx);
575 ev = odp_queue_deq(src_queue);
577 continue;
578
579 while (1) {
580 int ret = odp_queue_enq(dst_queue, ev);
581
582 if (odp_unlikely(ret != 0))
583 continue;
584 break;
585 };
586 }
587}
588
589static inline void run_multi_event(odp_queue_t src_queue_tbl[], odp_queue_t dst_queue_tbl[],
590 uint32_t num_queue, uint32_t num_round, uint32_t max_burst,
591 test_stat_t *stat, const test_options_t *test_options)
592{
593 odp_queue_t src_queue, dst_queue;
594 odp_event_t ev[max_burst];
595 int num_ev, num_enq;
596 uint32_t queue_idx = 0;
597
598 for (uint32_t i = 0; i < num_round; i++) {
599 num_enq = 0;
600
601 do {
602 queue_idx = next_queues(&src_queue, &dst_queue, src_queue_tbl,
603 dst_queue_tbl, num_queue, queue_idx);
604 num_ev = odp_queue_deq_multi(src_queue, ev, max_burst);
605 if (odp_unlikely(num_ev < 0))
606 ODPH_ABORT("odp_queue_deq_multi() failed\n");
607
608 if (odp_unlikely(num_ev == 0))
609 stat->deq_retry++;
610
611 } while (num_ev == 0);
612
613 process_features(test_options);
614
615 while (num_enq < num_ev) {
616 int num = odp_queue_enq_multi(dst_queue, &ev[num_enq], num_ev - num_enq);
617
618 if (odp_unlikely(num < 0))
619 ODPH_ABORT("odp_queue_enq_multi() failed\n");
620
621 num_enq += num;
622
623 if (odp_unlikely(num_enq != num_ev))
624 stat->enq_retry++;
625 }
626 stat->events += num_ev;
627 }
628 stat->rounds = num_round;
629}
630
631static inline void run_multi_event_cleanup(odp_queue_t src_queue_tbl[], odp_queue_t dst_queue_tbl[],
632 uint32_t num_queue, uint32_t max_burst,
633 uint32_t num_workers, odp_atomic_u32_t *workers_finished)
634{
635 odp_queue_t src_queue, dst_queue;
636 odp_event_t ev[max_burst];
637 int num_ev, num_enq;
638 uint32_t queue_idx = 0;
639
640 while (odp_atomic_load_u32(workers_finished) < num_workers) {
641 num_enq = 0;
642 queue_idx = next_queues(&src_queue, &dst_queue, src_queue_tbl, dst_queue_tbl,
643 num_queue, queue_idx);
644 num_ev = odp_queue_deq_multi(src_queue, ev, max_burst);
645
646 while (num_enq < num_ev) {
647 int num = odp_queue_enq_multi(dst_queue, &ev[num_enq], num_ev - num_enq);
648
649 if (odp_unlikely(num < 0))
650 ODPH_ABORT("odp_queue_enq_multi() failed\n");
651
652 num_enq += num;
653 }
654 }
655}
656
657static int run_test(void *arg)
658{
659 uint64_t c1, c2, cycles, nsec;
660 odp_time_t t1, t2;
661 thread_args_t *thr_args = arg;
662 test_global_t *global = thr_args->global;
663 test_options_t test_options = global->options;
664 test_stat_t *stat = &thr_args->stats;
665 test_stat_t local_stat = {0};
666 const uint32_t num_queue = thr_args->num_queues;
667 const uint32_t num_round = thr_args->options->num_round;
668 const uint32_t num_workers = thr_args->options->num_cpu;
669 const uint32_t max_burst = thr_args->options->max_burst;
670 const odp_bool_t single_event = max_burst == 0 ? 1 : 0;
671 odp_queue_t *src_queue_tbl = thr_args->src_queue_tbl;
672 odp_queue_t *dst_queue_tbl = thr_args->dst_queue_tbl;
673
674 /* Unique memcpy_src for each thread */
675 if (test_options.memcpy_bytes)
676 test_options.memcpy_src = &global->memcpy_data
677 [thr_args->thr_idx * 2 * test_options.memcpy_bytes];
678
679 for (uint32_t i = 0; i < num_queue; i++) {
680 src_queue_tbl[i] = global->queue[thr_args->src_queue_id[i]];
681 dst_queue_tbl[i] = global->queue[thr_args->dst_queue_id[i]];
682 }
683
684 /* Start all workers at the same time */
685 odp_barrier_wait(thr_args->barrier);
686
689
690 if (single_event)
691 run_single_event(src_queue_tbl, dst_queue_tbl, num_queue, num_round, &local_stat,
692 &test_options);
693 else
694 run_multi_event(src_queue_tbl, dst_queue_tbl, num_queue, num_round, max_burst,
695 &local_stat, &test_options);
696
699
700 odp_atomic_inc_u32(&global->workers_finished);
701
702 /* Keep forwarding events in pair mode until all workers have completed */
703 if (thr_args->options->mode == TEST_MODE_PAIR) {
704 if (single_event)
705 run_single_event_cleanup(src_queue_tbl, dst_queue_tbl, num_queue,
706 num_workers, &global->workers_finished);
707 else
708 run_multi_event_cleanup(src_queue_tbl, dst_queue_tbl, num_queue, max_burst,
709 num_workers, &global->workers_finished);
710 }
711
712 nsec = odp_time_diff_ns(t2, t1);
713 cycles = odp_cpu_cycles_diff(c2, c1);
714
715 stat->rounds = local_stat.rounds;
716 stat->events = local_stat.events;
717 stat->nsec = nsec;
718 stat->cycles = cycles;
719 stat->deq_retry = local_stat.deq_retry;
720 stat->enq_retry = local_stat.enq_retry;
721
722 return 0;
723}
724
725static void map_queues_to_threads(test_global_t *global)
726{
727 test_options_t *opt = &global->options;
728
729 if (opt->mode == TEST_MODE_LOOP) {
730 if (!opt->private_queues) {
731 for (uint32_t i = 0; i < opt->num_queue; i++) {
732 for (uint32_t j = 0; j < opt->num_cpu; j++) {
733 thread_args_t *thread_args = &global->thread_args[j];
734
735 thread_args->src_queue_id[i] = i;
736 thread_args->dst_queue_id[i] = i;
737 thread_args->num_queues++;
738 }
739 }
740 return;
741 }
742
743 for (uint32_t i = 0; i < opt->num_queue; i++) {
744 thread_args_t *thread_args = &global->thread_args[i % opt->num_cpu];
745 uint32_t queue_idx = thread_args->num_queues;
746
747 thread_args->src_queue_id[queue_idx] = i;
748 thread_args->dst_queue_id[queue_idx] = i;
749 thread_args->num_queues++;
750 }
751 return;
752 }
753 /* Pair mode. Always an even number of both queues and CPUs. */
754 if (!opt->private_queues) {
755 for (uint32_t i = 0; i < opt->num_queue; i += 2) {
756 for (uint32_t j = 0; j < opt->num_cpu; j++) {
757 thread_args_t *thread_args = &global->thread_args[j];
758 uint32_t num_queues = thread_args->num_queues;
759
760 if (j % 2 == 0) {
761 thread_args->src_queue_id[num_queues] = i;
762 thread_args->dst_queue_id[num_queues] = i + 1;
763 } else {
764 thread_args->src_queue_id[num_queues] = i + 1;
765 thread_args->dst_queue_id[num_queues] = i;
766 }
767 thread_args->num_queues++;
768 }
769 }
770 return;
771 }
772
773 for (uint32_t i = 0; i < opt->num_queue; i += 2) {
774 uint32_t num_queues;
775 uint32_t thread_a_idx = i % opt->num_cpu;
776 thread_args_t *thread_a_args = &global->thread_args[thread_a_idx];
777 thread_args_t *thread_b_args = &global->thread_args[thread_a_idx + 1];
778
779 num_queues = thread_a_args->num_queues;
780 thread_a_args->src_queue_id[num_queues] = i;
781 thread_a_args->dst_queue_id[num_queues] = i + 1;
782 thread_a_args->num_queues++;
783
784 num_queues = thread_b_args->num_queues;
785 thread_b_args->src_queue_id[num_queues] = i + 1;
786 thread_b_args->dst_queue_id[num_queues] = i;
787 thread_b_args->num_queues++;
788 }
789}
790
791static void print_queue_mappings(test_global_t *global)
792{
793 printf("Worker-queue mappings\n");
794 printf("---------------------\n");
795
796 for (uint32_t i = 0; i < global->options.num_cpu; i++) {
797 thread_args_t *thread_args = &global->thread_args[i];
798 uint32_t num_queues = thread_args->num_queues;
799
800 printf("Worker %u:\n", i);
801
802 printf(" src queue idx:");
803 for (uint32_t j = 0; j < num_queues; j++)
804 printf(" %" PRIu32 "", thread_args->src_queue_id[j]);
805 printf("\n dst queue idx:");
806 for (uint32_t j = 0; j < num_queues; j++)
807 printf(" %" PRIu32 "", thread_args->dst_queue_id[j]);
808 printf("\n\n");
809 }
810}
811
812static void init_thread_args(test_global_t *global)
813{
814 for (uint32_t i = 0; i < global->options.num_cpu; i++) {
815 thread_args_t *thread_args = &global->thread_args[i];
816
817 thread_args->global = global;
818 thread_args->barrier = &global->barrier;
819 thread_args->options = &global->options;
820 thread_args->thr_idx = i;
821 }
822
823 map_queues_to_threads(global);
824
825 print_queue_mappings(global);
826}
827
828static int start_workers(test_global_t *global)
829{
830 odph_thread_common_param_t thr_common;
831 odph_thread_param_t thr_param[ODP_THREAD_COUNT_MAX];
832 odp_cpumask_t cpumask;
833 int ret;
834 test_options_t *test_options = &global->options;
835 int num_cpu = test_options->num_cpu;
836
837 ret = odp_cpumask_default_worker(&cpumask, num_cpu);
838
839 if (num_cpu && ret != num_cpu) {
840 ODPH_ERR("Too many workers. Max supported %i\n.", ret);
841 return -1;
842 }
843
844 /* Zero: all available workers */
845 if (num_cpu == 0) {
846 num_cpu = ret;
847 test_options->num_cpu = num_cpu;
848 }
849
850 printf(" num workers %u\n\n", num_cpu);
851
852 odp_barrier_init(&global->barrier, num_cpu);
853
854 odph_thread_common_param_init(&thr_common);
855 thr_common.instance = global->instance;
856 thr_common.cpumask = &cpumask;
857
858 init_thread_args(global);
859
860 for (int i = 0; i < num_cpu; i++) {
861 odph_thread_param_init(&thr_param[i]);
862 thr_param[i].start = run_test;
863 thr_param[i].arg = &global->thread_args[i];
864 thr_param[i].thr_type = ODP_THREAD_WORKER;
865 }
866
867 if (odph_thread_create(global->thread_tbl, &thr_common, thr_param,
868 num_cpu) != num_cpu)
869 return -1;
870
871 return 0;
872}
873
874static int output_results(test_global_t *global)
875{
876 int i, num;
877 double rounds_ave, events_ave, nsec_ave, cycles_ave;
878 test_stat_t *stats;
879 test_options_t *test_options = &global->options;
880 int num_cpu = test_options->num_cpu;
881 uint64_t rounds_sum = 0;
882 uint64_t events_sum = 0;
883 uint64_t nsec_sum = 0;
884 uint64_t cycles_sum = 0;
885 uint64_t deq_retry_sum = 0;
886 uint64_t enq_retry_sum = 0;
887
888 /* Averages */
889 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
890 stats = &global->thread_args[i].stats;
891 rounds_sum += stats->rounds;
892 events_sum += stats->events;
893 nsec_sum += stats->nsec;
894 cycles_sum += stats->cycles;
895 deq_retry_sum += stats->deq_retry;
896 enq_retry_sum += stats->enq_retry;
897 }
898
899 if (rounds_sum == 0) {
900 printf("No results.\n");
901 return 0;
902 }
903
904 rounds_ave = rounds_sum / num_cpu;
905 events_ave = events_sum / num_cpu;
906 nsec_ave = nsec_sum / num_cpu;
907 cycles_ave = cycles_sum / num_cpu;
908 num = 0;
909
910 printf("RESULTS - per thread (Million events per sec):\n");
911 printf("----------------------------------------------\n");
912 printf(" 1 2 3 4 5 6 7 8 9 10");
913
914 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
915 stats = &global->thread_args[i].stats;
916 if (stats->rounds) {
917 if ((num % 10) == 0)
918 printf("\n ");
919
920 printf("%6.1f ", (1000.0 * stats->events) / stats->nsec);
921 num++;
922 }
923 }
924 printf("\n\n");
925
926 printf("RESULTS - per thread average (%i threads):\n", num_cpu);
927 printf("------------------------------------------\n");
928 printf(" duration: %.3f msec\n", nsec_ave / 1000000);
929 printf(" num cycles: %.3f M\n", cycles_ave / 1000000);
930 printf(" events per dequeue: %.3f\n",
931 events_ave / rounds_ave);
932 printf(" cycles per event: %.3f\n",
933 cycles_ave / events_ave);
934 printf(" dequeue retries: %" PRIu64 "\n", deq_retry_sum);
935 printf(" enqueue retries: %" PRIu64 "\n", enq_retry_sum);
936 printf(" events per sec: %.3f M\n\n",
937 (1000.0 * events_ave) / nsec_ave);
938
939 printf("TOTAL events per sec: %.3f M\n\n",
940 (1000.0 * events_sum) / nsec_ave);
941
942 if (global->common_options.is_export) {
943 if (test_common_write("cycles per event,events per sec (M),total events per sec (M),"
944 "dequeue retries,enqueue retries\n")) {
945 test_common_write_term();
946 return -1;
947 }
948 if (test_common_write("%f,%f,%f,%" PRIu64 ",%" PRIu64 "\n",
949 cycles_ave / events_ave,
950 (1000.0 * events_ave) / nsec_ave,
951 (1000.0 * events_sum) / nsec_ave,
952 deq_retry_sum,
953 enq_retry_sum)) {
954 test_common_write_term();
955 return -1;
956 }
957 test_common_write_term();
958 }
959
960 return 0;
961}
962
963int main(int argc, char **argv)
964{
965 odph_helper_options_t helper_options;
966 odp_instance_t instance;
967 odp_init_t init;
968 odp_shm_t shm;
969 test_global_t *global;
970 test_common_options_t common_options;
971
972 /* Let helper collect its own arguments (e.g. --odph_proc) */
973 argc = odph_parse_options(argc, argv);
974 if (odph_options(&helper_options)) {
975 ODPH_ERR("Reading ODP helper options failed.\n");
976 exit(EXIT_FAILURE);
977 }
978
979 argc = test_common_parse_options(argc, argv);
980 if (test_common_options(&common_options)) {
981 ODPH_ERR("Reading test options failed.\n");
982 exit(EXIT_FAILURE);
983 }
984
985 /* List features not to be used */
986 odp_init_param_init(&init);
987 init.not_used.feat.cls = 1;
988 init.not_used.feat.compress = 1;
989 init.not_used.feat.crypto = 1;
990 init.not_used.feat.ipsec = 1;
991 init.not_used.feat.schedule = 1;
992 init.not_used.feat.timer = 1;
993 init.not_used.feat.tm = 1;
994
995 init.mem_model = helper_options.mem_model;
996
997 /* Init ODP before calling anything else */
998 if (odp_init_global(&instance, &init, NULL)) {
999 ODPH_ERR("Global init failed.\n");
1000 return -1;
1001 }
1002
1003 /* Init this thread */
1004 if (odp_init_local(instance, ODP_THREAD_WORKER)) {
1005 ODPH_ERR("Local init failed.\n");
1006 return -1;
1007 }
1008
1009 shm = odp_shm_reserve("queue_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
1010 if (shm == ODP_SHM_INVALID) {
1011 ODPH_ERR("Shared memory reserve failed.\n");
1012 exit(EXIT_FAILURE);
1013 }
1014
1015 global = odp_shm_addr(shm);
1016 if (global == NULL) {
1017 ODPH_ERR("Shared memory address read failed.\n");
1018 exit(EXIT_FAILURE);
1019 }
1020
1021 memset(global, 0, sizeof(test_global_t));
1022 global->common_options = common_options;
1023 odp_atomic_init_u32(&global->workers_finished, 0);
1024
1025 if (parse_options(argc, argv, &global->options))
1026 return -1;
1027
1029
1030 global->instance = instance;
1031
1032 if (create_queues(global))
1033 goto destroy;
1034
1035 if (reserve_memcpy_memory(global))
1036 exit(EXIT_FAILURE);
1037
1038 if (start_workers(global)) {
1039 ODPH_ERR("Test start failed.\n");
1040 return -1;
1041 }
1042
1043 /* Wait workers to exit */
1044 odph_thread_join(global->thread_tbl, global->options.num_cpu);
1045
1046 if (output_results(global)) {
1047 ODPH_ERR("Outputting results failed.\n");
1048 exit(EXIT_FAILURE);
1049 }
1050
1051destroy:
1052 if (destroy_queues(global)) {
1053 ODPH_ERR("Destroy queues failed.\n");
1054 return -1;
1055 }
1056
1057 if (global->memcpy_shm != ODP_SHM_INVALID && odp_shm_free(global->memcpy_shm)) {
1058 ODPH_ERR("Shared memory free (memcpy) failed.\n");
1059 exit(EXIT_FAILURE);
1060 }
1061
1062 if (odp_shm_free(shm)) {
1063 ODPH_ERR("Shared memory free failed.\n");
1064 exit(EXIT_FAILURE);
1065 }
1066
1067 if (odp_term_local()) {
1068 ODPH_ERR("Term local failed.\n");
1069 return -1;
1070 }
1071
1072 if (odp_term_global(instance)) {
1073 ODPH_ERR("Term global failed.\n");
1074 return -1;
1075 }
1076
1077 return 0;
1078}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_inc_u32(odp_atomic_u32_t *atom)
Increment atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
odp_event_t odp_buffer_to_event(odp_buffer_t buf)
Convert buffer handle to event.
odp_buffer_t odp_buffer_alloc(odp_pool_t pool)
Buffer alloc.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
CPU cycle count difference.
uint64_t odp_cpu_cycles_strict(void)
Current CPU cycle count (strict)
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
void odp_event_free(odp_event_t event)
Free event.
#define ODP_EVENT_INVALID
Invalid event.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_BUFFER
Buffer pool.
odp_nonblocking_t
Non-blocking level.
int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num)
Enqueue multiple events to a queue.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
int odp_queue_capability(odp_queue_capability_t *capa)
Query queue capabilities.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_event_t odp_queue_deq(odp_queue_t queue)
Dequeue an event from a queue.
int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
Enqueue an event to a queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
int odp_queue_deq_multi(odp_queue_t queue, odp_event_t events[], int num)
Dequeue multiple events from a queue.
@ ODP_NONBLOCKING_WF
Non-blocking and wait-free implementation.
@ ODP_BLOCKING
Blocking implementation.
@ ODP_NONBLOCKING_LF
Non-blocking and lock-free implementation.
@ ODP_QUEUE_TYPE_PLAIN
Plain queue.
@ ODP_QUEUE_OP_MT_UNSAFE
Not multithread safe operation.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
void odp_sys_info_print(void)
Print system info.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
@ ODP_THREAD_WORKER
Worker thread.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_local_strict(void)
Current local time (strict)
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@133 buf
Buffer pool capabilities
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
struct odp_pool_param_t::@138 buf
Parameters for buffer pools.
struct odp_queue_capability_t::@155 plain
Plain queue capabilities.
uint32_t max_size
Maximum number of events a plain (ODP_BLOCKING) queue can store simultaneously.
uint32_t max_num
Maximum number of plain (ODP_BLOCKING) queues of the default size.
struct odp_queue_capability_t::@155::@156 lockfree
Lock-free (ODP_NONBLOCKING_LF) implementation capabilities.
struct odp_queue_capability_t::@155::@157 waitfree
Wait-free (ODP_NONBLOCKING_WF) implementation capabilities.
ODP Queue parameters.
odp_queue_op_mode_t enq_mode
Enqueue mode.
uint32_t size
Queue size.
odp_queue_type_t type
Queue type.
odp_queue_op_mode_t deq_mode
Dequeue mode.
odp_nonblocking_t nonblocking
Non-blocking level.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
struct odp_feature_t::@174 feat
Individual feature bits.
uint32_t schedule
Scheduler APIs, e.g., odp_schedule_xxx()
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()