22 #include <odp/helper/odph_api.h>
24 #include <export_results.h>
26 #define STAT_AVAILABLE 0x1
27 #define STAT_CACHE 0x2
28 #define STAT_THR_CACHE 0x4
29 #define STAT_ALLOC_OPS 0x10
30 #define STAT_FREE_OPS 0x20
31 #define STAT_TOTAL_OPS 0x40
33 typedef struct test_options_t {
46 typedef struct test_stat_t {
55 typedef struct test_global_t {
56 test_options_t test_options;
63 test_common_options_t common_options;
67 static void print_usage(
void)
70 "Pool performance test\n"
72 "Usage: odp_pool_perf [options]\n"
74 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default 1.\n"
75 " -e, --num_event Number of events\n"
76 " -r, --num_round Number of rounds\n"
77 " -b, --burst Maximum number of events per operation\n"
78 " -n, --num_burst Number of bursts allocated/freed back-to-back\n"
79 " -s, --data_size Data size in bytes\n"
80 " -S, --stats_mode Pool statistics usage. Enable counters with combination of these flags:\n"
81 " 0: no pool statistics (default)\n"
83 " 0x2: cache_available\n"
84 " 0x4: thread_cache_available\n"
88 " -t, --pool_type 0: Buffer pool (default)\n"
90 " -C, --cache_size Pool cache size (per thread)\n"
91 " -h, --help This help\n"
95 static int parse_options(
int argc,
char *argv[], test_options_t *test_options)
100 static const struct option longopts[] = {
101 {
"num_cpu", required_argument, NULL,
'c'},
102 {
"num_event", required_argument, NULL,
'e'},
103 {
"num_round", required_argument, NULL,
'r'},
104 {
"burst", required_argument, NULL,
'b'},
105 {
"num_burst", required_argument, NULL,
'n'},
106 {
"data_size", required_argument, NULL,
's'},
107 {
"stats_mode", required_argument, NULL,
'S'},
108 {
"pool_type", required_argument, NULL,
't'},
109 {
"cache_size", required_argument, NULL,
'C'},
110 {
"help", no_argument, NULL,
'h'},
114 static const char *shortopts =
"+c:e:r:b:n:s:S:t:C:h";
116 test_options->num_cpu = 1;
117 test_options->num_event = 1000;
118 test_options->num_round = 100000;
119 test_options->max_burst = 100;
120 test_options->num_burst = 1;
121 test_options->data_size = 64;
122 test_options->stats_mode = 0;
123 test_options->pool_type = 0;
124 test_options->cache_size = UINT32_MAX;
127 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
134 test_options->num_cpu = atoi(optarg);
137 test_options->num_event = atoi(optarg);
140 test_options->num_round = atoi(optarg);
143 test_options->max_burst = atoi(optarg);
146 test_options->num_burst = atoi(optarg);
149 test_options->data_size = atoi(optarg);
152 test_options->stats_mode = strtoul(optarg, NULL, 0);
155 test_options->pool_type = atoi(optarg);
158 test_options->cache_size = atoi(optarg);
169 if (test_options->num_burst * test_options->max_burst >
170 test_options->num_event) {
171 printf(
"Not enough events (%u) for the burst configuration.\n"
172 "Use smaller burst size (%u) or less bursts (%u)\n",
173 test_options->num_event, test_options->max_burst,
174 test_options->num_burst);
181 static int set_num_cpu(test_global_t *global)
184 test_options_t *test_options = &global->test_options;
185 int num_cpu = test_options->num_cpu;
189 printf(
"Error: Too many workers. Maximum is %i.\n",
196 if (num_cpu && ret != num_cpu) {
197 printf(
"Error: Too many workers. Max supported %i.\n", ret);
204 test_options->num_cpu = num_cpu;
212 static int create_pool(test_global_t *global)
218 uint32_t max_num, max_size, min_cache_size, max_cache_size;
219 test_options_t *test_options = &global->test_options;
220 uint32_t num_event = test_options->num_event;
221 uint32_t num_round = test_options->num_round;
222 uint32_t max_burst = test_options->max_burst;
223 uint32_t num_burst = test_options->num_burst;
224 uint32_t num_cpu = test_options->num_cpu;
225 uint32_t data_size = test_options->data_size;
226 uint32_t cache_size = test_options->cache_size;
227 uint32_t stats_mode = test_options->stats_mode;
228 int packet_pool = test_options->pool_type;
234 if (cache_size == UINT32_MAX)
238 if (stats_mode & STAT_AVAILABLE)
240 if (stats_mode & STAT_CACHE)
242 if (stats_mode & STAT_THR_CACHE)
244 if (stats_mode & STAT_ALLOC_OPS)
246 if (stats_mode & STAT_FREE_OPS)
248 if (stats_mode & STAT_TOTAL_OPS)
251 printf(
"\nPool performance test\n");
252 printf(
" num cpu %u\n", num_cpu);
253 printf(
" num rounds %u\n", num_round);
254 printf(
" num events %u\n", num_event);
255 printf(
" max burst %u\n", max_burst);
256 printf(
" num bursts %u\n", num_burst);
257 printf(
" data size %u\n", data_size);
258 printf(
" cache size %u\n", cache_size);
259 printf(
" stats mode 0x%x\n", stats_mode);
260 printf(
" pool type %s\n\n", packet_pool ?
"packet" :
"buffer");
263 printf(
"Error: Pool capa failed.\n");
281 if ((stats_capa.
all & stats.
all) != stats.
all) {
282 printf(
"Error: requested statistics not supported (0x%" PRIx64
" / 0x%" PRIx64
")\n",
283 stats.
all, stats_capa.
all);
287 if (cache_size < min_cache_size) {
288 printf(
"Error: min cache size supported %u\n", min_cache_size);
292 if (cache_size > max_cache_size) {
293 printf(
"Error: max cache size supported %u\n", max_cache_size);
297 if (max_num && num_event > max_num) {
298 printf(
"Error: max events supported %u\n", max_num);
302 if (max_size && data_size > max_size) {
303 printf(
"Error: max data size supported %u\n", max_size);
309 pool_param.
pkt.
num = num_event;
310 pool_param.
pkt.
len = data_size;
316 pool_param.
buf.
num = num_event;
317 pool_param.
buf.
size = data_size;
326 printf(
"Error: Pool create failed.\n");
335 static int test_buffer_pool(
void *arg)
338 uint32_t num, num_free, num_freed, i, rounds;
339 uint64_t c1, c2, cycles, nsec;
340 uint64_t events, frees;
342 test_global_t *global = arg;
343 test_options_t *test_options = &global->test_options;
344 uint32_t num_round = test_options->num_round;
345 uint32_t max_burst = test_options->max_burst;
346 uint32_t num_burst = test_options->num_burst;
347 uint32_t max_num = num_burst * max_burst;
353 for (i = 0; i < max_num; i++)
366 for (rounds = 0; rounds < num_round; rounds++) {
369 for (i = 0; i < num_burst; i++) {
373 printf(
"Error: Alloc failed. Round %u\n",
390 while (num_freed < num) {
391 num_free = num - num_freed;
392 if (num_free > max_burst)
393 num_free = max_burst;
397 num_freed += num_free;
408 global->stat[thr].rounds = rounds;
409 global->stat[thr].frees = frees;
410 global->stat[thr].events = events;
411 global->stat[thr].nsec = nsec;
412 global->stat[thr].cycles = cycles;
417 static int test_packet_pool(
void *arg)
420 uint32_t num, num_free, num_freed, i, rounds;
421 uint64_t c1, c2, cycles, nsec;
422 uint64_t events, frees;
424 test_global_t *global = arg;
425 test_options_t *test_options = &global->test_options;
426 uint32_t num_round = test_options->num_round;
427 uint32_t max_burst = test_options->max_burst;
428 uint32_t num_burst = test_options->num_burst;
429 uint32_t max_num = num_burst * max_burst;
430 uint32_t data_size = test_options->data_size;
436 for (i = 0; i < max_num; i++)
449 for (rounds = 0; rounds < num_round; rounds++) {
452 for (i = 0; i < num_burst; i++) {
456 printf(
"Error: Alloc failed. Round %u\n",
474 while (num_freed < num) {
475 num_free = num - num_freed;
476 if (num_free > max_burst)
477 num_free = max_burst;
481 num_freed += num_free;
492 global->stat[thr].rounds = rounds;
493 global->stat[thr].frees = frees;
494 global->stat[thr].events = events;
495 global->stat[thr].nsec = nsec;
496 global->stat[thr].cycles = cycles;
501 static int start_workers(test_global_t *global,
odp_instance_t instance)
503 odph_thread_common_param_t thr_common;
504 odph_thread_param_t thr_param;
505 test_options_t *test_options = &global->test_options;
506 int num_cpu = test_options->num_cpu;
507 int packet_pool = test_options->pool_type;
509 odph_thread_common_param_init(&thr_common);
510 thr_common.instance = instance;
511 thr_common.cpumask = &global->cpumask;
512 thr_common.share_param = 1;
514 odph_thread_param_init(&thr_param);
515 thr_param.arg = global;
519 thr_param.start = test_packet_pool;
521 thr_param.start = test_buffer_pool;
523 if (odph_thread_create(global->thread_tbl, &thr_common, &thr_param,
530 static void test_stats_perf(test_global_t *global)
536 int num_thr = global->test_options.num_cpu + 1;
538 double nsec_ave = 0.0;
539 const int rounds = 1000;
550 for (i = 0; i < rounds; i++) {
552 printf(
"Error: Stats request failed on round %i\n", i);
561 nsec_ave = (double)nsec / i;
563 printf(
"Pool statistics:\n");
564 printf(
" odp_pool_stats() calls %i\n", i);
565 printf(
" ave call latency %.2f nsec\n", nsec_ave);
566 printf(
" num threads %i\n", num_thr);
567 printf(
" alloc_ops %" PRIu64
"\n", stats.
alloc_ops);
568 printf(
" free_ops %" PRIu64
"\n", stats.
free_ops);
569 printf(
" total_ops %" PRIu64
"\n", stats.
total_ops);
570 printf(
" available %" PRIu64
"\n", stats.
available);
572 for (i = 0; i < num_thr; i++) {
573 printf(
" thr[%2i] cache_available %" PRIu64
"\n",
580 static int output_results(test_global_t *global)
583 double rounds_ave, allocs_ave, frees_ave;
584 double events_ave, nsec_ave, cycles_ave;
585 test_options_t *test_options = &global->test_options;
586 int num_cpu = test_options->num_cpu;
587 uint32_t num_burst = test_options->num_burst;
588 uint64_t rounds_sum = 0;
589 uint64_t frees_sum = 0;
590 uint64_t events_sum = 0;
591 uint64_t nsec_sum = 0;
592 uint64_t cycles_sum = 0;
596 rounds_sum += global->stat[i].rounds;
597 frees_sum += global->stat[i].frees;
598 events_sum += global->stat[i].events;
599 nsec_sum += global->stat[i].nsec;
600 cycles_sum += global->stat[i].cycles;
603 if (rounds_sum == 0) {
604 printf(
"No results.\n");
608 rounds_ave = rounds_sum / num_cpu;
609 allocs_ave = (num_burst * rounds_sum) / num_cpu;
610 frees_ave = frees_sum / num_cpu;
611 events_ave = events_sum / num_cpu;
612 nsec_ave = nsec_sum / num_cpu;
613 cycles_ave = cycles_sum / num_cpu;
616 printf(
"RESULTS - per thread (Million events per sec):\n");
617 printf(
"----------------------------------------------\n");
618 printf(
" 1 2 3 4 5 6 7 8 9 10");
621 if (global->stat[i].rounds) {
625 printf(
"%6.1f ", (1000.0 * global->stat[i].events) /
626 global->stat[i].nsec);
632 printf(
"RESULTS - average over %i threads:\n", num_cpu);
633 printf(
"----------------------------------\n");
634 printf(
" alloc calls: %.3f\n", allocs_ave);
635 printf(
" free calls: %.3f\n", frees_ave);
636 printf(
" duration: %.3f msec\n", nsec_ave / 1000000);
637 printf(
" num cycles: %.3f M\n", cycles_ave / 1000000);
638 printf(
" cycles per round: %.3f\n",
639 cycles_ave / rounds_ave);
640 printf(
" cycles per event: %.3f\n",
641 cycles_ave / events_ave);
642 printf(
" ave events allocated: %.3f\n",
643 events_ave / allocs_ave);
644 printf(
" allocs per sec: %.3f M\n",
645 (1000.0 * allocs_ave) / nsec_ave);
646 printf(
" frees per sec: %.3f M\n",
647 (1000.0 * frees_ave) / nsec_ave);
648 printf(
" events per sec: %.3f M\n\n",
649 (1000.0 * events_ave) / nsec_ave);
651 printf(
"TOTAL events per sec: %.3f M\n\n",
652 (1000.0 * events_sum) / nsec_ave);
654 if (global->common_options.is_export) {
655 if (test_common_write(
"alloc calls,free calls,duration (msec),"
656 "num cycles (M),cycles per round,cycles per event,"
657 "ave events allocated,allocs per sec (M),frees per sec (M),"
658 "events per sec (M),TOTAL events per sec (M)\n")) {
659 ODPH_ERR(
"Export failed\n");
660 test_common_write_term();
664 if (test_common_write(
"%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n",
665 allocs_ave, frees_ave, nsec_ave / 1000000,
666 cycles_ave / 1000000, cycles_ave / rounds_ave,
667 cycles_ave / events_ave, events_ave / allocs_ave,
668 (1000.0 * allocs_ave) / nsec_ave,
669 (1000.0 * frees_ave) / nsec_ave,
670 (1000.0 * events_ave) / nsec_ave,
671 (1000.0 * events_sum) / nsec_ave)) {
672 ODPH_ERR(
"Export failed\n");
673 test_common_write_term();
677 test_common_write_term();
683 int main(
int argc,
char **argv)
685 odph_helper_options_t helper_options;
689 test_global_t *global;
690 test_common_options_t common_options;
693 argc = odph_parse_options(argc, argv);
694 if (odph_options(&helper_options)) {
695 ODPH_ERR(
"Error: Reading ODP helper options failed.\n");
699 argc = test_common_parse_options(argc, argv);
700 if (test_common_options(&common_options)) {
701 ODPH_ERR(
"Error: Reading test options failed\n");
715 init.
mem_model = helper_options.mem_model;
719 printf(
"Error: Global init failed.\n");
725 printf(
"Error: Local init failed.\n");
729 shm =
odp_shm_reserve(
"pool_perf_global",
sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
731 ODPH_ERR(
"Error: Shared mem reserve failed.\n");
736 if (global == NULL) {
737 ODPH_ERR(
"Error: Shared mem alloc failed\n");
741 memset(global, 0,
sizeof(test_global_t));
744 global->common_options = common_options;
746 if (parse_options(argc, argv, &global->test_options))
751 if (set_num_cpu(global))
754 if (create_pool(global))
758 start_workers(global, instance);
761 odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
763 if (global->test_options.stats_mode)
764 test_stats_perf(global);
766 if (output_results(global))
770 printf(
"Error: Pool destroy failed.\n");
775 ODPH_ERR(
"Error: Shared mem free failed.\n");
780 printf(
"Error: term local failed.\n");
785 printf(
"Error: term global failed.\n");
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.
int odp_buffer_alloc_multi(odp_pool_t pool, odp_buffer_t buf[], int num)
Allocate multiple buffers.
#define ODP_BUFFER_INVALID
Invalid buffer.
void odp_buffer_free_multi(const odp_buffer_t buf[], int num)
Free multiple buffers.
#define odp_unlikely(x)
Branch unlikely taken.
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.
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.
#define ODP_PACKET_INVALID
Invalid packet.
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_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_MAX_THREAD_STATS
Maximum number of per thread statistics a single odp_pool_stats() call can read.
int odp_pool_stats(odp_pool_t pool, odp_pool_stats_t *stats)
Read pool statistics.
#define ODP_POOL_INVALID
Invalid 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.
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.
void odp_sys_info_print(void)
Print system info.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
odp_time_t odp_time_local(void)
Current local time.
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.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
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.
uint32_t min_cache_size
Minimum size of thread local cache.
uint32_t max_size
Maximum buffer data size in bytes.
odp_pool_stats_opt_t stats
Supported statistics counters.
uint32_t max_cache_size
Maximum size of thread local cache.
uint32_t max_len
Maximum packet data length in bytes.
uint32_t num
Number of buffers in the pool.
uint32_t max_len
Maximum packet length that will be allocated from the pool.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
uint32_t size
Minimum buffer size in bytes.
uint32_t max_num
Maximum number of packets.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
odp_pool_stats_opt_t stats
Configure statistics counters.
struct odp_pool_param_t::@125 buf
Parameters for buffer pools.
Pool statistics counters.
uint64_t alloc_ops
The number of alloc operations from the pool.
uint64_t available
The number of available events in the pool.
uint16_t first
First thread identifier to read counters from.
struct odp_pool_stats_t::@120 thread
Per thread counters.
uint64_t free_ops
The number of free operations to the pool.
uint64_t total_ops
The total number of alloc and free operations.
uint64_t cache_available
The number of available events in the local caches of all threads.
uint16_t last
Last thread identifier to read counters from.
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()
uint32_t schedule
Scheduler APIs, e.g., odp_schedule_xxx()
struct odp_feature_t::@148 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()
Pool statistics counters options.
uint64_t total_ops
See odp_pool_stats_t::total_ops.
uint64_t cache_available
See odp_pool_stats_t::cache_available.
struct odp_pool_stats_opt_t::@119 bit
Option flags.
uint64_t thread_cache_available
See odp_pool_stats_t::thread::cache_available.
uint64_t free_ops
See odp_pool_stats_t::free_ops.
uint64_t alloc_ops
See odp_pool_stats_t::alloc_ops.
uint64_t available
See odp_pool_stats_t::available.
uint64_t all
All bits of the bit field structure.