27#include <odp/helper/odph_api.h>
29#include <export_results.h>
31#define MAX_QUEUES (256 * 1024)
35#define DATA_MASK 0xffff
38#define MAX_SCHED_WAIT_NS (10 * ODP_TIME_SEC_IN_NS)
41#define TIME_CHECK_INTERVAL (1024 * 1024)
43typedef struct test_options_t {
54 uint32_t num_prefetch;
64 uint32_t forward_group_size;
72 uint32_t ctx_rd_words;
73 uint32_t ctx_rw_words;
84typedef struct test_stat_t {
96typedef struct thread_arg_t {
102typedef struct test_global_t {
103 test_options_t test_options;
123 test_common_options_t common_options;
132static test_global_t *test_globals;
139static int setup_sig_handler(
void)
141 struct sigaction action = { .sa_handler = sig_handler };
143 if (sigemptyset(&action.sa_mask) || sigaction(SIGINT, &action, NULL))
153 region->
l2.
len = len;
156 region->
l3.
len = len;
161static int parse_cache_stash_config(
char *optarg, test_options_t *test_options)
163 uint32_t region, level, offset, len;
166 if (sscanf(optarg,
"%u,%u,%u,%u", ®ion, &level, &offset, &len) != 4) {
167 ODPH_ERR(
"Invalid number of arguments for cache stashing\n");
172 ODPH_ERR(
"Invalid region for cache stashing: %u\n", region);
177 ODPH_ERR(
"Invalid cache level for cache stashing: %u\n", level);
182 ODPH_ERR(
"Invalid len for cache stashing: %u\n", len);
186 stash_config->
regions.
all |= (1U << (region * 2 + level));
190 set_cache_stash(&stash_config->
event_metadata, level, len, offset);
193 set_cache_stash(&stash_config->
event_data, level, len, offset);
199 set_cache_stash(&stash_config->
queue_context, level, len, offset);
208static void print_usage(
void)
211 "Scheduler performance test\n"
213 "Usage: odp_sched_perf [options]\n"
215 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1.\n"
216 " -q, --num_def Number of default priority queues. Default: 1.\n"
217 " -L, --num_low Number of lowest priority queues. Default: 0.\n"
218 " -H, --num_high Number of highest priority queues. Default: 0.\n"
219 " -d, --num_dummy Number of empty queues. Default: 0.\n"
220 " -e, --num_event Number of events per queue. Default: 100.\n"
221 " -s, --num_sched Number of events to schedule per thread. If zero, the application runs\n"
222 " until SIGINT is received. Default: 100 000.\n"
223 " -g, --num_group Number of schedule groups. Round robins threads and queues into groups.\n"
224 " -1: SCHED_GROUP_WORKER\n"
225 " 0: SCHED_GROUP_ALL (default)\n"
226 " -j, --num_join Number of groups a thread joins. Threads are divide evenly into groups,\n"
227 " if num_cpu is multiple of num_group and num_group is multiple of num_join.\n"
228 " 0: join all groups (default)\n"
229 " -b, --burst Maximum number of events per operation. Default: 100.\n"
230 " -C, --cache_stash Enable common group level cache stashing. Format: region,level,offset,len\n"
231 " region: 0: Event metadata, 1: Event data, 2: Event user area, 3: Queue context\n"
232 " level: 0: L2, 1: L3\n"
233 " offset/len: in bytes\n"
234 " E.g.: 1,0,16,32 enables 32-byte L2 stash on event data with a 16-byte offset\n"
235 " For stashing multiple regions, use -C multiple times\n"
236 " -t, --type Queue type. 0: parallel, 1: atomic, 2: ordered. Default: 0.\n"
237 " -T, --thr_type Thread type. 0: worker thread, 1: control thread. Default: 0\n"
238 " -f, --forward 0: Keep event in the original queue (default)\n"
239 " 1: Forward event between all queues\n"
240 " N: Forward events between queues of N identical queue sets. In this mode 'num_def',\n"
241 " 'num_low', and 'num_high' options are per set, so the total number of active\n"
242 " queues is N * (num_def + num_low + num_high).\n"
243 " -F, --fairness 0: Don't count events per queue, 1: Count and report events relative to average. Default: 0.\n"
244 " -w, --wait_ns Number of nsec to wait before enqueueing events. Default: 0.\n"
245 " -S, --stress CPU stress function(s) to be called for each event data word (requires -n or -m).\n"
246 " Data is processed as uint32_t words. Multiple flags may be selected.\n"
247 " 0: No extra data processing (default)\n"
248 " 0x1: Calculate square of each uint32_t\n"
249 " 0x2: Calculate log2 of each uint32_t\n"
250 " 0x4: Calculate square root of each uint32_t\n"
251 " 0x8: Calculate square root of each uint32_t in floating point\n"
252 " -k, --ctx_rd_words Number of queue context words (uint64_t) to read on every event. Default: 0.\n"
253 " -l, --ctx_rw_words Number of queue context words (uint64_t) to modify on every event. Default: 0.\n"
254 " -n, --rd_words Number of event data words (uint64_t) to read before enqueueing it. Default: 0.\n"
255 " -m, --rw_words Number of event data words (uint64_t) to modify before enqueueing it. Default: 0.\n"
256 " -u, --uarea_rd Number of user area words (uint64_t) to read on every event. Default: 0.\n"
257 " -U, --uarea_rw Number of user area words (uint64_t) to modify on every event. Default: 0.\n"
258 " -p, --pool_type Pool type. 0: buffer, 1: packet, 2: event vector. Default: 0.\n"
259 " -P, --prefetch Number of events to be prefetched. Default: 0.\n"
260 " -v, --verbose Verbose output.\n"
261 " -h, --help This help\n"
265static int parse_options(
int argc,
char *argv[], test_options_t *test_options)
267 int opt, num_group, num_join;
269 uint32_t ctx_size = 0;
272 static const struct option longopts[] = {
273 {
"num_cpu", required_argument, NULL,
'c'},
274 {
"num_def", required_argument, NULL,
'q'},
275 {
"num_low", required_argument, NULL,
'L'},
276 {
"num_high", required_argument, NULL,
'H'},
277 {
"num_dummy", required_argument, NULL,
'd'},
278 {
"num_event", required_argument, NULL,
'e'},
279 {
"num_sched", required_argument, NULL,
's'},
280 {
"num_group", required_argument, NULL,
'g'},
281 {
"num_join", required_argument, NULL,
'j'},
282 {
"burst", required_argument, NULL,
'b'},
283 {
"cache_stash", required_argument, NULL,
'C'},
284 {
"type", required_argument, NULL,
't'},
285 {
"thr_type", required_argument, NULL,
'T'},
286 {
"forward", required_argument, NULL,
'f'},
287 {
"fairness", required_argument, NULL,
'F'},
288 {
"wait_ns", required_argument, NULL,
'w'},
289 {
"stress", required_argument, NULL,
'S'},
290 {
"ctx_rd_words", required_argument, NULL,
'k'},
291 {
"ctx_rw_words", required_argument, NULL,
'l'},
292 {
"rd_words", required_argument, NULL,
'n'},
293 {
"rw_words", required_argument, NULL,
'm'},
294 {
"uarea_rd", required_argument, NULL,
'u'},
295 {
"uarea_rw", required_argument, NULL,
'U'},
296 {
"pool_type", required_argument, NULL,
'p'},
297 {
"prefetch", required_argument, NULL,
'P'},
298 {
"verbose", no_argument, NULL,
'v'},
299 {
"help", no_argument, NULL,
'h'},
303 static const char *shortopts =
"+c:q:L:H:d:e:s:g:j:b:C:t:T:f:F:w:S:k:l:n:m:p:P:u:U:vh";
305 test_options->num_cpu = 1;
306 test_options->num_def = 1;
307 test_options->num_event = 100;
308 test_options->num_sched = 100000;
309 test_options->max_burst = 100;
312 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
319 test_options->num_cpu = atoi(optarg);
322 test_options->num_def = atoi(optarg);
325 test_options->num_low = atoi(optarg);
328 test_options->num_high = atoi(optarg);
331 test_options->num_dummy = atoi(optarg);
334 test_options->num_event = atoi(optarg);
337 test_options->num_sched = atoi(optarg);
340 test_options->num_group = atoi(optarg);
343 test_options->num_join = atoi(optarg);
346 if (parse_cache_stash_config(optarg, test_options))
350 test_options->max_burst = atoi(optarg);
353 test_options->queue_type = atoi(optarg);
356 test_options->thr_type = atoi(optarg);
359 test_options->forward = atoi(optarg);
362 test_options->fairness = atoi(optarg);
365 test_options->stress = strtoul(optarg, NULL, 0);
368 test_options->ctx_rd_words = atoi(optarg);
371 test_options->ctx_rw_words = atoi(optarg);
374 test_options->rd_words = atoi(optarg);
377 test_options->rw_words = atoi(optarg);
380 test_options->uarea_rd = atoi(optarg);
383 test_options->uarea_rw = atoi(optarg);
386 pool_type = atoi(optarg);
389 test_options->num_prefetch = atoi(optarg);
392 test_options->wait_ns = atoll(optarg);
395 test_options->verbose = 1;
416 ODPH_ERR(
"Invalid pool type: %d.\n", pool_type);
421 test_options->touch_data = test_options->rd_words ||
422 test_options->rw_words;
424 if (test_options->stress && test_options->touch_data == 0) {
425 ODPH_ERR(
"Use -n or/and -m to select event data size with a stress function\n");
429 test_options->forward_group_size = test_options->num_def + test_options->num_low +
430 test_options->num_high;
432 if (test_options->forward > 1) {
433 test_options->num_def *= test_options->forward;
434 test_options->num_low *= test_options->forward;
435 test_options->num_high *= test_options->forward;
436 }
else if (test_options->forward < 0) {
437 ODPH_ERR(
"Invalid forward mode %i.\n", test_options->forward);
441 test_options->num_queue = test_options->num_def + test_options->num_low +
442 test_options->num_high;
443 if ((test_options->num_queue + test_options->num_dummy) > MAX_QUEUES) {
444 ODPH_ERR(
"Too many queues. Max supported %i.\n", MAX_QUEUES);
448 num_group = test_options->num_group;
449 num_join = test_options->num_join;
450 if (num_group > MAX_GROUPS) {
451 ODPH_ERR(
"Too many groups. Max supported %i.\n", MAX_GROUPS);
455 if (num_group > 0 && num_join > num_group) {
456 ODPH_ERR(
"num_join (%i) larger than num_group (%i).\n", num_join, num_group);
460 if (num_join && num_group > (
int)(test_options->num_cpu * num_join)) {
461 printf(
"WARNING: Too many groups (%i). Some groups (%i) are not served.\n\n",
462 num_group, num_group - (test_options->num_cpu * num_join));
464 if (test_options->forward) {
465 ODPH_ERR(
"Cannot forward when some queues are not served.\n");
470 test_options->tot_queue = test_options->num_queue +
471 test_options->num_dummy;
472 test_options->tot_event = test_options->num_queue *
473 test_options->num_event;
475 test_options->queue_size = test_options->num_event;
477 if (test_options->forward) {
479 test_options->queue_size *= test_options->forward_group_size;
482 if (test_options->forward || test_options->fairness)
483 ctx_size =
sizeof(queue_context_t);
485 if (test_options->ctx_rd_words || test_options->ctx_rw_words) {
488 ctx_size = ODPH_ROUNDUP_MULTIPLE(ctx_size, 8);
489 ctx_size += 8 * test_options->ctx_rd_words;
490 ctx_size += 8 * test_options->ctx_rw_words;
495 if (test_options->fairness || test_options->ctx_rw_words)
498 test_options->ctx_size = ctx_size;
499 test_options->uarea_size = 8 * (test_options->uarea_rd + test_options->uarea_rw);
500 test_options->tot_rd_size = 8 * (test_options->ctx_rd_words + test_options->uarea_rd +
501 test_options->rd_words);
502 test_options->tot_rw_size = 8 * (test_options->ctx_rw_words + test_options->uarea_rw +
503 test_options->rw_words);
508static int set_num_cpu(test_global_t *global)
511 test_options_t *test_options = &global->test_options;
512 int num_cpu = test_options->num_cpu;
522 if (num_cpu && ret != num_cpu) {
523 ODPH_ERR(
"Too many workers. Max supported %i\n.", ret);
530 test_options->num_cpu = num_cpu;
538static uint64_t init_data(uint64_t init, uint64_t *data, uint32_t words)
543 for (i = 0; i < words; i++) {
545 val = (val + 1) & DATA_MASK;
551static void print_options(test_options_t *options)
553 printf(
"\nScheduler performance test\n");
554 printf(
" num sched %u\n", options->num_sched);
555 printf(
" num cpu %u\n", options->num_cpu);
556 printf(
" num default prio queues %u\n", options->num_def);
557 printf(
" num lowest prio queues %u\n", options->num_low);
558 printf(
" num highest prio queues %u\n", options->num_high);
559 printf(
" num empty queues %u\n", options->num_dummy);
560 printf(
" total queues %u\n", options->tot_queue);
561 printf(
" num groups %i", options->num_group);
563 if (options->num_group == -1)
564 printf(
" (ODP_SCHED_GROUP_WORKER)\n");
565 else if (options->num_group == 0)
566 printf(
" (ODP_SCHED_GROUP_ALL)\n");
570 printf(
" num join %u\n", options->num_join);
571 printf(
" forward events %i\n", options->forward);
572 printf(
" wait %" PRIu64
" nsec\n", options->wait_ns);
573 printf(
" events per queue %u\n", options->num_event);
574 printf(
" queue size %u\n", options->queue_size);
575 printf(
" max burst size %u\n", options->max_burst);
576 printf(
" total events %u\n", options->tot_event);
577 printf(
" stress 0x%x\n", options->stress);
579 printf(
" event size %u bytes", options->event_size);
580 if (options->touch_data)
581 printf(
" (rd: %u, rw: %u)", 8 * options->rd_words, 8 * options->rw_words);
584 printf(
" queue context size %u bytes", options->ctx_size);
585 if (options->ctx_rd_words || options->ctx_rw_words) {
586 printf(
" (rd: %u, rw: %u)",
587 8 * options->ctx_rd_words,
588 8 * options->ctx_rw_words);
592 printf(
" user area size %u bytes", options->uarea_size);
593 if (options->uarea_size)
594 printf(
" (rd: %u, rw: %u)", 8 * options->uarea_rd, 8 * options->uarea_rw);
597 printf(
" pool type %s\n", options->pool_type ==
ODP_POOL_BUFFER ?
"buffer"
601 printf(
" queue type %s\n", options->queue_type == 0 ?
"parallel" :
602 options->queue_type == 1 ?
"atomic" :
604 printf(
" thread type %s\n\n", options->thr_type == 0 ?
"worker" :
607 printf(
"Extra rd/rw ops per event (queue context + user area + event data)\n");
608 printf(
" read %u bytes\n", options->tot_rd_size);
609 printf(
" write %u bytes\n\n", options->tot_rw_size);
613 uint32_t tot_event, uint32_t uarea_size,
618 uint32_t max_num, max_uarea;
619 uint32_t max_vector_size = 0;
620 uint32_t max_size = 0;
621 uint32_t max_vector_pools = 0;
624 ODPH_ERR(
"Pool capa failed\n");
643 if (max_num && tot_event > max_num) {
644 ODPH_ERR(
"Max events supported %u\n", max_num);
648 if (max_size && event_size > max_size) {
649 ODPH_ERR(
"Max supported event size %u\n", max_size);
653 if (uarea_size > max_uarea) {
654 ODPH_ERR(
"Max supported user area size %u\n", max_uarea);
659 ODPH_ERR(
"Event vector pools not supported\n");
664 pool_param.
type = pool_type;
667 pool_param.
buf.
num = tot_event;
668 pool_param.
buf.
size = event_size;
672 pool_param.
pkt.
num = tot_event;
673 pool_param.
pkt.
len = event_size;
685 ODPH_ERR(
"Pool creation failed for type %d (%s)\n", pool_param.
type, name);
692static int create_pool(test_global_t *global)
694 test_options_t *test_options = &global->test_options;
695 uint32_t tot_event = test_options->tot_event;
696 uint32_t event_size = 16;
697 uint32_t uarea_size = test_options->uarea_size;
700 if (test_options->touch_data) {
701 event_size = test_options->rd_words + test_options->rw_words;
702 event_size = 8 * event_size;
704 test_options->event_size = event_size;
706 ret = create_pool_helper(
"sched perf", &global->pool, test_options->pool_type, tot_event,
707 uarea_size, event_size);
712 ret = create_pool_helper(
"sched perf event vector content", &global->evv_pool,
721static int create_groups(test_global_t *global)
726 test_options_t *test_options = &global->test_options;
727 uint32_t num_group = test_options->num_group;
730 if (test_options->num_group <= 0)
734 ODPH_ERR(
"Schedule capability failed\n");
739 ODPH_ERR(
"Too many sched groups (max_groups capa %u)\n", sched_capa.
max_groups);
747 for (i = 0; i < num_group; i++) {
753 ODPH_ERR(
"Group create failed %u\n", i);
757 global->group[i] = group;
763static void setup_forwarding(test_global_t *global)
765 const test_options_t *test_options = &global->test_options;
766 const uint32_t forward_group_size = test_options->forward_group_size;
767 const uint32_t num_groups = test_options->forward;
768 uint32_t num_low = test_options->num_low;
769 uint32_t num_high = test_options->num_high;
770 uint32_t num_def = test_options->num_def;
771 const uint32_t num_high_per_group = num_high / num_groups;
772 const uint32_t num_low_per_group = num_low / num_groups;
773 const uint32_t num_def_per_group = num_def / num_groups;
776 for (uint32_t i = 0; i < num_groups; i++) {
777 uint32_t num_group_low = num_low_per_group;
778 uint32_t num_group_high = num_high_per_group;
779 uint32_t num_group_def = num_def_per_group;
782 first_queue = global->queue.low_prio[--num_low];
784 }
else if (num_group_high) {
785 first_queue = global->queue.high_prio[--num_high];
788 first_queue = global->queue.def_prio[--num_def];
791 cur_queue = first_queue;
793 for (uint32_t j = 0; j < forward_group_size; j++) {
796 const uint32_t next_id = j + 1;
798 if (next_id == forward_group_size) {
800 *next_queue = first_queue;
805 switch (next_id % 3) {
808 *next_queue = global->queue.low_prio[--num_low];
810 }
else if (num_group_high) {
811 *next_queue = global->queue.high_prio[--num_high];
814 *next_queue = global->queue.def_prio[--num_def];
819 if (num_group_high) {
820 *next_queue = global->queue.high_prio[--num_high];
822 }
else if (num_group_low) {
823 *next_queue = global->queue.low_prio[--num_low];
826 *next_queue = global->queue.def_prio[--num_def];
832 *next_queue = global->queue.def_prio[--num_def];
834 }
else if (num_group_high) {
835 *next_queue = global->queue.high_prio[--num_high];
838 *next_queue = global->queue.low_prio[--num_low];
843 cur_queue = *next_queue;
848static int set_queue_contexts(test_global_t *global, uint8_t *ctx)
850 test_options_t *test_options = &global->test_options;
851 uint32_t tot_queue = test_options->tot_queue;
852 uint32_t ctx_size = test_options->ctx_size;
853 uint32_t first = test_options->num_dummy;
858 for (uint32_t i = first; i < tot_queue; i++) {
859 if (test_options->fairness) {
862 queue_context_t *qc = (queue_context_t *)(uintptr_t)ctx;
868 ODPH_ERR(
"Context set failed %u\n", i);
875 if (test_options->forward)
876 setup_forwarding(global);
881static int create_queues(test_global_t *global,
odp_queue_param_t *queue_param,
int num_groups,
884 static uint32_t total_queues;
886 for (uint32_t i = 0; i < num; i++) {
888 queue_param->
sched.
group = global->group[(total_queues + i) % num_groups];
893 ODPH_ERR(
"Queue create failed %u\n", i);
899 for (uint32_t i = 0; i < num; i++)
900 global->queue.all[total_queues + i] = queue[i];
906static int create_all_queues(test_global_t *global)
911 uint32_t i, j, first;
912 test_options_t *test_options = &global->test_options;
913 uint32_t event_size = test_options->event_size;
914 uint32_t num_event = test_options->num_event;
915 uint32_t queue_size = test_options->queue_size;
916 uint32_t tot_queue = test_options->tot_queue;
917 uint32_t num_low = test_options->num_low;
918 uint32_t num_high = test_options->num_high;
919 uint32_t num_default = test_options->num_def;
920 int num_group = test_options->num_group;
921 int type = test_options->queue_type;
924 uint32_t ctx_size = test_options->ctx_size;
925 uint64_t init_val = 0;
934 if (tot_queue > global->schedule_config.num_queues) {
935 ODPH_ERR(
"Max queues supported %u\n", global->schedule_config.num_queues);
939 if (global->schedule_config.queue_size &&
940 queue_size > global->schedule_config.queue_size) {
941 ODPH_ERR(
"Max queue size %u\n", global->schedule_config.queue_size);
948 ODPH_ERR(
"Bad queue context\n");
957 queue_param.
size = queue_size;
963 first = test_options->num_dummy;
966 if (create_queues(global, &queue_param, num_group, global->queue.dummy,
967 test_options->num_dummy)) {
968 ODPH_ERR(
"Dummy queue create failed\n");
974 if (create_queues(global, &queue_param, num_group, global->queue.low_prio, num_low)) {
975 ODPH_ERR(
"Lowest priority queue create failed\n");
981 if (create_queues(global, &queue_param, num_group, global->queue.high_prio, num_high)) {
982 ODPH_ERR(
"Highest priority queue create failed\n");
988 if (create_queues(global, &queue_param, num_group, global->queue.def_prio, num_default)) {
989 ODPH_ERR(
"Default priority queue create failed\n");
993 if (set_queue_contexts(global, ctx)) {
994 ODPH_ERR(
"Set queue context failed\n");
1001 for (i = first; i < tot_queue; i++) {
1002 queue = global->queue.all[i];
1004 for (j = 0; j < num_event; j++) {
1013 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1024 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1037 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1044 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1056 init_val = init_data(init_val, data, words);
1059 ODPH_ERR(
"Enqueue failed %u/%u\n", i, j);
1068static int schedule_config(test_global_t *global)
1070 test_options_t *test_options = &global->test_options;
1077 if (test_options->num_group == -1)
1078 global->schedule_config.sched_group.worker_param = group_param;
1079 else if (test_options->num_group == 0)
1080 global->schedule_config.sched_group.all_param = group_param;
1085static int join_group(test_global_t *global,
int grp_index,
int thr)
1092 group = global->group[grp_index];
1095 ODPH_ERR(
"Group %i join failed (thr %i)\n", grp_index, thr);
1102static int join_all_groups(test_global_t *global,
int thr)
1105 test_options_t *test_options = &global->test_options;
1106 int num_group = test_options->num_group;
1111 for (i = 0; i < num_group; i++) {
1112 if (join_group(global, i, thr)) {
1113 ODPH_ERR(
"Group %u join failed (thr %i)\n", i, thr);
1121static void print_queue_fairness(test_global_t *global)
1124 queue_context_t *ctx;
1125 test_options_t *test_options = &global->test_options;
1126 uint32_t first = test_options->num_dummy;
1127 uint32_t num_queue = test_options->num_queue;
1128 uint32_t tot_queue = test_options->tot_queue;
1132 if (!test_options->fairness)
1135 for (i = first; i < tot_queue; i++) {
1140 average = (double)total / (
double)num_queue;
1143 printf(
"RESULTS - events per queue (percent of average):\n");
1144 printf(
"------------------------------------------------\n");
1145 printf(
" 1 2 3 4 5 6 7 8 9 10");
1147 for (i = first; i < tot_queue; i++) {
1160static int destroy_queues(test_global_t *global)
1165 test_options_t *test_options = &global->test_options;
1166 uint32_t tot_queue = test_options->tot_queue;
1169 if (join_all_groups(global, thr))
1177 for (i = 0; i < tot_queue; i++) {
1180 ODPH_ERR(
"Queue destroy failed %u\n", i);
1189static int destroy_groups(test_global_t *global)
1192 test_options_t *test_options = &global->test_options;
1193 int num_group = test_options->num_group;
1198 for (i = 0; i < num_group; i++) {
1202 ODPH_ERR(
"Group destroy failed %u\n", i);
1210static uint64_t rw_uarea(
odp_event_t ev[],
int num, uint32_t rd_words, uint32_t rw_words)
1217 for (i = 0; i < num; i++) {
1220 for (j = 0; j < rd_words; j++)
1223 for (; j < rd_words + rw_words; j++) {
1232static inline uint64_t rw_ctx_data(
void *ctx, uint32_t offset,
1233 uint32_t rd_words, uint32_t rw_words)
1239 data = (uint64_t *)(uintptr_t)((uint8_t *)ctx + offset);
1241 for (i = 0; i < rd_words; i++)
1244 for (; i < rd_words + rw_words; i++) {
1252static uint64_t rw_data(
odp_event_t ev[],
int num, uint32_t rd_words, uint32_t rw_words,
1261 for (
int i = 0; i < num; i++) {
1272 for (j = 0; j < rd_words; j++)
1275 for (; j < rd_words + rw_words; j++) {
1284static uint64_t rw_data_stress(
odp_event_t ev[],
int num, uint32_t rd_words, uint32_t rw_words,
1294 for (
int i = 0; i < num; i++) {
1305 for (j = 0; j < rd_words + rw_words; j++) {
1309 sum += odph_stress_pow2_u32(word);
1311 sum += odph_stress_log2_u32(word);
1313 sum += odph_stress_sqrt_u32(word);
1315 sum += odph_stress_sqrt_f32(word);
1318 data[j] = (word + 1) & DATA_MASK;
1325static int test_sched(
void *arg)
1327 int num, num_enq, ret, thr;
1329 uint64_t c1, c2, cycles, nsec;
1330 uint64_t events, enqueues, waits, events_prev;
1333 thread_arg_t *thread_arg = arg;
1334 test_global_t *global = thread_arg->global;
1335 test_options_t *test_options = &global->test_options;
1336 uint32_t num_sched = test_options->num_sched;
1337 uint32_t max_burst = test_options->max_burst;
1338 int num_group = test_options->num_group;
1339 int forward = test_options->forward;
1340 int fairness = test_options->fairness;
1341 const int touch_data = test_options->touch_data;
1342 const uint32_t stress = test_options->stress;
1343 const uint32_t rd_words = test_options->rd_words;
1344 const uint32_t rw_words = test_options->rw_words;
1345 uint32_t ctx_size = test_options->ctx_size;
1346 uint32_t ctx_rd_words = test_options->ctx_rd_words;
1347 uint32_t ctx_rw_words = test_options->ctx_rw_words;
1348 const uint32_t uarea_size = test_options->uarea_size;
1349 const uint32_t uarea_rd = test_options->uarea_rd;
1350 const uint32_t uarea_rw = test_options->uarea_rw;
1352 const uint32_t num_prefetch = test_options->num_prefetch;
1353 int touch_ctx = ctx_rd_words || ctx_rw_words;
1355 uint32_t ctx_offset = 0;
1356 uint32_t sched_retries = 0;
1357 uint64_t data_sum = 0;
1358 uint64_t ctx_sum = 0;
1359 uint64_t uarea_sum = 0;
1360 uint64_t wait_ns = test_options->wait_ns;
1365 if (forward || fairness)
1366 ctx_offset = ODPH_ROUNDUP_MULTIPLE(
sizeof(queue_context_t), 8);
1368 if (num_group > 0) {
1369 uint32_t num_join = test_options->num_join;
1375 int group_index = thread_arg->first_group;
1377 pos += snprintf(&str[pos], n - pos,
1378 "Thread %i joined groups:", thr);
1380 for (i = 0; i < num_join; i++) {
1381 if (join_group(global, group_index, thr))
1384 pos += snprintf(&str[pos], n - pos,
" %i",
1387 group_index = (group_index + 1) % num_group;
1390 printf(
"%s\n", str);
1393 if (join_all_groups(global, thr))
1398 for (i = 0; i < max_burst; i++)
1427 uarea_sum += rw_uarea(ev, num, uarea_rd, uarea_rw);
1439 ctx_sum += rw_ctx_data(ctx, ctx_offset,
1446 data_sum += rw_data_stress(ev, num, rd_words, rw_words,
1449 data_sum += rw_data(ev, num, rd_words, rw_words, pool_type);
1466 ODPH_ERR(
"Enqueue failed. Round %u\n", rounds);
1481 }
else if (num == 0) {
1483 if (
odp_unlikely(sched_retries > TIME_CHECK_INTERVAL)) {
1489 if (events_prev != events) {
1490 events_prev = events;
1491 last_retry_ts = cur_time;
1494 MAX_SCHED_WAIT_NS) {
1495 ODPH_ERR(
"Scheduling timed out\n");
1504 ODPH_ERR(
"Sched failed. Round %u\n", rounds);
1517 global->stat[thr].rounds = rounds;
1518 global->stat[thr].enqueues = enqueues;
1519 global->stat[thr].events = events;
1520 global->stat[thr].nsec = nsec;
1521 global->stat[thr].cycles = cycles;
1522 global->stat[thr].waits = waits;
1523 global->stat[thr].dummy_sum = data_sum + ctx_sum + uarea_sum;
1524 global->stat[thr].failed = ret;
1534 if (test_options->verbose) {
1556 ODPH_ERR(
"Queue enqueue failed\n");
1565static int start_workers(test_global_t *global,
odp_instance_t instance)
1567 odph_thread_common_param_t thr_common;
1569 test_options_t *test_options = &global->test_options;
1570 int num_group = test_options->num_group;
1571 uint32_t num_join = test_options->num_join;
1572 int num_cpu = test_options->num_cpu;
1573 odph_thread_param_t thr_param[num_cpu];
1579 memset(global->thread_tbl, 0,
sizeof(global->thread_tbl));
1580 odph_thread_common_param_init(&thr_common);
1582 thr_common.instance = instance;
1583 thr_common.cpumask = &global->cpumask;
1585 for (i = 0; i < num_cpu; i++) {
1586 odph_thread_param_init(&thr_param[i]);
1587 thr_param[i].start = test_sched;
1588 thr_param[i].arg = &global->thread_arg[i];
1589 thr_param[i].thr_type = thr_type;
1591 global->thread_arg[i].global = global;
1592 global->thread_arg[i].first_group = 0;
1594 if (num_group > 0 && num_join) {
1598 int first_group = (i * num_join) % num_group;
1600 global->thread_arg[i].first_group = first_group;
1604 ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
1607 if (ret != num_cpu) {
1608 ODPH_ERR(
"Thread create failed %i\n", ret);
1615static double measure_wait_time_cycles(uint64_t wait_ns)
1617 uint64_t i, c1, c2, diff;
1632 for (i = 0; i < rounds; i++)
1637 wait_cycles = (double)diff / rounds;
1639 printf(
"\nMeasured wait cycles: %.3f\n", wait_cycles);
1644static int output_results(test_global_t *global)
1647 double rounds_ave, enqueues_ave, events_ave, events_per_sec, nsec_ave, cycles_ave;
1648 double waits_ave, wait_cycles, wait_cycles_ave;
1649 test_options_t *test_options = &global->test_options;
1650 int num_cpu = test_options->num_cpu;
1651 uint64_t wait_ns = test_options->wait_ns;
1652 uint64_t rounds_sum = 0;
1653 uint64_t enqueues_sum = 0;
1654 uint64_t events_sum = 0;
1655 uint64_t nsec_sum = 0;
1656 uint64_t cycles_sum = 0;
1657 uint64_t waits_sum = 0;
1658 uint32_t tot_rd = test_options->tot_rd_size;
1659 uint32_t tot_rw = test_options->tot_rw_size;
1661 wait_cycles = measure_wait_time_cycles(wait_ns);
1665 if (global->stat[i].failed) {
1669 rounds_sum += global->stat[i].rounds;
1670 enqueues_sum += global->stat[i].enqueues;
1671 events_sum += global->stat[i].events;
1672 nsec_sum += global->stat[i].nsec;
1673 cycles_sum += global->stat[i].cycles;
1674 waits_sum += global->stat[i].waits;
1677 if (rounds_sum == 0 || num_cpu <= 0) {
1678 printf(
"No results.\n");
1682 rounds_ave = rounds_sum / num_cpu;
1683 enqueues_ave = enqueues_sum / num_cpu;
1684 events_ave = events_sum / num_cpu;
1685 nsec_ave = nsec_sum / num_cpu;
1686 cycles_ave = cycles_sum / num_cpu;
1687 waits_ave = waits_sum / num_cpu;
1688 wait_cycles_ave = waits_ave * wait_cycles;
1692 printf(
"RESULTS - per thread (Million events per sec):\n");
1693 printf(
"----------------------------------------------\n");
1694 printf(
" 1 2 3 4 5 6 7 8 9 10");
1697 if (global->stat[i].rounds) {
1698 if ((num % 10) == 0)
1701 if (global->stat[i].failed)
1705 (1000.0 * global->stat[i].events) /
1706 global->stat[i].nsec);
1713 printf(
"RESULTS - average over %i threads:\n", num_cpu);
1714 printf(
"----------------------------------\n");
1715 printf(
" schedule calls: %.3f\n", rounds_ave);
1716 printf(
" enqueue calls: %.3f\n", enqueues_ave);
1717 printf(
" duration: %.3f msec\n", nsec_ave / 1000000);
1718 printf(
" num cycles: %.3f M\n", cycles_ave / 1000000);
1719 printf(
" cycles per round: %.3f\n",
1720 cycles_ave / rounds_ave);
1721 printf(
" cycles per event: %.3f\n",
1722 cycles_ave / events_ave);
1724 printf(
" without wait_ns cycles: %.3f\n",
1725 (cycles_ave - wait_cycles_ave) / events_ave);
1727 printf(
" ave events received: %.3f\n",
1728 events_ave / rounds_ave);
1729 printf(
" rounds per sec: %.3f M\n",
1730 (1000.0 * rounds_ave) / nsec_ave);
1732 events_per_sec = (1000.0 * events_ave) / nsec_ave;
1733 printf(
" events per sec: %.3f M\n", events_per_sec);
1735 printf(
" extra reads per sec: %.3f MB\n", tot_rd * events_per_sec);
1736 printf(
" extra writes per sec: %.3f MB\n", tot_rw * events_per_sec);
1738 printf(
"TOTAL events per sec: %.3f M\n\n",
1739 (1000.0 * events_sum) / nsec_ave);
1741 if (global->common_options.is_export) {
1742 if (test_common_write(
"schedule calls,enqueue calls,duration (msec),"
1743 "num cycles (M),cycles per round,cycles per event,"
1744 "ave events received,rounds per sec (M),"
1745 "events per sec (M),total events per sec (M)\n")) {
1746 ODPH_ERR(
"Export failed\n");
1747 test_common_write_term();
1751 if (test_common_write(
"%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n",
1752 rounds_ave, enqueues_ave, nsec_ave / 1000000,
1753 cycles_ave / 1000000, cycles_ave / rounds_ave,
1754 cycles_ave / events_ave, events_ave / rounds_ave,
1755 (1000.0 * rounds_ave) / nsec_ave,
1756 (1000.0 * events_ave) / nsec_ave,
1757 (1000.0 * events_sum) / nsec_ave)) {
1758 ODPH_ERR(
"Export failed\n");
1759 test_common_write_term();
1763 test_common_write_term();
1769int main(
int argc,
char **argv)
1771 odph_helper_options_t helper_options;
1775 test_global_t *global;
1776 test_common_options_t common_options;
1780 argc = odph_parse_options(argc, argv);
1781 if (odph_options(&helper_options)) {
1782 ODPH_ERR(
"Reading ODP helper options failed\n");
1786 argc = test_common_parse_options(argc, argv);
1787 if (test_common_options(&common_options)) {
1788 ODPH_ERR(
"Reading test options failed\n");
1801 init.
mem_model = helper_options.mem_model;
1805 ODPH_ERR(
"Global init failed\n");
1811 ODPH_ERR(
"Local init failed\n");
1815 shm =
odp_shm_reserve(
"sched_perf_global",
sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
1817 ODPH_ERR(
"SHM reserve failed\n");
1822 if (global == NULL) {
1823 ODPH_ERR(
"SHM alloc failed\n");
1826 test_globals = global;
1828 memset(global, 0,
sizeof(test_global_t));
1833 global->common_options = common_options;
1835 if (setup_sig_handler()) {
1836 ODPH_ERR(
"Signal handler setup failed\n");
1840 if (parse_options(argc, argv, &global->test_options))
1845 if (global->test_options.ctx_size) {
1846 uint64_t size = (uint64_t)global->test_options.ctx_size *
1847 global->test_options.tot_queue;
1850 ODP_CACHE_LINE_SIZE, 0);
1852 ODPH_ERR(
"SHM reserve %" PRIu64
" bytes failed\n", size);
1857 if (schedule_config(global))
1860 if (set_num_cpu(global))
1863 ret = create_pool(global);
1867 if (create_groups(global))
1870 if (create_all_queues(global))
1873 if (global->test_options.verbose)
1876 print_options(&global->test_options);
1879 start_workers(global, instance);
1882 odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
1884 print_queue_fairness(global);
1886 if (destroy_queues(global))
1889 if (destroy_groups(global))
1892 if (output_results(global))
1896 ODPH_ERR(
"Pool destroy failed.\n");
1902 ODPH_ERR(
"Event vector content pool destroy failed.\n");
1910 ODPH_ERR(
"SHM free failed\n");
1915 ODPH_ERR(
"Term local failed\n");
1920 ODPH_ERR(
"Term global failed\n");
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_init_u64(odp_atomic_u64_t *atom, uint64_t val)
Initialize atomic uint64 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
uint32_t odp_atomic_fetch_dec_u32(odp_atomic_u32_t *atom)
Fetch and decrement atomic uint32 variable.
void odp_atomic_add_u64(odp_atomic_u64_t *atom, uint64_t val)
Add to atomic uint64 variable.
uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
Load value of atomic uint64 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.
uint32_t odp_buffer_size(odp_buffer_t buf)
Buffer maximum data size.
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.
odp_buffer_t odp_buffer_from_event(odp_event_t ev)
Get buffer handle from event.
void * odp_buffer_addr(odp_buffer_t buf)
Buffer start address.
#define ODP_BUFFER_INVALID
Invalid buffer.
#define odp_unlikely(x)
Branch unlikely taken.
#define ODP_UNUSED
Intentionally unused variables of functions.
#define odp_likely(x)
Branch likely taken.
#define ODP_CACHE_LINE_ROUNDUP(x)
Round up to cache line size.
uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
CPU cycle count difference.
uint64_t odp_cpu_cycles(void)
Current CPU cycle count.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
odp_event_t odp_event_vector_to_event(odp_event_vector_t evv)
Convert event vector handle to event.
void odp_event_vector_size_set(odp_event_vector_t evv, uint32_t size)
Set the number of events stored in a vector.
void odp_event_vector_free(odp_event_vector_t evv)
Free event vector.
odp_event_vector_t odp_event_vector_from_event(odp_event_t ev)
Get event vector handle from event.
uint32_t odp_event_vector_tbl(odp_event_vector_t evv, odp_event_t **event_tbl)
Get event vector table.
odp_event_vector_t odp_event_vector_alloc(odp_pool_t pool)
Allocate event vector from event vector pool.
#define ODP_EVENT_VECTOR_INVALID
Invalid event vector.
void odp_event_free_multi(const odp_event_t event[], int num)
Free multiple events.
void odp_event_free(odp_event_t event)
Free event.
void * odp_event_user_area(odp_event_t event)
Event user area.
#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_event_t odp_packet_to_event(odp_packet_t pkt)
Convert packet handle to event.
uint32_t odp_packet_seg_len(odp_packet_t pkt)
Packet data length following the data pointer.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
odp_packet_t odp_packet_alloc(odp_pool_t pool, uint32_t len)
Allocate a packet from a packet pool.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
#define ODP_PACKET_INVALID
Invalid packet.
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()
odp_pool_type_t
Pool types.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_BUFFER
Buffer pool.
@ ODP_POOL_PACKET
Packet pool.
@ ODP_POOL_EVENT_VECTOR
Event vector pool.
void * odp_queue_context(odp_queue_t queue)
Get queue context.
int odp_queue_context_set(odp_queue_t queue, void *context, uint32_t len)
Set queue context.
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.
#define ODP_QUEUE_INVALID
Invalid queue.
int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
Enqueue an event to a queue.
void odp_queue_print_all(void)
Print debug info about all queues.
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.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
int odp_schedule_sync_t
Scheduler synchronization method.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[], int num)
Schedule multiple events.
int odp_schedule_group_t
Scheduler thread group.
void odp_schedule_config_init(odp_schedule_config_t *config)
Initialize schedule configuration options.
int odp_schedule_group_join(odp_schedule_group_t group, const odp_thrmask_t *mask)
Join a schedule group.
void odp_schedule_group_param_init(odp_schedule_group_param_t *param)
Initialize schedule group parameters.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
int odp_schedule_min_prio(void)
Minimum scheduling priority level.
#define ODP_SCHED_GROUP_WORKER
Group of all worker threads.
int odp_schedule_group_destroy(odp_schedule_group_t group)
Schedule group destroy.
#define ODP_SCHED_GROUP_INVALID
Invalid scheduler group.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_default_prio(void)
Default scheduling priority level.
void odp_schedule_pause(void)
Pause scheduling.
int odp_schedule_max_prio(void)
Maximum scheduling priority level.
void odp_schedule_prefetch(int num)
Prefetch events for next schedule call.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
int odp_schedule_capability(odp_schedule_capability_t *capa)
Query scheduler capabilities.
odp_schedule_group_t odp_schedule_group_create_2(const char *name, const odp_thrmask_t *mask, const odp_schedule_group_param_t *param)
Schedule group create with parameters.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
void odp_schedule_print(void)
Print debug info about scheduler.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
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.
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.
void odp_sys_info_print(void)
Print system info.
enum odp_thread_type_e odp_thread_type_t
Thread type.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
void odp_thrmask_set(odp_thrmask_t *mask, int thr)
Add thread to mask.
int odp_thread_id(void)
Get thread identifier.
void odp_thrmask_zero(odp_thrmask_t *mask)
Clear entire thread mask.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
Cache stashing configuration.
union odp_cache_stash_config_t::@161 regions
Region specific configuration toggle.
odp_cache_stash_region_t event_user_area
Cache stashing for event user area.
odp_cache_stash_region_t event_data
Cache stashing for event data.
uint32_t all
All bits of the bit field structure.
odp_cache_stash_region_t event_metadata
Cache stashing for event metadata.
odp_cache_stash_region_t queue_context
Cache stashing for queue context region.
Region specific cache stashing configuration.
uint32_t len
Length in bytes to cache.
struct odp_cache_stash_region_t::@160 l3
L3 cache stashing.
struct odp_cache_stash_region_t::@159 l2
L2 cache stashing.
uint32_t offset
Byte offset into a region to start caching from.
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 max_uarea_size
Maximum user area size in bytes.
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
struct odp_pool_capability_t::@137 event_vector
Event vector pool capabilities.
uint32_t max_size
Maximum buffer data size in bytes.
uint32_t max_pools
Maximum number of pools of any type (odp_pool_type_t)
uint32_t max_seg_len
Maximum packet segment data length in bytes.
uint32_t uarea_size
Minimum user area size in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
struct odp_pool_param_t::@142 event_vector
Parameters for event vector pools.
uint32_t align
Minimum buffer alignment in bytes.
uint32_t size
Minimum buffer size in bytes.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t max_size
Maximum number of handles (such as odp_packet_t) in a vector.
struct odp_pool_param_t::@138 buf
Parameters for buffer pools.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
uint32_t max_groups
Maximum number of scheduling groups.
Schedule group parameters.
odp_cache_stash_config_t common
Common group specific cache stashing hints.
struct odp_schedule_group_param_t::@165 cache_stash_hints
Group specific cache stashing hints.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
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 compress
Compression APIs, e.g., odp_comp_xxx()