API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_mem_perf.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2021-2026 Nokia
3 */
4
13#include <stdio.h>
14#include <string.h>
15#include <stdint.h>
16#include <inttypes.h>
17#include <stdlib.h>
18#include <getopt.h>
19
20#include <odp_api.h>
21#include <odp/helper/odph_api.h>
22
23/* Maximum number of stash get/put retries */
24#define MAX_STASH_RETRY 100
25
26/* Minimum shared memory block length in bytes */
27#define MIN_BLOCK_LEN 1024
28
29typedef struct test_options_t {
30 uint32_t num_cpu;
31 uint32_t num_round;
32 uint64_t data_len;
33 uint32_t shm_flags;
34 uint32_t shared_blocks;
35 int private;
36 int mode;
37
38} test_options_t;
39
40typedef struct test_global_t test_global_t;
41
42typedef struct test_thread_ctx_t {
43 test_global_t *global;
44 void *shm_addr;
45 uint64_t nsec;
46 uint64_t dummy_sum;
47
48} test_thread_ctx_t;
49
50struct test_global_t {
51 test_options_t test_options;
52
53 odp_barrier_t barrier;
54 uint32_t num_shm;
56 void *shm_addr[ODP_THREAD_COUNT_MAX];
57 odp_stash_t stash;
58 uint64_t block_len;
59 odp_cpumask_t cpumask;
60 odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
61 test_thread_ctx_t thread_ctx[ODP_THREAD_COUNT_MAX];
62
63};
64
65static void print_usage(void)
66{
67 printf("\n"
68 "Memory performance test\n"
69 "\n"
70 "Usage: odp_mem_perf [options]\n"
71 "\n"
72 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default 1.\n"
73 " -r, --num_round Number of rounds. Default 1000.\n"
74 " -l, --data_len Data length in bytes. Default 10MB.\n"
75 " -f, --flags SHM flags parameter. Default 0.\n"
76 " -p, --private 0: Shared memory. The area is split into blocks stored in a stash.\n"
77 " Threads share the memory, and each block is used by one\n"
78 " thread at a time (default).\n"
79 " 1: Memory areas are private to each thread. This increases\n"
80 " memory consumption to num_cpu * data_len.\n"
81 " -s, --shared_blocks Number of memory blocks in shared memory mode. The shared\n"
82 " area is split into this many equal, cache line aligned blocks.\n"
83 " Default: 2 x num_cpu. Minimum: num_cpu. Maximum: data_len / %u\n"
84 " (i.e. min block size).\n"
85 " -m, --mode 0: Memset data with varying value (default)\n"
86 " 1: Memset data with zero\n"
87 " 2: Memcpy data. On each round, reads data from one half of the memory area\n"
88 " and writes it to the other half.\n"
89 " 3: Read data. On each round, reads through the entire memory area.\n"
90 " 4: Memcmp data. On each round, compares data of one half of the memory\n"
91 " area to the other half.\n"
92 " -h, --help This help\n"
93 "\n", MIN_BLOCK_LEN);
94}
95
96static int parse_options(int argc, char *argv[], test_options_t *test_options)
97{
98 int opt;
99 int ret = 0;
100
101 static const struct option longopts[] = {
102 {"num_cpu", required_argument, NULL, 'c'},
103 {"num_round", required_argument, NULL, 'r'},
104 {"data_len", required_argument, NULL, 'l'},
105 {"flags", required_argument, NULL, 'f'},
106 {"private", required_argument, NULL, 'p'},
107 {"shared_blocks", required_argument, NULL, 's'},
108 {"mode", required_argument, NULL, 'm'},
109 {"help", no_argument, NULL, 'h'},
110 {NULL, 0, NULL, 0}
111 };
112
113 static const char *shortopts = "+c:r:l:f:p:s:m:h";
114
115 test_options->num_cpu = 1;
116 test_options->num_round = 1000;
117 test_options->data_len = 10 * 1024 * 1024;
118 test_options->shm_flags = 0;
119 test_options->shared_blocks = 0; /* when 0, defaults to 2x num_cpu */
120 test_options->private = 0;
121 test_options->mode = 0;
122
123 while (1) {
124 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
125
126 if (opt == -1)
127 break;
128
129 switch (opt) {
130 case 'c':
131 test_options->num_cpu = atoi(optarg);
132 break;
133 case 'r':
134 test_options->num_round = atoi(optarg);
135 break;
136 case 'l':
137 test_options->data_len = strtoull(optarg, NULL, 0);
138 break;
139 case 'f':
140 test_options->shm_flags = strtoul(optarg, NULL, 0);
141 break;
142 case 'p':
143 test_options->private = atoi(optarg);
144 break;
145 case 's':
146 test_options->shared_blocks = strtoul(optarg, NULL, 0);
147 break;
148 case 'm':
149 test_options->mode = atoi(optarg);
150 break;
151 case 'h':
152 /* fall through */
153 default:
154 print_usage();
155 ret = -1;
156 break;
157 }
158 }
159
160 if (test_options->mode < 0 || test_options->mode > 4) {
161 ODPH_ERR("Bad mode: %i\n", test_options->mode);
162 return -1;
163 }
164
165 return ret;
166}
167
168static int set_num_cpu(test_global_t *global)
169{
170 int ret, max_num;
171 test_options_t *test_options = &global->test_options;
172 int num_cpu = test_options->num_cpu;
173
174 /* One thread used for the main thread */
175 if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
176 ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
177 return -1;
178 }
179
180 max_num = num_cpu;
181 if (num_cpu == 0)
182 max_num = ODP_THREAD_COUNT_MAX - 1;
183
184 ret = odp_cpumask_default_worker(&global->cpumask, max_num);
185
186 if (num_cpu && ret != num_cpu) {
187 ODPH_ERR("Too many workers. Max supported %i.\n", ret);
188 return -1;
189 }
190
191 /* Zero: all available workers */
192 if (num_cpu == 0) {
193 if (ret > max_num) {
194 ODPH_ERR("Too many cpus from odp_cpumask_default_worker(): %i\n", ret);
195 return -1;
196 }
197
198 num_cpu = ret;
199 test_options->num_cpu = num_cpu;
200 }
201
202 odp_barrier_init(&global->barrier, num_cpu);
203
204 return 0;
205}
206
207/* Resolve shared block count and cache-line-aligned block length */
208static int set_shared_blocks(test_global_t *global)
209{
210 test_options_t *opt = &global->test_options;
211 uint32_t num_cpu = opt->num_cpu;
212 uint64_t max_blocks;
213 uint64_t block_len;
214
215 if (opt->private)
216 return 0;
217
218 max_blocks = opt->data_len / MIN_BLOCK_LEN;
219 if (max_blocks < num_cpu) {
220 ODPH_ERR("Data length %" PRIu64 " is too small for %u threads. "
221 "At least %" PRIu64 " bytes needed.\n",
222 opt->data_len, num_cpu, (uint64_t)num_cpu * MIN_BLOCK_LEN);
223 return -1;
224 }
225
226 /* Default is two blocks per thread */
227 if (opt->shared_blocks == 0) {
228 if ((uint64_t)num_cpu * 2 > max_blocks) {
229 ODPH_ERR("Default shared block count %u exceeds maximum %" PRIu64 ". "
230 "Increase data length or set --shared_blocks.\n",
231 num_cpu * 2, max_blocks);
232 return -1;
233 }
234
235 opt->shared_blocks = num_cpu * 2;
236 }
237
238 if (opt->shared_blocks < num_cpu || (uint64_t)opt->shared_blocks > max_blocks) {
239 ODPH_ERR("Invalid shared block count %u. Valid range is %u ... %" PRIu64 ".\n",
240 opt->shared_blocks, num_cpu, max_blocks);
241 return -1;
242 }
243
244 /* Round block length down so that every block stays inside the area and
245 * starts on a cache line */
246 block_len = opt->data_len / opt->shared_blocks;
247 block_len &= ~((uint64_t)ODP_CACHE_LINE_SIZE - 1);
248
249 if (block_len < MIN_BLOCK_LEN) {
250 ODPH_ERR("Block size %" PRIu64 " is below %u bytes after cache line alignment.\n",
251 block_len, MIN_BLOCK_LEN);
252 return -1;
253 }
254
255 global->block_len = block_len;
256
257 return 0;
258}
259
260static int create_shm(test_global_t *global)
261{
262 odp_shm_capability_t shm_capa;
263 odp_shm_t shm;
264 void *addr;
265 uint32_t i, num_shm;
266 test_options_t *test_options = &global->test_options;
267 uint32_t num_round = test_options->num_round;
268 uint32_t num_cpu = test_options->num_cpu;
269 uint64_t data_len = test_options->data_len;
270 uint32_t shm_flags = test_options->shm_flags;
271 int private = test_options->private;
272 char name[] = "mem_perf_00";
273
274 num_shm = 1;
275 if (private)
276 num_shm = num_cpu;
277
278 printf("\nMemory performance test\n");
279 printf(" num cpu %u\n", num_cpu);
280 printf(" num rounds %u\n", num_round);
281 printf(" data len %" PRIu64 "\n", data_len);
282 printf(" memory footprint %" PRIu64 "\n", num_shm * data_len);
283 printf(" shm flags 0x%x\n", shm_flags);
284 printf(" num shm %u\n", num_shm);
285 printf(" private %i\n", private);
286 if (!private) {
287 printf(" shared blocks %u\n", test_options->shared_blocks);
288 printf(" block len %" PRIu64 "\n", global->block_len);
289 }
290 printf(" mode %i\n", test_options->mode);
291
292 if (odp_shm_capability(&shm_capa)) {
293 ODPH_ERR("SHM capa failed.\n");
294 return -1;
295 }
296
297 if (shm_capa.max_size && data_len > shm_capa.max_size) {
298 ODPH_ERR("Data len too large. Maximum len is %" PRIu64 "\n", shm_capa.max_size);
299 return -1;
300 }
301
302 if (num_shm > shm_capa.max_blocks) {
303 ODPH_ERR("Too many SHM blocks. Maximum is %u\n", shm_capa.max_blocks);
304 return -1;
305 }
306
307 for (i = 0; i < num_shm; i++) {
308 name[9] = '0' + i / 10;
309 name[10] = '0' + i % 10;
310
311 shm = odp_shm_reserve(name, data_len, ODP_CACHE_LINE_SIZE, shm_flags);
312
313 if (shm == ODP_SHM_INVALID) {
314 ODPH_ERR("SHM[%u] reserve failed.\n", i);
315 return -1;
316 }
317
318 global->shm[i] = shm;
319
320 addr = odp_shm_addr(shm);
321 if (addr == NULL) {
322 ODPH_ERR("SHM[%u] addr failed.\n", i);
323 return -1;
324 }
325
326 global->shm_addr[i] = addr;
327
328 printf(" shm addr[%u] %p\n", i, addr);
329 }
330
331 printf("\n");
332 global->num_shm = num_shm;
333
335
336 return 0;
337}
338
339static int free_shm(test_global_t *global)
340{
341 uint32_t i;
342
343 for (i = 0; i < global->num_shm; i++) {
344 if (odp_shm_free(global->shm[i])) {
345 ODPH_ERR("SHM[%u] free failed.\n", i);
346 return -1;
347 }
348 }
349
350 return 0;
351}
352
353static int create_stash(test_global_t *global)
354{
356 odp_stash_param_t param;
357 test_options_t *opt = &global->test_options;
358 uint32_t num_block = opt->shared_blocks;
359 uint64_t block_len = global->block_len;
360 uint8_t *base;
361 uint32_t i;
362 uintptr_t ptr;
363
364 if (opt->private)
365 return 0;
366
367 if (odp_stash_capability(&capa, ODP_STASH_TYPE_FIFO) || capa.max_stashes == 0) {
368 ODPH_ERR("FIFO stash is not supported.\n");
369 return -1;
370 }
371
372 if (num_block > capa.max_num.ptr) {
373 ODPH_ERR("Too many shared blocks for stash (%u). Maximum %" PRIu64 ".\n",
374 num_block, capa.max_num.ptr);
375 return -1;
376 }
377
378 odp_stash_param_init(&param);
382 param.num_obj = num_block;
383 param.obj_size = sizeof(uintptr_t);
384 /* Not using thread local cache as each thread should touch all
385 * blocks during a test run */
386 param.cache_size = 0;
387 param.strict_size = 1;
388
389 global->stash = odp_stash_create("mem_perf_blocks", &param);
390 if (global->stash == ODP_STASH_INVALID) {
391 ODPH_ERR("Stash create failed.\n");
392 return -1;
393 }
394
395 base = global->shm_addr[0];
396
397 for (i = 0; i < num_block; i++) {
398 ptr = (uintptr_t)(base + ((uint64_t)i * block_len));
399
400 if (odp_stash_put_ptr(global->stash, &ptr, 1) != 1) {
401 ODPH_ERR("Stash put failed for block %u.\n", i);
402 return -1;
403 }
404 }
405
406 return 0;
407}
408
409static int destroy_stash(test_global_t *global)
410{
411 uint32_t i;
412 uintptr_t ptr;
413 int ret;
414
415 if (global->test_options.private)
416 return 0;
417
418 for (i = 0; i < global->test_options.shared_blocks; i++) {
419 ret = odp_stash_get_ptr(global->stash, &ptr, 1);
420 if (ret != 1) {
421 ODPH_ERR("Stash get failed while emptying (%u).\n", i);
422 return -1;
423 }
424 }
425
426 if (odp_stash_destroy(global->stash)) {
427 ODPH_ERR("Stash destroy failed.\n");
428 return -1;
429 }
430
431 return 0;
432}
433
434/* Read through the data area. Calculate sum, so that the compiler
435 * cannot optimize the reads away. */
436static inline uint64_t read_data(const uint64_t *data, uint64_t num_word)
437{
438 uint64_t i;
439 uint64_t sum_0 = 0;
440 uint64_t sum_1 = 0;
441 uint64_t sum_2 = 0;
442 uint64_t sum_3 = 0;
443 uint64_t num_unroll = num_word & ~(uint64_t)0x7;
444
445 /* Data start is cache line aligned. Read 8 words per loop. */
446 for (i = 0; i < num_unroll; i += 8) {
447 sum_0 += data[i + 0] + data[i + 1];
448 sum_1 += data[i + 2] + data[i + 3];
449 sum_2 += data[i + 4] + data[i + 5];
450 sum_3 += data[i + 6] + data[i + 7];
451 }
452
453 /* Left over words */
454 for (; i < num_word; i++)
455 sum_0 += data[i];
456
457 return sum_0 + sum_1 + sum_2 + sum_3;
458}
459
460static int run_test_private(void *arg)
461{
462 int thr;
463 uint32_t i;
464 uint64_t nsec;
465 odp_time_t t1, t2;
466 test_thread_ctx_t *thread_ctx = arg;
467 test_global_t *global = thread_ctx->global;
468 test_options_t *test_options = &global->test_options;
469 uint32_t num_round = test_options->num_round;
470 uint64_t data_len = test_options->data_len;
471 uint64_t half_len = data_len / 2;
472 uint64_t num_word = data_len / sizeof(uint64_t);
473 uint64_t dummy_sum = 0;
474 int mode = test_options->mode;
475 uint8_t *addr = thread_ctx->shm_addr;
476
477 thr = odp_thread_id();
478
479 /* Write the data area so that all pages are faulted in before test starts */
480 memset(addr, thr, data_len);
481
482 /* Start all workers at the same time */
483 odp_barrier_wait(&global->barrier);
484
485 t1 = odp_time_local();
486
487 if (mode == 0) {
488 for (i = 0; i < num_round; i++)
489 memset(addr, thr + i, data_len);
490 } else if (mode == 1) {
491 for (i = 0; i < num_round; i++)
492 memset(addr, 0, data_len);
493 } else if (mode == 2) {
494 for (i = 0; i < num_round; i++) {
495 if ((i & 0x1) == 0)
496 memcpy(&addr[half_len], addr, half_len);
497 else
498 memcpy(addr, &addr[half_len], half_len);
499 }
500 } else if (mode == 3) {
501 for (i = 0; i < num_round; i++)
502 dummy_sum += read_data((const uint64_t *)(uintptr_t)addr, num_word);
503 } else {
504 /* volatile prevents compiler from optimizing the loop away */
505 uint8_t *volatile cmp_addr = addr;
506
507 for (i = 0; i < num_round; i++)
508 dummy_sum |= memcmp(cmp_addr, &cmp_addr[half_len], half_len);
509 }
510
511 t2 = odp_time_local();
512
513 nsec = odp_time_diff_ns(t2, t1);
514
515 /* Update stats */
516 thread_ctx->nsec = nsec;
517 thread_ctx->dummy_sum = dummy_sum;
518
519 return 0;
520}
521
522static inline int get_block(odp_stash_t stash, uint8_t **addr)
523{
524 uintptr_t ptr;
525 int ret = 0;
526 int retry = MAX_STASH_RETRY;
527
528 while (retry--) {
529 ret = odp_stash_get_ptr(stash, &ptr, 1);
530 if (ret > 0) {
531 break;
532 } else if (odp_unlikely(ret < 0)) {
533 ODPH_ERR("Stash get failed.\n");
534 return -1;
535 }
536 }
537
538 if (odp_unlikely(ret == 0)) {
539 ODPH_ERR("Stash get exceeded %d retries.\n", MAX_STASH_RETRY);
540 return -1;
541 }
542
543 *addr = (uint8_t *)ptr;
544 return 0;
545}
546
547static inline int put_block(odp_stash_t stash, uint8_t *addr)
548{
549 uintptr_t ptr = (uintptr_t)addr;
550 int ret = 0;
551 int retry = MAX_STASH_RETRY;
552
553 while (retry--) {
554 ret = odp_stash_put_ptr(stash, &ptr, 1);
555 if (ret > 0) {
556 break;
557 } else if (odp_unlikely(ret < 0)) {
558 ODPH_ERR("Stash put failed.\n");
559 return -1;
560 }
561 }
562
563 if (odp_unlikely(ret == 0)) {
564 ODPH_ERR("Stash put exceeded %d retries.\n", MAX_STASH_RETRY);
565 return -1;
566 }
567
568 return 0;
569}
570
571static int run_test_shared(void *arg)
572{
573 int thr;
574 uint32_t i;
575 uint64_t nsec;
576 odp_time_t t1, t2;
577 test_thread_ctx_t *thread_ctx = arg;
578 test_global_t *global = thread_ctx->global;
579 test_options_t *test_options = &global->test_options;
580 uint32_t num_round = test_options->num_round;
581 uint64_t block_len = global->block_len;
582 uint64_t area_len = (uint64_t)test_options->shared_blocks * block_len;
583 uint64_t half_len = block_len / 2;
584 uint64_t num_word = block_len / sizeof(uint64_t);
585 uint64_t dummy_sum = 0;
586 const int mode = test_options->mode;
587 odp_stash_t stash = global->stash;
588 uint8_t *base = thread_ctx->shm_addr;
589 uint8_t *addr;
590
591 thr = odp_thread_id();
592
593 /* Fault in the shared area before the timed section. During the test, a block
594 * is held by only one worker at a time. In memcmp mode, all workers write the same
595 * value, so that block halves match regardless of the write order between workers. */
596 if (mode == 4)
597 memset(base, 0, area_len);
598 else
599 memset(base, thr, area_len);
600
601 /* Start all workers at the same time */
602 odp_barrier_wait(&global->barrier);
603
604 t1 = odp_time_local();
605
606 for (i = 0; i < num_round; i++) {
607 if (get_block(stash, &addr))
608 return -1;
609
610 if (mode == 0) {
611 memset(addr, thr + i, block_len);
612 } else if (mode == 1) {
613 memset(addr, 0, block_len);
614 } else if (mode == 2) {
615 if ((i & 0x1) == 0)
616 memcpy(&addr[half_len], addr, half_len);
617 else
618 memcpy(addr, &addr[half_len], half_len);
619 } else if (mode == 3) {
620 dummy_sum += read_data((const uint64_t *)(uintptr_t)addr, num_word);
621 } else {
622 dummy_sum |= memcmp(addr, &addr[half_len], half_len);
623 }
624
625 if (put_block(stash, addr))
626 return -1;
627 }
628
629 t2 = odp_time_local();
630 nsec = odp_time_diff_ns(t2, t1);
631
632 /* Update stats */
633 thread_ctx->nsec = nsec;
634 thread_ctx->dummy_sum = dummy_sum;
635
636 return 0;
637}
638
639static int start_workers(test_global_t *global, odp_instance_t instance)
640{
641 odph_thread_common_param_t param;
642 int i, ret;
643 test_options_t *test_options = &global->test_options;
644 int num_cpu = test_options->num_cpu;
645 odph_thread_param_t thr_param[num_cpu];
646
647 odph_thread_common_param_init(&param);
648 param.instance = instance;
649 param.cpumask = &global->cpumask;
650
651 for (i = 0; i < num_cpu; i++) {
652 test_thread_ctx_t *thread_ctx = &global->thread_ctx[i];
653
654 thread_ctx->global = global;
655 thread_ctx->shm_addr = global->shm_addr[0];
656 if (global->test_options.private)
657 thread_ctx->shm_addr = global->shm_addr[i];
658
659 odph_thread_param_init(&thr_param[i]);
660 thr_param[i].thr_type = ODP_THREAD_WORKER;
661 thr_param[i].start = test_options->private ? run_test_private : run_test_shared;
662 thr_param[i].arg = thread_ctx;
663 }
664
665 ret = odph_thread_create(global->thread_tbl, &param, thr_param, num_cpu);
666 if (ret != num_cpu) {
667 ODPH_ERR("Failed to create all threads %i\n", ret);
668 return -1;
669 }
670
671 return 0;
672}
673
674static int print_stat(test_global_t *global)
675{
676 int i, num;
677 double nsec_ave;
678 uint64_t data_touch;
679 test_options_t *test_options = &global->test_options;
680 int num_cpu = test_options->num_cpu;
681 uint32_t num_round = test_options->num_round;
682 uint64_t round_len = test_options->private ? test_options->data_len : global->block_len;
683 uint64_t nsec_sum = 0;
684 uint64_t dummy_sum = 0;
685 int num_mismatch = 0;
686 int ret = 0;
687
688 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
689 nsec_sum += global->thread_ctx[i].nsec;
690 dummy_sum += global->thread_ctx[i].dummy_sum;
691
692 if (global->thread_ctx[i].dummy_sum)
693 num_mismatch++;
694 }
695
696 if (nsec_sum == 0) {
697 printf("No results.\n");
698 return 0;
699 }
700
701 data_touch = num_round * round_len;
702 nsec_ave = nsec_sum / num_cpu;
703 num = 0;
704
705 printf("\ndummy_sum: %" PRIu64 "\n\n", dummy_sum);
706
707 if (test_options->mode == 4 && num_mismatch) {
708 printf("WARNING: memcmp mismatch on %i/%i threads!\n\n", num_mismatch, num_cpu);
709 ret = -1;
710 }
711
712 printf("RESULTS - per thread (MB per sec):\n");
713 printf("----------------------------------\n");
714 printf(" 1 2 3 4 5 6 7 8 9 10");
715
716 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
717 if (global->thread_ctx[i].nsec) {
718 if ((num % 10) == 0)
719 printf("\n ");
720
721 printf("%8.1f ", data_touch / (global->thread_ctx[i].nsec / 1000.0));
722 num++;
723 }
724 }
725 printf("\n\n");
726
727 if (num != num_cpu)
728 printf("WARNING: thread result miss-match %i/%i!\n\n", num, num_cpu);
729
730 printf("RESULTS - average over %i threads:\n", num_cpu);
731 printf("----------------------------------\n");
732 printf(" duration: %.6f sec\n", nsec_ave / 1000000000);
733 printf(" bandwidth per cpu: %.3f MB/s\n", data_touch / (nsec_ave / 1000.0));
734 printf(" total bandwidth: %.3f MB/s\n", (num_cpu * data_touch) / (nsec_ave / 1000.0));
735 printf("\n");
736
737 return ret;
738}
739
740int main(int argc, char **argv)
741{
742 odph_helper_options_t helper_options;
743 odp_instance_t instance;
744 odp_init_t init;
745 odp_shm_t shm;
746 test_global_t *global;
747 test_options_t test_options;
748 int ret = 0;
749
750 /* Let helper collect its own arguments (e.g. --odph_proc) */
751 argc = odph_parse_options(argc, argv);
752 if (odph_options(&helper_options)) {
753 ODPH_ERR("Reading ODP helper options failed.\n");
754 exit(EXIT_FAILURE);
755 }
756
757 if (parse_options(argc, argv, &test_options))
758 return -1;
759
760 /* List features not to be used */
761 odp_init_param_init(&init);
762 init.not_used.feat.cls = 1;
763 init.not_used.feat.compress = 1;
764 init.not_used.feat.crypto = 1;
765 init.not_used.feat.dma = 1;
766 init.not_used.feat.ipsec = 1;
767 init.not_used.feat.ml = 1;
768 init.not_used.feat.schedule = 1;
769 /* Stash used only in shared memory mode */
770 init.not_used.feat.stash = test_options.private ? 1 : 0;
771 init.not_used.feat.timer = 1;
772 init.not_used.feat.tm = 1;
773
774 init.mem_model = helper_options.mem_model;
775
776 /* Init ODP before calling anything else */
777 if (odp_init_global(&instance, &init, NULL)) {
778 ODPH_ERR("Global init failed.\n");
779 return -1;
780 }
781
782 /* Init this thread */
783 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
784 ODPH_ERR("Local init failed.\n");
785 return -1;
786 }
787
788 shm = odp_shm_reserve("mem_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
789 if (shm == ODP_SHM_INVALID) {
790 ODPH_ERR("Shared mem reserve failed.\n");
791 exit(EXIT_FAILURE);
792 }
793
794 global = odp_shm_addr(shm);
795 if (global == NULL) {
796 ODPH_ERR("Shared mem alloc failed\n");
797 exit(EXIT_FAILURE);
798 }
799
800 memset(global, 0, sizeof(test_global_t));
801 global->test_options = test_options;
802
804
805 if (set_num_cpu(global))
806 return -1;
807
808 if (set_shared_blocks(global))
809 return -1;
810
811 if (create_shm(global))
812 return -1;
813
814 if (create_stash(global))
815 return -1;
816
817 /* Start workers */
818 if (start_workers(global, instance))
819 return -1;
820
821 /* Wait workers to exit */
822 odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
823
824 ret = print_stat(global);
825
826 if (destroy_stash(global))
827 return -1;
828
829 if (free_shm(global))
830 return -1;
831
832 if (odp_shm_free(shm)) {
833 ODPH_ERR("Shared mem free failed.\n");
834 exit(EXIT_FAILURE);
835 }
836
837 if (odp_term_local()) {
838 ODPH_ERR("term local failed.\n");
839 return -1;
840 }
841
842 if (odp_term_global(instance)) {
843 ODPH_ERR("term global failed.\n");
844 return -1;
845 }
846
847 if (ret)
848 return EXIT_FAILURE;
849
850 return 0;
851}
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.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
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.
void odp_shm_print_all(void)
Print all shared memory blocks.
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.
int odp_shm_capability(odp_shm_capability_t *capa)
Query shared memory capabilities.
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.
#define ODP_STASH_INVALID
Invalid stash handle.
int32_t odp_stash_get_ptr(odp_stash_t stash, uintptr_t ptr[], int32_t num)
Get pointers from a stash.
void odp_stash_param_init(odp_stash_param_t *param)
Initialize stash params.
int odp_stash_destroy(odp_stash_t stash)
Destroy a stash.
int odp_stash_capability(odp_stash_capability_t *capa, odp_stash_type_t type)
Query stash capabilities.
int32_t odp_stash_put_ptr(odp_stash_t stash, const uintptr_t ptr[], int32_t num)
Put pointers into a stash.
odp_stash_t odp_stash_create(const char *name, const odp_stash_param_t *param)
Create a stash.
@ ODP_STASH_TYPE_FIFO
Stash type FIFO.
@ ODP_STASH_OP_MT
Multi-thread safe operation.
void odp_sys_info_print(void)
Print system info.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
odp_time_t odp_time_local(void)
Current local time.
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.
Shared memory capabilities.
uint32_t max_blocks
Maximum number of shared memory blocks.
uint64_t max_size
Maximum memory block size in bytes.
Stash capabilities (per stash type)
uint64_t ptr
Maximum number of pointers.
uint32_t max_stashes
Maximum number of stashes of this type.
struct odp_stash_capability_t::@171 max_num
Maximum number of object handles per stash for each object size.
uint32_t cache_size
Maximum number of object handles cached locally per thread.
odp_stash_op_mode_t get_mode
Get operation mode.
odp_bool_t strict_size
Strict size.
uint32_t obj_size
Object handle size in bytes.
odp_stash_type_t type
Stash type.
odp_stash_op_mode_t put_mode
Put operation mode.
uint64_t num_obj
Number of object handles.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t stash
Stash APIs, e.g., odp_stash_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t dma
DMA APIs, e.g., odp_dma_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()
uint32_t ml
Machine Learning APIs, e.g., odp_ml_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()