27#include <odp/helper/odph_api.h>
29#include <export_results.h>
31#define MAX_QUEUES (256 * 1024)
33#define MAX_VECTOR_SIZE 16U
36#define DATA_MASK 0xffff
39#define MAX_SCHED_WAIT_NS (10 * ODP_TIME_SEC_IN_NS)
42#define TIME_CHECK_INTERVAL (1024 * 1024)
44typedef struct test_options_t {
55 uint32_t num_prefetch;
65 uint32_t forward_group_size;
73 uint32_t ctx_rd_words;
74 uint32_t ctx_rw_words;
85typedef struct test_stat_t {
97typedef struct thread_arg_t {
103typedef struct test_global_t {
104 test_options_t test_options;
124 test_common_options_t common_options;
133static test_global_t *test_globals;
140static int setup_sig_handler(
void)
142 struct sigaction action = { .sa_handler = sig_handler };
144 if (sigemptyset(&action.sa_mask) || sigaction(SIGINT, &action, NULL))
154 region->
l2.
len = len;
157 region->
l3.
len = len;
162static int parse_cache_stash_config(
char *optarg, test_options_t *test_options)
164 uint32_t region, level, offset, len;
167 if (sscanf(optarg,
"%u,%u,%u,%u", ®ion, &level, &offset, &len) != 4) {
168 ODPH_ERR(
"Invalid number of arguments for cache stashing\n");
173 ODPH_ERR(
"Invalid region for cache stashing: %u\n", region);
178 ODPH_ERR(
"Invalid cache level for cache stashing: %u\n", level);
183 ODPH_ERR(
"Invalid len for cache stashing: %u\n", len);
187 stash_config->
regions.
all |= (1U << (region * 2 + level));
191 set_cache_stash(&stash_config->
event_metadata, level, len, offset);
194 set_cache_stash(&stash_config->
event_data, level, len, offset);
200 set_cache_stash(&stash_config->
queue_context, level, len, offset);
209static void print_usage(
void)
212 "Scheduler performance test\n"
214 "Usage: odp_sched_perf [options]\n"
216 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1.\n"
217 " -q, --num_def Number of default priority queues. Default: 1.\n"
218 " -L, --num_low Number of lowest priority queues. Default: 0.\n"
219 " -H, --num_high Number of highest priority queues. Default: 0.\n"
220 " -d, --num_dummy Number of empty queues. Default: 0.\n"
221 " -e, --num_event Number of events per queue. Default: 100.\n"
222 " -s, --num_sched Number of events to schedule per thread. If zero, the application runs\n"
223 " until SIGINT is received. Default: 100 000.\n"
224 " -g, --num_group Number of schedule groups. Round robins threads and queues into groups.\n"
225 " -1: SCHED_GROUP_WORKER\n"
226 " 0: SCHED_GROUP_ALL (default)\n"
227 " -j, --num_join Number of groups a thread joins. Threads are divide evenly into groups,\n"
228 " if num_cpu is multiple of num_group and num_group is multiple of num_join.\n"
229 " 0: join all groups (default)\n"
230 " -b, --burst Maximum number of events per operation. Default: 100.\n"
231 " -C, --cache_stash Enable common group level cache stashing. Format: region,level,offset,len\n"
232 " region: 0: Event metadata, 1: Event data, 2: Event user area, 3: Queue context\n"
233 " level: 0: L2, 1: L3\n"
234 " offset/len: in bytes\n"
235 " E.g.: 1,0,16,32 enables 32-byte L2 stash on event data with a 16-byte offset\n"
236 " For stashing multiple regions, use -C multiple times\n"
237 " -t, --type Queue type. 0: parallel, 1: atomic, 2: ordered. Default: 0.\n"
238 " -T, --thr_type Thread type. 0: worker thread, 1: control thread. Default: 0\n"
239 " -f, --forward 0: Keep event in the original queue (default)\n"
240 " 1: Forward event between all queues\n"
241 " N: Forward events between queues of N identical queue sets. In this mode 'num_def',\n"
242 " 'num_low', and 'num_high' options are per set, so the total number of active\n"
243 " queues is N * (num_def + num_low + num_high).\n"
244 " -F, --fairness 0: Don't count events per queue, 1: Count and report events relative to average. Default: 0.\n"
245 " -w, --wait_ns Number of nsec to wait before enqueueing events. Default: 0.\n"
246 " -S, --stress CPU stress function(s) to be called for each event data word (requires -n or -m).\n"
247 " Data is processed as uint32_t words. Multiple flags may be selected.\n"
248 " 0: No extra data processing (default)\n"
249 " 0x1: Calculate square of each uint32_t\n"
250 " 0x2: Calculate log2 of each uint32_t\n"
251 " 0x4: Calculate square root of each uint32_t\n"
252 " 0x8: Calculate square root of each uint32_t in floating point\n"
253 " -k, --ctx_rd_words Number of queue context words (uint64_t) to read on every event. Default: 0.\n"
254 " -l, --ctx_rw_words Number of queue context words (uint64_t) to modify on every event. Default: 0.\n"
255 " -n, --rd_words Number of event data words (uint64_t) to read before enqueueing it. Default: 0.\n"
256 " -m, --rw_words Number of event data words (uint64_t) to modify before enqueueing it. Default: 0.\n"
257 " -u, --uarea_rd Number of user area words (uint64_t) to read on every event. Default: 0.\n"
258 " -U, --uarea_rw Number of user area words (uint64_t) to modify on every event. Default: 0.\n"
259 " -p, --pool_type Pool type. 0: buffer, 1: packet, 2: event vector. Default: 0.\n"
260 " -P, --prefetch Number of events to be prefetched. Default: 0.\n"
261 " -v, --verbose Verbose output.\n"
262 " -h, --help This help\n"
266static int parse_options(
int argc,
char *argv[], test_options_t *test_options)
268 int opt, num_group, num_join;
270 uint32_t ctx_size = 0;
273 static const struct option longopts[] = {
274 {
"num_cpu", required_argument, NULL,
'c'},
275 {
"num_def", required_argument, NULL,
'q'},
276 {
"num_low", required_argument, NULL,
'L'},
277 {
"num_high", required_argument, NULL,
'H'},
278 {
"num_dummy", required_argument, NULL,
'd'},
279 {
"num_event", required_argument, NULL,
'e'},
280 {
"num_sched", required_argument, NULL,
's'},
281 {
"num_group", required_argument, NULL,
'g'},
282 {
"num_join", required_argument, NULL,
'j'},
283 {
"burst", required_argument, NULL,
'b'},
284 {
"cache_stash", required_argument, NULL,
'C'},
285 {
"type", required_argument, NULL,
't'},
286 {
"thr_type", required_argument, NULL,
'T'},
287 {
"forward", required_argument, NULL,
'f'},
288 {
"fairness", required_argument, NULL,
'F'},
289 {
"wait_ns", required_argument, NULL,
'w'},
290 {
"stress", required_argument, NULL,
'S'},
291 {
"ctx_rd_words", required_argument, NULL,
'k'},
292 {
"ctx_rw_words", required_argument, NULL,
'l'},
293 {
"rd_words", required_argument, NULL,
'n'},
294 {
"rw_words", required_argument, NULL,
'm'},
295 {
"uarea_rd", required_argument, NULL,
'u'},
296 {
"uarea_rw", required_argument, NULL,
'U'},
297 {
"pool_type", required_argument, NULL,
'p'},
298 {
"prefetch", required_argument, NULL,
'P'},
299 {
"verbose", no_argument, NULL,
'v'},
300 {
"help", no_argument, NULL,
'h'},
304 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";
306 test_options->num_cpu = 1;
307 test_options->num_def = 1;
308 test_options->num_event = 100;
309 test_options->num_sched = 100000;
310 test_options->max_burst = 100;
313 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
320 test_options->num_cpu = atoi(optarg);
323 test_options->num_def = atoi(optarg);
326 test_options->num_low = atoi(optarg);
329 test_options->num_high = atoi(optarg);
332 test_options->num_dummy = atoi(optarg);
335 test_options->num_event = atoi(optarg);
338 test_options->num_sched = atoi(optarg);
341 test_options->num_group = atoi(optarg);
344 test_options->num_join = atoi(optarg);
347 if (parse_cache_stash_config(optarg, test_options))
351 test_options->max_burst = atoi(optarg);
354 test_options->queue_type = atoi(optarg);
357 test_options->thr_type = atoi(optarg);
360 test_options->forward = atoi(optarg);
363 test_options->fairness = atoi(optarg);
366 test_options->stress = strtoul(optarg, NULL, 0);
369 test_options->ctx_rd_words = atoi(optarg);
372 test_options->ctx_rw_words = atoi(optarg);
375 test_options->rd_words = atoi(optarg);
378 test_options->rw_words = atoi(optarg);
381 test_options->uarea_rd = atoi(optarg);
384 test_options->uarea_rw = atoi(optarg);
387 pool_type = atoi(optarg);
390 test_options->num_prefetch = atoi(optarg);
393 test_options->wait_ns = atoll(optarg);
396 test_options->verbose = 1;
417 ODPH_ERR(
"Invalid pool type: %d.\n", pool_type);
422 test_options->touch_data = test_options->rd_words ||
423 test_options->rw_words;
425 if (test_options->stress && test_options->touch_data == 0) {
426 ODPH_ERR(
"Use -n or/and -m to select event data size with a stress function\n");
430 test_options->forward_group_size = test_options->num_def + test_options->num_low +
431 test_options->num_high;
433 if (test_options->forward > 1) {
434 test_options->num_def *= test_options->forward;
435 test_options->num_low *= test_options->forward;
436 test_options->num_high *= test_options->forward;
437 }
else if (test_options->forward < 0) {
438 ODPH_ERR(
"Invalid forward mode %i.\n", test_options->forward);
442 test_options->num_queue = test_options->num_def + test_options->num_low +
443 test_options->num_high;
444 if ((test_options->num_queue + test_options->num_dummy) > MAX_QUEUES) {
445 ODPH_ERR(
"Too many queues. Max supported %i.\n", MAX_QUEUES);
449 num_group = test_options->num_group;
450 num_join = test_options->num_join;
451 if (num_group > MAX_GROUPS) {
452 ODPH_ERR(
"Too many groups. Max supported %i.\n", MAX_GROUPS);
456 if (num_group > 0 && num_join > num_group) {
457 ODPH_ERR(
"num_join (%i) larger than num_group (%i).\n", num_join, num_group);
461 if (num_join && num_group > (
int)(test_options->num_cpu * num_join)) {
462 printf(
"WARNING: Too many groups (%i). Some groups (%i) are not served.\n\n",
463 num_group, num_group - (test_options->num_cpu * num_join));
465 if (test_options->forward) {
466 ODPH_ERR(
"Cannot forward when some queues are not served.\n");
471 test_options->tot_queue = test_options->num_queue +
472 test_options->num_dummy;
473 test_options->tot_event = test_options->num_queue *
474 test_options->num_event;
476 test_options->queue_size = test_options->num_event;
478 if (test_options->forward) {
480 test_options->queue_size *= test_options->forward_group_size;
483 if (test_options->forward || test_options->fairness)
484 ctx_size =
sizeof(queue_context_t);
486 if (test_options->ctx_rd_words || test_options->ctx_rw_words) {
489 ctx_size = ODPH_ROUNDUP_MULTIPLE(ctx_size, 8);
490 ctx_size += 8 * test_options->ctx_rd_words;
491 ctx_size += 8 * test_options->ctx_rw_words;
496 if (test_options->fairness || test_options->ctx_rw_words)
499 test_options->ctx_size = ctx_size;
500 test_options->uarea_size = 8 * (test_options->uarea_rd + test_options->uarea_rw);
501 test_options->tot_rd_size = 8 * (test_options->ctx_rd_words + test_options->uarea_rd +
502 test_options->rd_words);
503 test_options->tot_rw_size = 8 * (test_options->ctx_rw_words + test_options->uarea_rw +
504 test_options->rw_words);
509static int set_num_cpu(test_global_t *global)
512 test_options_t *test_options = &global->test_options;
513 int num_cpu = test_options->num_cpu;
523 if (num_cpu && ret != num_cpu) {
524 ODPH_ERR(
"Too many workers. Max supported %i\n.", ret);
531 test_options->num_cpu = num_cpu;
539static uint64_t init_data(uint64_t init, uint64_t *data, uint32_t words)
544 for (i = 0; i < words; i++) {
546 val = (val + 1) & DATA_MASK;
552static void print_options(test_options_t *options)
554 printf(
"\nScheduler performance test\n");
555 printf(
" num sched %u\n", options->num_sched);
556 printf(
" num cpu %u\n", options->num_cpu);
557 printf(
" num default prio queues %u\n", options->num_def);
558 printf(
" num lowest prio queues %u\n", options->num_low);
559 printf(
" num highest prio queues %u\n", options->num_high);
560 printf(
" num empty queues %u\n", options->num_dummy);
561 printf(
" total queues %u\n", options->tot_queue);
562 printf(
" num groups %i", options->num_group);
564 if (options->num_group == -1)
565 printf(
" (ODP_SCHED_GROUP_WORKER)\n");
566 else if (options->num_group == 0)
567 printf(
" (ODP_SCHED_GROUP_ALL)\n");
571 printf(
" num join %u\n", options->num_join);
572 printf(
" forward events %i\n", options->forward);
573 printf(
" wait %" PRIu64
" nsec\n", options->wait_ns);
574 printf(
" events per queue %u\n", options->num_event);
575 printf(
" queue size %u\n", options->queue_size);
576 printf(
" max burst size %u\n", options->max_burst);
577 printf(
" total events %u\n", options->tot_event);
578 printf(
" stress 0x%x\n", options->stress);
580 printf(
" event size %u bytes", options->event_size);
581 if (options->touch_data)
582 printf(
" (rd: %u, rw: %u)", 8 * options->rd_words, 8 * options->rw_words);
585 printf(
" queue context size %u bytes", options->ctx_size);
586 if (options->ctx_rd_words || options->ctx_rw_words) {
587 printf(
" (rd: %u, rw: %u)",
588 8 * options->ctx_rd_words,
589 8 * options->ctx_rw_words);
593 printf(
" user area size %u bytes", options->uarea_size);
594 if (options->uarea_size)
595 printf(
" (rd: %u, rw: %u)", 8 * options->uarea_rd, 8 * options->uarea_rw);
598 printf(
" pool type %s\n", options->pool_type ==
ODP_POOL_BUFFER ?
"buffer"
602 printf(
" queue type %s\n", options->queue_type == 0 ?
"parallel" :
603 options->queue_type == 1 ?
"atomic" :
605 printf(
" thread type %s\n\n", options->thr_type == 0 ?
"worker" :
608 printf(
"Extra rd/rw ops per event (queue context + user area + event data)\n");
609 printf(
" read %u bytes\n", options->tot_rd_size);
610 printf(
" write %u bytes\n\n", options->tot_rw_size);
614 uint32_t tot_event, uint32_t uarea_size,
619 uint32_t max_num, max_uarea;
620 uint32_t max_vector_size = 0;
621 uint32_t max_size = 0;
622 uint32_t max_vector_pools = 0;
625 ODPH_ERR(
"Pool capa failed\n");
644 if (max_num && tot_event > max_num) {
645 ODPH_ERR(
"Max events supported %u\n", max_num);
649 if (max_size && event_size > max_size) {
650 ODPH_ERR(
"Max supported event size %u\n", max_size);
654 if (uarea_size > max_uarea) {
655 ODPH_ERR(
"Max supported user area size %u\n", max_uarea);
660 ODPH_ERR(
"Event vector pools not supported\n");
665 pool_param.
type = pool_type;
668 pool_param.
buf.
num = tot_event;
669 pool_param.
buf.
size = event_size;
673 pool_param.
pkt.
num = tot_event;
674 pool_param.
pkt.
len = event_size;
686 ODPH_ERR(
"Pool creation failed for type %d (%s)\n", pool_param.
type, name);
693static int create_pool(test_global_t *global)
695 test_options_t *test_options = &global->test_options;
696 uint32_t tot_event = test_options->tot_event;
697 uint32_t event_size = 16;
698 uint32_t uarea_size = test_options->uarea_size;
701 if (test_options->touch_data) {
702 event_size = test_options->rd_words + test_options->rw_words;
703 event_size = 8 * event_size;
705 test_options->event_size = event_size;
707 ret = create_pool_helper(
"sched perf", &global->pool, test_options->pool_type, tot_event,
708 uarea_size, event_size);
713 ret = create_pool_helper(
"sched perf event vector content", &global->evv_pool,
722static int create_groups(test_global_t *global)
727 test_options_t *test_options = &global->test_options;
728 uint32_t num_group = test_options->num_group;
731 if (test_options->num_group <= 0)
735 ODPH_ERR(
"Schedule capability failed\n");
740 ODPH_ERR(
"Too many sched groups (max_groups capa %u)\n", sched_capa.
max_groups);
748 for (i = 0; i < num_group; i++) {
754 ODPH_ERR(
"Group create failed %u\n", i);
758 global->group[i] = group;
764static void setup_forwarding(test_global_t *global)
766 const test_options_t *test_options = &global->test_options;
767 const uint32_t forward_group_size = test_options->forward_group_size;
768 const uint32_t num_groups = test_options->forward;
769 uint32_t num_low = test_options->num_low;
770 uint32_t num_high = test_options->num_high;
771 uint32_t num_def = test_options->num_def;
772 const uint32_t num_high_per_group = num_high / num_groups;
773 const uint32_t num_low_per_group = num_low / num_groups;
774 const uint32_t num_def_per_group = num_def / num_groups;
777 for (uint32_t i = 0; i < num_groups; i++) {
778 uint32_t num_group_low = num_low_per_group;
779 uint32_t num_group_high = num_high_per_group;
780 uint32_t num_group_def = num_def_per_group;
783 first_queue = global->queue.low_prio[--num_low];
785 }
else if (num_group_high) {
786 first_queue = global->queue.high_prio[--num_high];
789 first_queue = global->queue.def_prio[--num_def];
792 cur_queue = first_queue;
794 for (uint32_t j = 0; j < forward_group_size; j++) {
797 const uint32_t next_id = j + 1;
799 if (next_id == forward_group_size) {
801 *next_queue = first_queue;
806 switch (next_id % 3) {
809 *next_queue = global->queue.low_prio[--num_low];
811 }
else if (num_group_high) {
812 *next_queue = global->queue.high_prio[--num_high];
815 *next_queue = global->queue.def_prio[--num_def];
820 if (num_group_high) {
821 *next_queue = global->queue.high_prio[--num_high];
823 }
else if (num_group_low) {
824 *next_queue = global->queue.low_prio[--num_low];
827 *next_queue = global->queue.def_prio[--num_def];
833 *next_queue = global->queue.def_prio[--num_def];
835 }
else if (num_group_high) {
836 *next_queue = global->queue.high_prio[--num_high];
839 *next_queue = global->queue.low_prio[--num_low];
844 cur_queue = *next_queue;
849static int set_queue_contexts(test_global_t *global, uint8_t *ctx)
851 test_options_t *test_options = &global->test_options;
852 uint32_t tot_queue = test_options->tot_queue;
853 uint32_t ctx_size = test_options->ctx_size;
854 uint32_t first = test_options->num_dummy;
859 for (uint32_t i = first; i < tot_queue; i++) {
860 if (test_options->fairness) {
863 queue_context_t *qc = (queue_context_t *)(uintptr_t)ctx;
869 ODPH_ERR(
"Context set failed %u\n", i);
876 if (test_options->forward)
877 setup_forwarding(global);
882static int create_queues(test_global_t *global,
odp_queue_param_t *queue_param,
int num_groups,
885 static uint32_t total_queues;
887 for (uint32_t i = 0; i < num; i++) {
889 queue_param->
sched.
group = global->group[(total_queues + i) % num_groups];
894 ODPH_ERR(
"Queue create failed %u\n", i);
900 for (uint32_t i = 0; i < num; i++)
901 global->queue.all[total_queues + i] = queue[i];
907static int create_all_queues(test_global_t *global)
912 uint32_t i, j, first;
913 test_options_t *test_options = &global->test_options;
914 uint32_t event_size = test_options->event_size;
915 uint32_t num_event = test_options->num_event;
916 uint32_t queue_size = test_options->queue_size;
917 uint32_t tot_queue = test_options->tot_queue;
918 uint32_t num_low = test_options->num_low;
919 uint32_t num_high = test_options->num_high;
920 uint32_t num_default = test_options->num_def;
921 int num_group = test_options->num_group;
922 int type = test_options->queue_type;
925 uint32_t ctx_size = test_options->ctx_size;
926 uint64_t init_val = 0;
935 if (tot_queue > global->schedule_config.num_queues) {
936 ODPH_ERR(
"Max queues supported %u\n", global->schedule_config.num_queues);
940 if (global->schedule_config.queue_size &&
941 queue_size > global->schedule_config.queue_size) {
942 ODPH_ERR(
"Max queue size %u\n", global->schedule_config.queue_size);
949 ODPH_ERR(
"Bad queue context\n");
958 queue_param.
size = queue_size;
964 first = test_options->num_dummy;
967 if (create_queues(global, &queue_param, num_group, global->queue.dummy,
968 test_options->num_dummy)) {
969 ODPH_ERR(
"Dummy queue create failed\n");
975 if (create_queues(global, &queue_param, num_group, global->queue.low_prio, num_low)) {
976 ODPH_ERR(
"Lowest priority queue create failed\n");
982 if (create_queues(global, &queue_param, num_group, global->queue.high_prio, num_high)) {
983 ODPH_ERR(
"Highest priority queue create failed\n");
989 if (create_queues(global, &queue_param, num_group, global->queue.def_prio, num_default)) {
990 ODPH_ERR(
"Default priority queue create failed\n");
994 if (set_queue_contexts(global, ctx)) {
995 ODPH_ERR(
"Set queue context failed\n");
1002 for (i = first; i < tot_queue; i++) {
1003 queue = global->queue.all[i];
1005 for (j = 0; j < num_event; j++) {
1014 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1025 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1038 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1045 ODPH_ERR(
"Alloc failed %u/%u\n", i, j);
1057 init_val = init_data(init_val, data, words);
1060 ODPH_ERR(
"Enqueue failed %u/%u\n", i, j);
1069static int schedule_config(test_global_t *global)
1071 test_options_t *test_options = &global->test_options;
1078 if (test_options->num_group == -1)
1079 global->schedule_config.sched_group.worker_param = group_param;
1080 else if (test_options->num_group == 0)
1081 global->schedule_config.sched_group.all_param = group_param;
1086static int join_group(test_global_t *global,
int grp_index,
int thr)
1093 group = global->group[grp_index];
1096 ODPH_ERR(
"Group %i join failed (thr %i)\n", grp_index, thr);
1103static int join_all_groups(test_global_t *global,
int thr)
1106 test_options_t *test_options = &global->test_options;
1107 int num_group = test_options->num_group;
1112 for (i = 0; i < num_group; i++) {
1113 if (join_group(global, i, thr)) {
1114 ODPH_ERR(
"Group %u join failed (thr %i)\n", i, thr);
1122static void print_queue_fairness(test_global_t *global)
1125 queue_context_t *ctx;
1126 test_options_t *test_options = &global->test_options;
1127 uint32_t first = test_options->num_dummy;
1128 uint32_t num_queue = test_options->num_queue;
1129 uint32_t tot_queue = test_options->tot_queue;
1133 if (!test_options->fairness)
1136 for (i = first; i < tot_queue; i++) {
1141 average = (double)total / (
double)num_queue;
1144 printf(
"RESULTS - events per queue (percent of average):\n");
1145 printf(
"------------------------------------------------\n");
1146 printf(
" 1 2 3 4 5 6 7 8 9 10");
1148 for (i = first; i < tot_queue; i++) {
1161static int destroy_queues(test_global_t *global)
1166 test_options_t *test_options = &global->test_options;
1167 uint32_t tot_queue = test_options->tot_queue;
1170 if (join_all_groups(global, thr))
1178 for (i = 0; i < tot_queue; i++) {
1181 ODPH_ERR(
"Queue destroy failed %u\n", i);
1190static int destroy_groups(test_global_t *global)
1193 test_options_t *test_options = &global->test_options;
1194 int num_group = test_options->num_group;
1199 for (i = 0; i < num_group; i++) {
1203 ODPH_ERR(
"Group destroy failed %u\n", i);
1211static uint64_t rw_uarea(
odp_event_t ev[],
int num, uint32_t rd_words, uint32_t rw_words)
1218 for (i = 0; i < num; i++) {
1221 for (j = 0; j < rd_words; j++)
1224 for (; j < rd_words + rw_words; j++) {
1233static inline uint64_t rw_ctx_data(
void *ctx, uint32_t offset,
1234 uint32_t rd_words, uint32_t rw_words)
1240 data = (uint64_t *)(uintptr_t)((uint8_t *)ctx + offset);
1242 for (i = 0; i < rd_words; i++)
1245 for (; i < rd_words + rw_words; i++) {
1253static uint64_t rw_data(
odp_event_t ev[],
int num, uint32_t rd_words, uint32_t rw_words,
1262 for (
int i = 0; i < num; i++) {
1273 for (j = 0; j < rd_words; j++)
1276 for (; j < rd_words + rw_words; j++) {
1285static uint64_t rw_data_stress(
odp_event_t ev[],
int num, uint32_t rd_words, uint32_t rw_words,
1295 for (
int i = 0; i < num; i++) {
1306 for (j = 0; j < rd_words + rw_words; j++) {
1310 sum += odph_stress_pow2_u32(word);
1312 sum += odph_stress_log2_u32(word);
1314 sum += odph_stress_sqrt_u32(word);
1316 sum += odph_stress_sqrt_f32(word);
1319 data[j] = (word + 1) & DATA_MASK;
1326static int test_sched(
void *arg)
1328 int num, num_enq, ret, thr;
1330 uint64_t c1, c2, cycles, nsec;
1331 uint64_t events, enqueues, waits, events_prev;
1334 thread_arg_t *thread_arg = arg;
1335 test_global_t *global = thread_arg->global;
1336 test_options_t *test_options = &global->test_options;
1337 uint32_t num_sched = test_options->num_sched;
1338 uint32_t max_burst = test_options->max_burst;
1339 int num_group = test_options->num_group;
1340 int forward = test_options->forward;
1341 int fairness = test_options->fairness;
1342 const int touch_data = test_options->touch_data;
1343 const uint32_t stress = test_options->stress;
1344 const uint32_t rd_words = test_options->rd_words;
1345 const uint32_t rw_words = test_options->rw_words;
1346 uint32_t ctx_size = test_options->ctx_size;
1347 uint32_t ctx_rd_words = test_options->ctx_rd_words;
1348 uint32_t ctx_rw_words = test_options->ctx_rw_words;
1349 const uint32_t uarea_size = test_options->uarea_size;
1350 const uint32_t uarea_rd = test_options->uarea_rd;
1351 const uint32_t uarea_rw = test_options->uarea_rw;
1353 const uint32_t num_prefetch = test_options->num_prefetch;
1354 int touch_ctx = ctx_rd_words || ctx_rw_words;
1356 uint32_t ctx_offset = 0;
1357 uint32_t sched_retries = 0;
1358 uint64_t data_sum = 0;
1359 uint64_t ctx_sum = 0;
1360 uint64_t uarea_sum = 0;
1361 uint64_t wait_ns = test_options->wait_ns;
1366 if (forward || fairness)
1367 ctx_offset = ODPH_ROUNDUP_MULTIPLE(
sizeof(queue_context_t), 8);
1369 if (num_group > 0) {
1370 uint32_t num_join = test_options->num_join;
1376 int group_index = thread_arg->first_group;
1378 pos += snprintf(&str[pos], n - pos,
1379 "Thread %i joined groups:", thr);
1381 for (i = 0; i < num_join; i++) {
1382 if (join_group(global, group_index, thr))
1385 pos += snprintf(&str[pos], n - pos,
" %i",
1388 group_index = (group_index + 1) % num_group;
1391 printf(
"%s\n", str);
1394 if (join_all_groups(global, thr))
1399 for (i = 0; i < max_burst; i++)
1428 uarea_sum += rw_uarea(ev, num, uarea_rd, uarea_rw);
1440 ctx_sum += rw_ctx_data(ctx, ctx_offset,
1447 data_sum += rw_data_stress(ev, num, rd_words, rw_words,
1450 data_sum += rw_data(ev, num, rd_words, rw_words, pool_type);
1467 ODPH_ERR(
"Enqueue failed. Round %u\n", rounds);
1482 }
else if (num == 0) {
1484 if (
odp_unlikely(sched_retries > TIME_CHECK_INTERVAL)) {
1490 if (events_prev != events) {
1491 events_prev = events;
1492 last_retry_ts = cur_time;
1495 MAX_SCHED_WAIT_NS) {
1496 ODPH_ERR(
"Scheduling timed out\n");
1505 ODPH_ERR(
"Sched failed. Round %u\n", rounds);
1518 global->stat[thr].rounds = rounds;
1519 global->stat[thr].enqueues = enqueues;
1520 global->stat[thr].events = events;
1521 global->stat[thr].nsec = nsec;
1522 global->stat[thr].cycles = cycles;
1523 global->stat[thr].waits = waits;
1524 global->stat[thr].dummy_sum = data_sum + ctx_sum + uarea_sum;
1525 global->stat[thr].failed = ret;
1535 if (test_options->verbose) {
1557 ODPH_ERR(
"Queue enqueue failed\n");
1566static int start_workers(test_global_t *global,
odp_instance_t instance)
1568 odph_thread_common_param_t thr_common;
1570 test_options_t *test_options = &global->test_options;
1571 int num_group = test_options->num_group;
1572 uint32_t num_join = test_options->num_join;
1573 int num_cpu = test_options->num_cpu;
1574 odph_thread_param_t thr_param[num_cpu];
1580 memset(global->thread_tbl, 0,
sizeof(global->thread_tbl));
1581 odph_thread_common_param_init(&thr_common);
1583 thr_common.instance = instance;
1584 thr_common.cpumask = &global->cpumask;
1586 for (i = 0; i < num_cpu; i++) {
1587 odph_thread_param_init(&thr_param[i]);
1588 thr_param[i].start = test_sched;
1589 thr_param[i].arg = &global->thread_arg[i];
1590 thr_param[i].thr_type = thr_type;
1592 global->thread_arg[i].global = global;
1593 global->thread_arg[i].first_group = 0;
1595 if (num_group > 0 && num_join) {
1599 int first_group = (i * num_join) % num_group;
1601 global->thread_arg[i].first_group = first_group;
1605 ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
1608 if (ret != num_cpu) {
1609 ODPH_ERR(
"Thread create failed %i\n", ret);
1616static double measure_wait_time_cycles(uint64_t wait_ns)
1618 uint64_t i, c1, c2, diff;
1633 for (i = 0; i < rounds; i++)
1638 wait_cycles = (double)diff / rounds;
1640 printf(
"\nMeasured wait cycles: %.3f\n", wait_cycles);
1645static int output_results(test_global_t *global)
1648 double rounds_ave, enqueues_ave, events_ave, events_per_sec, nsec_ave, cycles_ave;
1649 double waits_ave, wait_cycles, wait_cycles_ave;
1650 test_options_t *test_options = &global->test_options;
1651 int num_cpu = test_options->num_cpu;
1652 uint64_t wait_ns = test_options->wait_ns;
1653 uint64_t rounds_sum = 0;
1654 uint64_t enqueues_sum = 0;
1655 uint64_t events_sum = 0;
1656 uint64_t nsec_sum = 0;
1657 uint64_t cycles_sum = 0;
1658 uint64_t waits_sum = 0;
1659 uint32_t tot_rd = test_options->tot_rd_size;
1660 uint32_t tot_rw = test_options->tot_rw_size;
1662 wait_cycles = measure_wait_time_cycles(wait_ns);
1666 if (global->stat[i].failed) {
1670 rounds_sum += global->stat[i].rounds;
1671 enqueues_sum += global->stat[i].enqueues;
1672 events_sum += global->stat[i].events;
1673 nsec_sum += global->stat[i].nsec;
1674 cycles_sum += global->stat[i].cycles;
1675 waits_sum += global->stat[i].waits;
1678 if (rounds_sum == 0 || num_cpu <= 0) {
1679 printf(
"No results.\n");
1683 rounds_ave = rounds_sum / num_cpu;
1684 enqueues_ave = enqueues_sum / num_cpu;
1685 events_ave = events_sum / num_cpu;
1686 nsec_ave = nsec_sum / num_cpu;
1687 cycles_ave = cycles_sum / num_cpu;
1688 waits_ave = waits_sum / num_cpu;
1689 wait_cycles_ave = waits_ave * wait_cycles;
1693 printf(
"RESULTS - per thread (Million events per sec):\n");
1694 printf(
"----------------------------------------------\n");
1695 printf(
" 1 2 3 4 5 6 7 8 9 10");
1698 if (global->stat[i].rounds) {
1699 if ((num % 10) == 0)
1702 if (global->stat[i].failed)
1706 (1000.0 * global->stat[i].events) /
1707 global->stat[i].nsec);
1714 printf(
"RESULTS - average over %i threads:\n", num_cpu);
1715 printf(
"----------------------------------\n");
1716 printf(
" schedule calls: %.3f\n", rounds_ave);
1717 printf(
" enqueue calls: %.3f\n", enqueues_ave);
1718 printf(
" duration: %.3f msec\n", nsec_ave / 1000000);
1719 printf(
" num cycles: %.3f M\n", cycles_ave / 1000000);
1720 printf(
" cycles per round: %.3f\n",
1721 cycles_ave / rounds_ave);
1722 printf(
" cycles per event: %.3f\n",
1723 cycles_ave / events_ave);
1725 printf(
" without wait_ns cycles: %.3f\n",
1726 (cycles_ave - wait_cycles_ave) / events_ave);
1728 printf(
" ave events received: %.3f\n",
1729 events_ave / rounds_ave);
1730 printf(
" rounds per sec: %.3f M\n",
1731 (1000.0 * rounds_ave) / nsec_ave);
1733 events_per_sec = (1000.0 * events_ave) / nsec_ave;
1734 printf(
" events per sec: %.3f M\n", events_per_sec);
1736 printf(
" extra reads per sec: %.3f MB\n", tot_rd * events_per_sec);
1737 printf(
" extra writes per sec: %.3f MB\n", tot_rw * events_per_sec);
1739 printf(
"TOTAL events per sec: %.3f M\n\n",
1740 (1000.0 * events_sum) / nsec_ave);
1742 if (global->common_options.is_export) {
1743 if (test_common_write(
"schedule calls,enqueue calls,duration (msec),"
1744 "num cycles (M),cycles per round,cycles per event,"
1745 "ave events received,rounds per sec (M),"
1746 "events per sec (M),total events per sec (M)\n")) {
1747 ODPH_ERR(
"Export failed\n");
1748 test_common_write_term();
1752 if (test_common_write(
"%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n",
1753 rounds_ave, enqueues_ave, nsec_ave / 1000000,
1754 cycles_ave / 1000000, cycles_ave / rounds_ave,
1755 cycles_ave / events_ave, events_ave / rounds_ave,
1756 (1000.0 * rounds_ave) / nsec_ave,
1757 (1000.0 * events_ave) / nsec_ave,
1758 (1000.0 * events_sum) / nsec_ave)) {
1759 ODPH_ERR(
"Export failed\n");
1760 test_common_write_term();
1764 test_common_write_term();
1770int main(
int argc,
char **argv)
1772 odph_helper_options_t helper_options;
1776 test_global_t *global;
1777 test_common_options_t common_options;
1781 argc = odph_parse_options(argc, argv);
1782 if (odph_options(&helper_options)) {
1783 ODPH_ERR(
"Reading ODP helper options failed\n");
1787 argc = test_common_parse_options(argc, argv);
1788 if (test_common_options(&common_options)) {
1789 ODPH_ERR(
"Reading test options failed\n");
1805 init.
mem_model = helper_options.mem_model;
1809 ODPH_ERR(
"Global init failed\n");
1815 ODPH_ERR(
"Local init failed\n");
1819 shm =
odp_shm_reserve(
"sched_perf_global",
sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
1821 ODPH_ERR(
"SHM reserve failed\n");
1826 if (global == NULL) {
1827 ODPH_ERR(
"SHM alloc failed\n");
1830 test_globals = global;
1832 memset(global, 0,
sizeof(test_global_t));
1837 global->common_options = common_options;
1839 if (setup_sig_handler()) {
1840 ODPH_ERR(
"Signal handler setup failed\n");
1844 if (parse_options(argc, argv, &global->test_options))
1849 if (global->test_options.ctx_size) {
1850 uint64_t size = (uint64_t)global->test_options.ctx_size *
1851 global->test_options.tot_queue;
1854 ODP_CACHE_LINE_SIZE, 0);
1856 ODPH_ERR(
"SHM reserve %" PRIu64
" bytes failed\n", size);
1861 if (schedule_config(global))
1864 if (set_num_cpu(global))
1867 ret = create_pool(global);
1871 if (create_groups(global))
1874 if (create_all_queues(global))
1877 if (global->test_options.verbose)
1880 print_options(&global->test_options);
1883 start_workers(global, instance);
1886 odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
1888 print_queue_fairness(global);
1890 if (destroy_queues(global))
1893 if (destroy_groups(global))
1896 if (output_results(global))
1900 ODPH_ERR(
"Pool destroy failed.\n");
1906 ODPH_ERR(
"Event vector content pool destroy failed.\n");
1914 ODPH_ERR(
"SHM free failed\n");
1919 ODPH_ERR(
"Term local failed\n");
1924 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 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 compress
Compression APIs, e.g., odp_comp_xxx()