25 #include <odp/helper/odph_api.h>
27 #define PROG_NAME "odp_pool_latency"
50 #define DEF_TYPE BUFFER
51 #define DEF_CNT 32768U
52 #define DEF_SIZE 1024U
53 #define DEF_POLICY MANY
54 #define DEF_ROUNDS 100000U
56 #define DEF_WORKERS 1U
57 #define DEF_UA_SIZE 0U
59 #define MAX_PATTERN_LEN 32U
60 #define MAX_WORKERS ((uint32_t)(ODP_THREAD_COUNT_MAX - 1))
61 #define MAX_RETRIES 10U
63 #define COND_MIN(a, b) ((a) > 0U ? ODPH_MIN((a), (b)) : (b))
73 uint32_t data_size_buf;
74 uint32_t data_size_pkt;
75 uint32_t data_size_vec;
76 uint32_t cache_size_buf;
77 uint32_t cache_size_pkt;
78 uint32_t cache_size_tmo;
79 uint32_t cache_size_vec;
91 uint64_t max_alloc_tm;
92 uint64_t min_alloc_tm;
93 uint64_t max_alloc_rnd;
94 uint64_t min_alloc_rnd;
98 uint64_t max_uarea_tm;
99 uint64_t min_uarea_tm;
100 uint64_t max_uarea_rnd;
101 uint64_t min_uarea_rnd;
103 uint64_t max_free_tm;
104 uint64_t min_free_tm;
105 uint64_t max_free_rnd;
106 uint64_t min_free_rnd;
110 uint64_t pattern_errs;
111 uint64_t act_num_rounds;
112 uint8_t max_alloc_pt;
113 uint8_t min_alloc_pt;
114 uint8_t max_uarea_pt;
115 uint8_t min_uarea_pt;
126 typedef struct prog_config_s prog_config_t;
132 prog_config_t *prog_config;
138 typedef uint32_t (*alloc_fn_t)(worker_config_t *config,
void *data, uint32_t idx, uint32_t num,
139 uint64_t round, uint8_t pattern,
odp_bool_t is_saved);
140 typedef void (*free_fn_t)(
void *data, uint32_t idx, uint32_t num, stats_t *stats,
141 uint64_t round, uint8_t pattern,
odp_bool_t is_saved);
143 typedef struct prog_config_s {
144 odph_thread_t thread_tbl[MAX_WORKERS];
145 worker_config_t worker_config[MAX_WORKERS];
146 alloc_elem_t alloc_elems[MAX_PATTERN_LEN];
147 dynamic_defs_t dyn_defs;
158 uint32_t num_data_elems;
160 uint32_t handle_size;
163 uint32_t num_workers;
170 static prog_config_t *prog_conf;
177 static void init_config(prog_config_t *config)
179 alloc_elem_t *alloc_elem;
182 worker_config_t *worker;
184 memset(config, 0,
sizeof(*config));
185 alloc_elem = &config->alloc_elems[0];
186 alloc_elem->val = DEF_ALLOC;
187 alloc_elem->op = ALLOC;
188 alloc_elem = &config->alloc_elems[1];
189 alloc_elem->val = DEF_FREE;
190 alloc_elem->op = FREE;
191 alloc_elem->opt = DEF_DIR;
192 config->num_elems = 2U;
195 config->dyn_defs.num_evs_buf = COND_MIN(capa.
buf.
max_num, DEF_CNT);
196 config->dyn_defs.num_evs_pkt = COND_MIN(capa.
pkt.
max_num, DEF_CNT);
197 config->dyn_defs.num_evs_tmo = COND_MIN(capa.
tmo.
max_num, DEF_CNT);
198 config->dyn_defs.num_evs_vec = COND_MIN(capa.
vector.
max_num, DEF_CNT);
199 config->dyn_defs.data_size_buf = COND_MIN(capa.
buf.
max_size, DEF_SIZE);
200 config->dyn_defs.data_size_pkt = COND_MIN(capa.
pkt.
max_len, DEF_SIZE);
201 config->dyn_defs.data_size_vec = COND_MIN(capa.
vector.
max_size, DEF_SIZE);
209 config->cache_size = -1;
210 config->num_rounds = DEF_ROUNDS;
211 config->num_ignore = DEF_IGNORE;
212 config->num_workers = DEF_WORKERS;
213 config->uarea_size = DEF_UA_SIZE;
214 config->type = DEF_TYPE;
215 config->policy = DEF_POLICY;
217 for (uint32_t i = 0U; i < MAX_WORKERS; ++i) {
218 worker = &config->worker_config[i];
219 worker->stats.min_alloc_tm = UINT64_MAX;
220 worker->stats.min_uarea_tm = UINT64_MAX;
221 worker->stats.min_free_tm = UINT64_MAX;
227 static void parse_burst_pattern(prog_config_t *config,
const char *optarg)
229 char *tmp_str = strdup(optarg), *tmp, op, opt;
230 uint8_t num_elems = 0U;
238 tmp = strtok(tmp_str, DELIMITER);
240 while (tmp && num_elems < MAX_PATTERN_LEN) {
241 elem = &config->alloc_elems[num_elems];
242 ret = sscanf(tmp,
"%c%u%c", &op, &val, &opt);
244 if (ret == 2 || ret == 3) {
245 if (op == ALLOC || (op == FREE && (opt == TOP || opt == BOTTOM)) ||
256 tmp = strtok(NULL, DELIMITER);
260 config->num_elems = num_elems;
263 static void print_usage(
const dynamic_defs_t *dyn_defs)
266 "Pool latency tester. Allocate from different kind of pools with a varying set of\n"
267 "configurations and record latencies.\n"
269 "Usage: " PROG_NAME
" [OPTIONS]\n");
271 " E.g. " PROG_NAME
"\n"
272 " " PROG_NAME
" -b %c7" DELIMITER
"%c1%c" DELIMITER
"%c3" DELIMITER
"%c9%c\n",
273 ALLOC, FREE, TOP, ALLOC, FREE, BOTTOM);
274 printf(
" " PROG_NAME
" -b %c10" DELIMITER
"%c1000" DELIMITER
"%c10%c -t 1 -d 2048 "
275 "-p 0 -w 64\n", ALLOC, DELAY, FREE, TOP);
277 "Optional OPTIONS:\n"
279 " -b, --burst_pattern Burst pattern for allocations, frees and delays per round,\n"
280 " delimited by '%s', no spaces. Allocations are indicated\n"
281 " with a '%c' prefix, frees with a '%c' prefix. The location\n"
282 " of frees are indicated from the top of a previously\n"
283 " allocated array of events with a '%c' suffix and from the\n"
284 " bottom with a '%c' suffix. Delays are indicated with a\n"
285 " '%c' prefix, followed by a delay in nanoseconds.\n"
286 " Allocations and frees should be equal in the aggregate and\n"
287 " frees should never outnumber allocations at any instant.\n"
288 " '%c%u%s%c%u%c' by default. Maximum pattern length is %u.\n"
289 " -t, --type Pool type. %u by default.\n"
294 " -e, --event_count Number of events. Defaults:\n"
299 " -d, --data_size Data size in bytes, ignored in case of timeout pools, with\n"
300 " vector pools, defines the vector size.\n"
305 " -p, --policy Pool allocation policy. %u by default.\n"
307 " 0: One pool shared by workers\n"
308 " 1: One pool per worker\n"
309 " -r, --round_count Number of rounds to run. Use 0 to run indefinitely. %u by\n"
311 " -i, --ignore_rounds Ignore an amount of initial rounds. %u by default.\n"
312 " -c, --worker_count Number of workers. %u by default.\n"
313 " -C, --cache_size Maximum cache size for pools. Defaults:\n"
318 " -w, --write_uarea Write data to allocated event user areas. 0 bytes disables\n"
319 " user area write. %u by default.\n"
320 " -h, --help This help.\n"
321 "\n", DELIMITER, ALLOC, FREE, TOP, BOTTOM, DELAY, ALLOC, DEF_ALLOC, DELIMITER, FREE,
322 DEF_FREE, DEF_DIR, MAX_PATTERN_LEN, DEF_TYPE, dyn_defs->num_evs_buf,
323 dyn_defs->num_evs_pkt, dyn_defs->num_evs_tmo, dyn_defs->num_evs_vec,
324 dyn_defs->data_size_buf, dyn_defs->data_size_pkt, dyn_defs->data_size_vec,
325 DEF_POLICY, DEF_ROUNDS, DEF_IGNORE, DEF_WORKERS, dyn_defs->cache_size_buf,
326 dyn_defs->cache_size_pkt, dyn_defs->cache_size_tmo, dyn_defs->cache_size_vec,
330 static parse_result_t check_options(prog_config_t *config)
333 uint32_t max_workers, num_pools;
339 if (config->type != BUFFER && config->type != PACKET && config->type != TMO &&
340 config->type != VECTOR) {
341 ODPH_ERR(
"Invalid pool type: %u\n", config->type);
346 ODPH_ERR(
"Error querying pool capabilities\n");
352 if (config->num_workers == 0U || config->num_workers > max_workers) {
353 ODPH_ERR(
"Invalid worker count: %u (min: 1, max: %u)\n", config->num_workers,
359 num_pools = config->policy == SINGLE ? 1U : config->num_workers;
361 if (config->type == BUFFER) {
362 if (config->num_evs == 0U)
363 config->num_evs = config->dyn_defs.num_evs_buf;
365 if (config->data_size == 0U)
366 config->data_size = config->dyn_defs.data_size_buf;
368 if (config->cache_size == -1)
369 config->cache_size = config->dyn_defs.cache_size_buf;
371 if (config->num_evs > pool_capa.
buf.
max_num) {
372 ODPH_ERR(
"Invalid event count: %u (max: %u)\n", config->num_evs,
378 ODPH_ERR(
"Invalid data size: %u (max: %u)\n", config->data_size,
385 ODPH_ERR(
"Invalid cache size: %" PRIi64
" (min: %u, max: %u)\n",
392 ODPH_ERR(
"Invalid pool count: %u (max: %u)\n", num_pools,
399 }
else if (config->type == PACKET) {
400 if (config->num_evs == 0U)
401 config->num_evs = config->dyn_defs.num_evs_pkt;
403 if (config->data_size == 0U)
404 config->data_size = config->dyn_defs.data_size_pkt;
406 if (config->cache_size == -1)
407 config->cache_size = config->dyn_defs.cache_size_pkt;
409 if (config->num_evs > pool_capa.
pkt.
max_num) {
410 ODPH_ERR(
"Invalid event count: %u (max: %u)\n", config->num_evs,
415 if (config->data_size > pool_capa.
pkt.
max_len) {
416 ODPH_ERR(
"Invalid data size: %u (max: %u)\n", config->data_size,
423 ODPH_ERR(
"Invalid cache size: %" PRIi64
" (min: %u, max: %u)\n",
430 ODPH_ERR(
"Invalid pool count: %u (max: %u)\n", num_pools,
439 }
else if (config->type == TMO) {
440 if (config->num_evs == 0U)
441 config->num_evs = config->dyn_defs.num_evs_tmo;
443 if (config->cache_size == -1)
444 config->cache_size = config->dyn_defs.cache_size_tmo;
446 if (config->num_evs > pool_capa.
tmo.
max_num) {
447 ODPH_ERR(
"Invalid event count: %u (max: %u)\n", config->num_evs,
454 ODPH_ERR(
"Invalid cache size: %" PRIi64
" (min: %u, max: %u)\n",
461 ODPH_ERR(
"Invalid pool count: %u (max: %u)\n", num_pools,
469 if (config->num_evs == 0U)
470 config->num_evs = config->dyn_defs.num_evs_vec;
472 if (config->data_size == 0U)
473 config->data_size = config->dyn_defs.data_size_vec;
475 if (config->cache_size == -1)
476 config->cache_size = config->dyn_defs.cache_size_vec;
479 ODPH_ERR(
"Invalid event count: %u (max: %u)\n", config->num_evs,
485 ODPH_ERR(
"Invalid vector size: %u (max: %u)\n", config->data_size,
492 ODPH_ERR(
"Invalid cache size: %" PRIi64
" (min: %u, max: %u)\n",
499 ODPH_ERR(
"Invalid pool count: %u (max: %u)\n", num_pools,
508 if (config->num_elems == 0U) {
509 ODPH_ERR(
"Invalid burst pattern, no elements\n");
513 for (uint8_t i = 0U; i < config->num_elems; ++i) {
514 elem = &config->alloc_elems[i];
516 if (elem->op == ALLOC)
517 num_tot += elem->val;
518 else if (elem->op == FREE)
519 num_tot -= elem->val;
522 ODPH_ERR(
"Invalid burst pattern, frees exceed allocations "
523 "instantaneously\n");
527 config->num_data_elems += (elem->op == ALLOC ? elem->val : 0U);
531 ODPH_ERR(
"Invalid burst pattern, cumulative sum not zero: %" PRId64
"\n", num_tot);
536 ODPH_ERR(
"Error querying SHM capabilities\n");
540 if (shm_capa.
max_blocks < config->num_workers + 1U) {
541 ODPH_ERR(
"Invalid amount of SHM blocks: %u (max: %u)\n", config->num_workers + 1U,
546 shm_size = (uint64_t)config->num_data_elems * config->handle_size;
549 ODPH_ERR(
"Invalid total SHM block size: %" PRIu64
" (max: %" PRIu64
")\n",
554 if (config->policy != SINGLE && config->policy != MANY) {
555 ODPH_ERR(
"Invalid pool policy: %u\n", config->policy);
559 if (config->num_rounds > 0U && config->num_ignore >= config->num_rounds) {
560 ODPH_ERR(
"Invalid round ignore count: %" PRIu64
" (max: %" PRIu64
")\n",
561 config->num_ignore, config->num_rounds - 1U);
568 static parse_result_t parse_options(
int argc,
char **argv, prog_config_t *config)
572 static const struct option longopts[] = {
573 {
"burst_pattern", required_argument, NULL,
'b' },
574 {
"type", required_argument, NULL,
't' },
575 {
"event_count", required_argument, NULL,
'e' },
576 {
"data_size", required_argument, NULL,
'd' },
577 {
"policy", required_argument, NULL,
'p' },
578 {
"round_count", required_argument, NULL,
'r' },
579 {
"ignore_rounds", required_argument, NULL,
'i' },
580 {
"worker_count", required_argument, NULL,
'c' },
581 {
"cache_size", required_argument, NULL,
'C' },
582 {
"write_uarea", required_argument, NULL,
'w' },
583 {
"help", no_argument, NULL,
'h' },
587 static const char *shortopts =
"b:t:e:d:p:r:i:c:C:w:h";
592 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
599 parse_burst_pattern(config, optarg);
602 config->type = atoi(optarg);
605 config->num_evs = atoi(optarg);
608 config->data_size = atoi(optarg);
611 config->policy = atoi(optarg);
614 config->num_rounds = atoll(optarg);
617 config->num_ignore = atoll(optarg);
620 config->num_workers = atoi(optarg);
623 config->cache_size = atoi(optarg);
626 config->uarea_size = atoi(optarg);
629 print_usage(&config->dyn_defs);
633 print_usage(&config->dyn_defs);
638 return check_options(config);
641 static parse_result_t setup_program(
int argc,
char **argv, prog_config_t *config)
643 struct sigaction action = { .sa_handler = terminate };
645 if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
646 sigaddset(&action.sa_mask, SIGTERM) == -1 ||
647 sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
648 sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
649 ODPH_ERR(
"Error installing signal handler\n");
653 return parse_options(argc, argv, config);
657 uint64_t round, uint8_t pattern, stats_t *stats)
661 stats->alloc_tm += tm_diff;
662 stats->alloc_cnt += num_alloc;
663 ++stats->alloc_b_cnt;
665 if (tm_diff > stats->max_alloc_tm) {
666 stats->max_alloc_tm = tm_diff;
667 stats->max_alloc_rnd = round;
668 stats->max_alloc_pt = pattern;
671 if (tm_diff < stats->min_alloc_tm) {
672 stats->min_alloc_tm = tm_diff;
673 stats->min_alloc_rnd = round;
674 stats->min_alloc_pt = pattern;
678 static inline void write_to_uarea(uint8_t *data, uint32_t size)
680 memset(data, UA_DATA, size);
683 static inline void save_uarea_stats(
odp_time_t t1,
odp_time_t t2, uint64_t round, uint8_t pattern,
688 stats->uarea_tm += tm_diff;
690 if (tm_diff > stats->max_uarea_tm) {
691 stats->max_uarea_tm = tm_diff;
692 stats->max_uarea_rnd = round;
693 stats->max_uarea_pt = pattern;
696 if (tm_diff < stats->min_uarea_tm) {
697 stats->min_uarea_tm = tm_diff;
698 stats->min_uarea_rnd = round;
699 stats->min_uarea_pt = pattern;
703 static inline void save_free_stats(
odp_time_t t1,
odp_time_t t2, uint64_t round, uint8_t pattern,
708 stats->free_tm += tm_diff;
711 if (tm_diff > stats->max_free_tm) {
712 stats->max_free_tm = tm_diff;
713 stats->max_free_rnd = round;
714 stats->max_free_pt = pattern;
717 if (tm_diff < stats->min_free_tm) {
718 stats->min_free_tm = tm_diff;
719 stats->min_free_rnd = round;
720 stats->min_free_pt = pattern;
723 stats->max_free_tm = ODPH_MAX(tm_diff, stats->max_free_tm);
724 stats->min_free_tm = ODPH_MIN(tm_diff, stats->min_free_tm);
727 static uint32_t allocate_buffers(worker_config_t *config,
void *data, uint32_t idx, uint32_t num,
728 uint64_t round, uint8_t pattern,
odp_bool_t is_saved)
732 uint32_t retries = MAX_RETRIES;
734 uint32_t num_alloc, num_tot = 0U;
736 stats_t *stats = &config->stats;
738 while (retries-- > 0U && num_tot < num) {
739 num_alloc = num - num_tot;
755 save_alloc_stats(t1, t2, ret, round, pattern, stats);
758 if (config->uarea_size > 0U) {
761 for (uint32_t i = 0U; i < num_tot; ++i)
767 save_uarea_stats(t1, t2, round, pattern, stats);
773 static void free_buffers(
void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
784 save_free_stats(t1, t2, round, pattern, stats);
787 static uint32_t allocate_packets(worker_config_t *config,
void *data, uint32_t idx, uint32_t num,
788 uint64_t round, uint8_t pattern,
odp_bool_t is_saved)
792 uint32_t retries = MAX_RETRIES, data_size = config->data_size;
794 uint32_t num_alloc, num_tot = 0U;
796 stats_t *stats = &config->stats;
798 while (retries-- > 0U && num_tot < num) {
799 num_alloc = num - num_tot;
815 save_alloc_stats(t1, t2, ret, round, pattern, stats);
818 if (config->uarea_size > 0U) {
821 for (uint32_t i = 0U; i < num_tot; ++i)
827 save_uarea_stats(t1, t2, round, pattern, stats);
833 static void free_packets(
void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
844 save_free_stats(t1, t2, round, pattern, stats);
847 static uint32_t allocate_timeouts(worker_config_t *config,
void *data, uint32_t idx, uint32_t num,
848 uint64_t round, uint8_t pattern,
odp_bool_t is_saved)
852 uint32_t retries = MAX_RETRIES;
854 uint32_t num_alloc, num_tot = 0U;
856 stats_t *stats = &config->stats;
858 while (retries-- > 0U && num_tot < num) {
859 num_alloc = num - num_tot;
875 save_alloc_stats(t1, t2, ret, round, pattern, stats);
878 if (config->uarea_size > 0U) {
881 for (uint32_t i = 0U; i < num_tot; ++i)
887 save_uarea_stats(t1, t2, round, pattern, stats);
893 static void free_timeouts(
void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
904 save_free_stats(t1, t2, round, pattern, stats);
907 static uint32_t allocate_vectors(worker_config_t *config,
void *data, uint32_t idx, uint32_t num,
908 uint64_t round, uint8_t pattern,
odp_bool_t is_saved)
912 uint32_t num_tot = 0U;
914 stats_t *stats = &config->stats;
918 for (uint32_t i = 0U; i < num; ++i) {
924 vecs[num_tot++] = vec;
932 save_alloc_stats(t1, t2, num_tot, round, pattern, stats);
934 if (config->uarea_size > 0U) {
937 for (uint32_t i = 0U; i < num_tot; ++i)
943 save_uarea_stats(t1, t2, round, pattern, stats);
949 static void free_vectors(
void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
957 for (uint32_t i = 0U; i < num; ++i)
963 save_free_stats(t1, t2, round, pattern, stats);
978 static odp_bool_t setup_worker_config(prog_config_t *config)
982 worker_config_t *worker;
988 if (config->type == BUFFER) {
990 param.
buf.
num = config->num_evs;
991 param.
buf.
size = config->data_size;
994 config->alloc_fn = allocate_buffers;
995 config->free_fn = free_buffers;
996 }
else if (config->type == PACKET) {
998 param.
pkt.
num = config->num_evs;
999 param.
pkt.
len = config->data_size;
1003 config->alloc_fn = allocate_packets;
1004 config->free_fn = free_packets;
1005 }
else if (config->type == TMO) {
1007 param.
tmo.
num = config->num_evs;
1010 config->alloc_fn = allocate_timeouts;
1011 config->free_fn = free_timeouts;
1018 config->alloc_fn = allocate_vectors;
1019 config->free_fn = free_vectors;
1022 for (uint32_t i = 0U; i < config->num_workers; ++i) {
1023 pool = create_pool(PROG_NAME
"_pool", ¶m, config->policy);
1026 ODPH_ERR(
"Error creating worker pool\n");
1031 config->handle_size * config->num_data_elems,
1032 ODP_CACHE_LINE_SIZE, 0U);
1035 ODPH_ERR(
"Error creating worker SHM\n");
1042 ODPH_ERR(
"Error resolving worker SHM\n");
1046 worker = &config->worker_config[i];
1047 worker->pool = pool;
1048 worker->data = data;
1049 worker->prog_config = config;
1051 worker->data_size = config->data_size;
1052 worker->uarea_size = config->uarea_size;
1058 static int run_test(
void *args)
1060 worker_config_t *config = args;
1062 uint64_t i, num_ignore = config->prog_config->num_ignore;
1063 const uint64_t num_rnds = config->prog_config->num_rounds;
1065 uint32_t head_idx, cur_idx, val, num_alloc, idx;
1067 const uint8_t num_elems = config->prog_config->num_elems;
1068 const alloc_elem_t *elems = config->prog_config->alloc_elems, *elem;
1070 void *data = config->data;
1071 const alloc_fn_t alloc_fn = config->prog_config->alloc_fn;
1072 stats_t *stats = &config->stats;
1073 const free_fn_t free_fn = config->prog_config->free_fn;
1081 is_saved = (num_ignore > 0U ? num_ignore-- : num_ignore) == 0U;
1083 for (uint8_t j = 0U; j < num_elems; ++j) {
1089 num_alloc = alloc_fn(config, data, cur_idx, val, i, j, is_saved);
1092 ++stats->pattern_errs;
1094 cur_idx += num_alloc;
1095 }
else if (op == FREE) {
1098 val = ODPH_MIN(val, cur_idx - head_idx);
1100 if (elem->opt == TOP) {
1108 free_fn(data, idx, val, stats, i, j, is_saved);
1117 stats->act_num_rounds = i;
1123 static odp_bool_t setup_workers(prog_config_t *config)
1125 odph_thread_common_param_t thr_common;
1126 odph_thread_param_t thr_params[config->num_workers], *thr_param;
1130 odph_thread_common_param_init(&thr_common);
1131 thr_common.instance = config->odp_instance;
1132 thr_common.cpumask = &config->worker_mask;
1134 for (uint32_t i = 0; i < config->num_workers; ++i) {
1135 thr_param = &thr_params[i];
1136 odph_thread_param_init(thr_param);
1137 thr_param->start = run_test;
1139 thr_param->arg = &config->worker_config[i];
1142 if ((uint32_t)odph_thread_create(config->thread_tbl, &thr_common, thr_params,
1143 config->num_workers) != config->num_workers) {
1144 ODPH_ERR(
"Error configuring worker threads\n");
1153 static odp_bool_t setup_test(prog_config_t *config)
1155 return setup_worker_config(config) && setup_workers(config);
1158 static void stop_test(prog_config_t *config)
1161 (void)odph_thread_join(config->thread_tbl, config->num_workers);
1164 static void print_stats(
const prog_config_t *config)
1166 const alloc_elem_t *elem;
1167 const stats_t *stats;
1168 uint64_t ev_rate, ave_b_alloc_tm, b_alloc_min, b_alloc_max, ave_b_free_tm, b_free_min,
1169 b_free_max, ave_alloc_tm, ave_free_tm, ave_ua_b_tm, b_ua_min, b_ua_max, ave_ua_tm,
1170 tot_b_alloc_tm = 0U, tot_b_free_tm = 0U, tot_alloc_tm = 0U, tot_free_tm = 0U,
1171 tot_alloc_min = 0U, tot_alloc_max = 0U, tot_free_min = 0U, tot_free_max = 0U,
1172 tot_b_ua_tm = 0U, tot_ua_tm = 0U, tot_ua_min = 0U, tot_ua_max = 0U;
1174 printf(
"\n==================\n\n"
1175 "Pool latency test done\n\n"
1177 " event count: %u\n", config->type == BUFFER ?
"buffer" :
1178 config->type == PACKET ?
"packet" : config->type == TMO ?
"timeout" :
"vector",
1181 if (config->type != TMO)
1183 config->type != VECTOR ?
"data size: " :
"vector size: ",
1186 printf(
" pool policy: %s\n"
1187 " target round count: %" PRIu64
"\n"
1188 " ignore count: %" PRIu64
"\n"
1189 " cache size: %" PRIi64
"\n"
1190 " user area: %u (B)\n"
1191 " burst pattern:\n", config->policy == SINGLE ?
"shared" :
"per-worker",
1192 config->num_rounds, config->num_ignore, config->cache_size, config->uarea_size);
1194 for (uint8_t i = 0U; i < config->num_elems; ++i) {
1195 elem = &config->alloc_elems[i];
1196 printf(
" %s %u%s\n", elem->op == ALLOC ?
"allocate:" :
1197 elem->op == FREE && elem->opt == TOP ?
"free (t):" :
1198 elem->op == FREE && elem->opt == BOTTOM ?
"free (b):" :
1199 "delay: ", elem->val, elem->op == DELAY ?
" (ns)" :
"");
1204 for (uint32_t i = 0U; i < config->num_workers; ++i) {
1205 stats = &config->worker_config[i].stats;
1206 ev_rate = stats->tot_tm > 0U ?
1208 ave_b_alloc_tm = stats->alloc_b_cnt > 0U ?
1209 stats->alloc_tm / stats->alloc_b_cnt : 0U;
1210 b_alloc_min = ave_b_alloc_tm > 0U ? stats->min_alloc_tm : 0U;
1211 b_alloc_max = ave_b_alloc_tm > 0U ? stats->max_alloc_tm : 0U;
1212 ave_b_free_tm = stats->free_b_cnt > 0U ?
1213 stats->free_tm / stats->free_b_cnt : 0U;
1214 b_free_min = ave_b_free_tm > 0U ? stats->min_free_tm : 0U;
1215 b_free_max = ave_b_free_tm > 0U ? stats->max_free_tm : 0U;
1216 ave_alloc_tm = stats->alloc_cnt > 0U ? stats->alloc_tm / stats->alloc_cnt : 0U;
1217 ave_free_tm = stats->alloc_cnt > 0U ? stats->free_tm / stats->alloc_cnt : 0U;
1219 printf(
" worker %d:\n"
1220 " actual round count: %" PRIu64
"\n"
1221 " significant events allocated/freed: %" PRIu64
"\n"
1222 " allocation retries: %" PRIu64
"\n"
1223 " allocation errors: %" PRIu64
"\n"
1224 " pattern errors: %" PRIu64
"\n"
1225 " run time: %" PRIu64
" (ns)\n"
1226 " event rate %" PRIu64
" (evs/s)\n"
1227 " average latency breakdown (ns):\n"
1228 " per allocation burst: %" PRIu64
" (min: %" PRIu64
" (round: %"
1229 PRIu64
", pattern: %u), max: %" PRIu64
" (round: %" PRIu64
", pattern: %u))"
1231 " per allocation: %" PRIu64
"\n"
1232 " per free burst: %" PRIu64
" (min: %" PRIu64
" (round: %"
1233 PRIu64
", pattern: %u), max: %" PRIu64
" (round: %" PRIu64
", pattern: %u))"
1235 " per free: %" PRIu64
"\n", i, stats->act_num_rounds,
1236 stats->alloc_cnt, stats->reallocs, stats->alloc_errs, stats->pattern_errs,
1237 stats->tot_tm, ev_rate, ave_b_alloc_tm, b_alloc_min, stats->min_alloc_rnd,
1238 stats->min_alloc_pt, b_alloc_max, stats->max_alloc_rnd, stats->max_alloc_pt,
1239 ave_alloc_tm, ave_b_free_tm, b_free_min, stats->min_free_rnd,
1240 stats->min_free_pt, b_free_max, stats->max_free_rnd, stats->max_free_pt,
1242 tot_b_alloc_tm += ave_b_alloc_tm;
1243 tot_b_free_tm += ave_b_free_tm;
1244 tot_alloc_tm += ave_alloc_tm;
1245 tot_free_tm += ave_free_tm;
1246 tot_alloc_min += b_alloc_min;
1247 tot_alloc_max += b_alloc_max;
1248 tot_free_min += b_free_min;
1249 tot_free_max += b_free_max;
1251 if (config->uarea_size > 0U) {
1252 ave_ua_b_tm = stats->alloc_b_cnt > 0U ?
1253 stats->uarea_tm / stats->alloc_b_cnt : 0U;
1254 ave_ua_tm = stats->alloc_cnt > 0U ?
1255 stats->uarea_tm / stats->alloc_cnt : 0U;
1256 b_ua_min = ave_ua_b_tm > 0U ? stats->min_uarea_tm : 0U;
1257 b_ua_max = ave_ua_b_tm > 0U ? stats->max_uarea_tm : 0U;
1258 printf(
" per ua write burst: %" PRIu64
" (min: %" PRIu64
" ("
1259 "round: %" PRIu64
", pattern: %u), max: %" PRIu64
" (round: %"
1260 PRIu64
", pattern: %u))\n"
1261 " per ua write: %" PRIu64
"\n", ave_ua_b_tm,
1262 b_ua_min, stats->min_uarea_rnd, stats->min_uarea_pt, b_ua_max,
1263 stats->max_uarea_rnd, stats->max_uarea_pt, ave_ua_tm);
1264 tot_b_ua_tm += ave_ua_b_tm;
1265 tot_ua_tm += ave_ua_tm;
1266 tot_ua_min += b_ua_min;
1267 tot_ua_max += b_ua_max;
1273 printf(
" total (ns):\n"
1274 " per allocation burst: %" PRIu64
" (min: %" PRIu64
", max: %" PRIu64
")\n"
1275 " per allocation: %" PRIu64
"\n"
1276 " per free burst: %" PRIu64
" (min: %" PRIu64
", max: %" PRIu64
")\n"
1277 " per free: %" PRIu64
"\n",
1278 tot_b_alloc_tm / config->num_workers, tot_alloc_min / config->num_workers,
1279 tot_alloc_max / config->num_workers, tot_alloc_tm / config->num_workers,
1280 tot_b_free_tm / config->num_workers, tot_free_min / config->num_workers,
1281 tot_free_max / config->num_workers, tot_free_tm / config->num_workers);
1283 if (config->uarea_size > 0U) {
1284 printf(
" per ua write burst: %" PRIu64
" (min: %" PRIu64
", max: %"
1286 " per ua write: %" PRIu64
"\n",
1287 tot_b_ua_tm / config->num_workers, tot_ua_min / config->num_workers,
1288 tot_ua_max / config->num_workers, tot_ua_tm / config->num_workers);
1291 printf(
"\n==================\n");
1294 static void destroy_pool(
odp_pool_t pool, uint8_t policy)
1298 if (policy == SINGLE && is_destroyed)
1302 is_destroyed =
true;
1305 static void teardown(
const prog_config_t *config)
1307 const worker_config_t *worker;
1309 for (uint32_t i = 0U; i < config->num_workers; ++i) {
1310 worker = &config->worker_config[i];
1313 destroy_pool(worker->pool, config->policy);
1320 int main(
int argc,
char **argv)
1322 odph_helper_options_t odph_opts;
1326 int ret = EXIT_SUCCESS;
1327 parse_result_t parse_res;
1329 argc = odph_parse_options(argc, argv);
1331 if (odph_options(&odph_opts) == -1) {
1332 ODPH_ERR(
"Error while reading ODP helper options, exiting\n");
1337 init_param.
mem_model = odph_opts.mem_model;
1340 ODPH_ERR(
"ODP global init failed, exiting\n");
1345 ODPH_ERR(
"ODP local init failed, exiting\n");
1349 shm_cfg =
odp_shm_reserve(PROG_NAME
"_cfg",
sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
1353 ODPH_ERR(
"Error reserving shared memory\n");
1360 if (prog_conf == NULL) {
1361 ODPH_ERR(
"Error resolving shared memory address\n");
1366 parse_res = setup_program(argc, argv, prog_conf);
1368 if (parse_res == PRS_NOK) {
1373 if (parse_res == PRS_TERM) {
1381 if (!setup_test(prog_conf)) {
1386 stop_test(prog_conf);
1387 print_stats(prog_conf);
1390 teardown(prog_conf);
1397 ODPH_ERR(
"ODP local terminate failed, exiting\n");
1402 ODPH_ERR(
"ODP global terminate failed, exiting\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_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
void * odp_buffer_user_area(odp_buffer_t buf)
Buffer user area.
int odp_buffer_alloc_multi(odp_pool_t pool, odp_buffer_t buf[], int num)
Allocate multiple buffers.
_odp_abi_buffer_t * odp_buffer_t
ODP buffer.
void odp_buffer_free_multi(const odp_buffer_t buf[], int num)
Free multiple buffers.
#define ODP_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
#define odp_unlikely(x)
Branch unlikely taken.
#define ODP_UNUSED
Intentionally unused variables of functions.
#define odp_likely(x)
Branch likely taken.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_instance(odp_instance_t *instance)
Get instance handle.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
#define ODP_STATIC_ASSERT(cond, msg)
Compile time assertion macro.
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_abi_packet_vector_t * odp_packet_vector_t
ODP packet vector.
void * odp_packet_vector_user_area(odp_packet_vector_t pktv)
Packet vector user area.
void * odp_packet_user_area(odp_packet_t pkt)
User area address.
odp_packet_vector_t odp_packet_vector_alloc(odp_pool_t pool)
Allocate a packet vector from a packet vector pool.
void odp_packet_vector_free(odp_packet_vector_t pktv)
Free packet vector.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
int odp_packet_alloc_multi(odp_pool_t pool, uint32_t len, odp_packet_t pkt[], int num)
Allocate multiple packets from a packet pool.
_odp_abi_packet_t * odp_packet_t
ODP packet.
#define ODP_PACKET_VECTOR_INVALID
Invalid packet vector.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_TIMEOUT
Timeout pool.
@ ODP_POOL_VECTOR
Vector event pool.
@ ODP_POOL_BUFFER
Buffer pool.
@ ODP_POOL_PACKET
Packet pool.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#define ODP_SHM_INVALID
Invalid shared memory block.
int odp_shm_capability(odp_shm_capability_t *capa)
Query shared memory capabilities.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_local_strict(void)
Current local time (strict)
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
void * odp_timeout_user_area(odp_timeout_t tmo)
Timeout user area.
int odp_timeout_alloc_multi(odp_pool_t pool, odp_timeout_t tmo[], int num)
Allocate multiple timeouts.
void odp_timeout_free_multi(odp_timeout_t tmo[], int num)
Free multiple timeouts.
_odp_abi_timeout_t * odp_timeout_t
ODP timeout handle.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
struct odp_pool_capability_t::@121 buf
Buffer pool capabilities
struct odp_pool_capability_t::@122 pkt
Packet pool capabilities
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@123 tmo
Timeout pool capabilities
uint32_t min_cache_size
Minimum size of thread local cache.
uint32_t max_uarea_size
Maximum user area size in bytes.
struct odp_pool_capability_t::@124 vector
Vector pool capabilities.
uint32_t max_size
Maximum buffer data size in bytes.
uint32_t max_cache_size
Maximum size of thread local cache.
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 max_len
Maximum packet 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::@127 tmo
Parameters for timeout pools.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
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.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
struct odp_pool_param_t::@128 vector
Parameters for vector pools.
struct odp_pool_param_t::@125 buf
Parameters for buffer pools.
Shared memory capabilities.
uint32_t max_blocks
Maximum number of shared memory blocks.
uint64_t max_size
Maximum memory block size in bytes.