21 #include <odp/helper/odph_api.h>
23 typedef struct test_options_t {
33 typedef struct test_global_t test_global_t;
35 typedef struct test_thread_ctx_t {
36 test_global_t *global;
42 struct test_global_t {
43 test_options_t test_options;
55 static void print_usage(
void)
58 "Memory performance test\n"
60 "Usage: odp_mem_perf [options]\n"
62 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default 1.\n"
63 " -r, --num_round Number of rounds\n"
64 " -l, --data_len Data length in bytes\n"
65 " -f, --flags SHM flags parameter. Default 0.\n"
66 " -p, --private 0: The same memory area is shared between threads (default)\n"
67 " 1: Memory areas are private to each thread. This increases\n"
68 " memory consumption to num_cpu * data_len.\n"
69 " -m, --mode 0: Memset data (default)\n"
70 " 1: Memcpy data. On each round, reads data from one half of the memory area\n"
71 " and writes it to the other half.\n"
72 " -h, --help This help\n"
76 static int parse_options(
int argc,
char *argv[], test_options_t *test_options)
81 static const struct option longopts[] = {
82 {
"num_cpu", required_argument, NULL,
'c'},
83 {
"num_round", required_argument, NULL,
'r'},
84 {
"data_len", required_argument, NULL,
'l'},
85 {
"flags", required_argument, NULL,
'f'},
86 {
"private", required_argument, NULL,
'p'},
87 {
"mode", required_argument, NULL,
'm'},
88 {
"help", no_argument, NULL,
'h'},
92 static const char *shortopts =
"+c:r:l:f:p:m:h";
94 test_options->num_cpu = 1;
95 test_options->num_round = 1000;
96 test_options->data_len = 10 * 1024 * 1024;
97 test_options->shm_flags = 0;
98 test_options->private = 0;
99 test_options->mode = 0;
102 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
109 test_options->num_cpu = atoi(optarg);
112 test_options->num_round = atoi(optarg);
115 test_options->data_len = strtoull(optarg, NULL, 0);
118 test_options->shm_flags = strtoul(optarg, NULL, 0);
121 test_options->private = atoi(optarg);
124 test_options->mode = atoi(optarg);
138 static int set_num_cpu(test_global_t *global)
141 test_options_t *test_options = &global->test_options;
142 int num_cpu = test_options->num_cpu;
156 if (num_cpu && ret != num_cpu) {
157 ODPH_ERR(
"Too many workers. Max supported %i.\n", ret);
164 ODPH_ERR(
"Too many cpus from odp_cpumask_default_worker(): %i\n", ret);
169 test_options->num_cpu = num_cpu;
177 static int create_shm(test_global_t *global)
183 test_options_t *test_options = &global->test_options;
184 uint32_t num_round = test_options->num_round;
185 uint32_t num_cpu = test_options->num_cpu;
186 uint64_t data_len = test_options->data_len;
187 uint32_t shm_flags = test_options->shm_flags;
188 int private = test_options->private;
189 char name[] =
"mem_perf_00";
195 printf(
"\nMemory performance test\n");
196 printf(
" num cpu %u\n", num_cpu);
197 printf(
" num rounds %u\n", num_round);
198 printf(
" data len %" PRIu64
"\n", data_len);
199 printf(
" memory footprint %" PRIu64
"\n", num_shm * data_len);
200 printf(
" shm flags 0x%x\n", shm_flags);
201 printf(
" num shm %u\n", num_shm);
202 printf(
" private %i\n",
private);
203 printf(
" mode %i\n", test_options->mode);
206 ODPH_ERR(
"SHM capa failed.\n");
211 ODPH_ERR(
"Data len too large. Maximum len is %" PRIu64
"\n", shm_capa.
max_size);
216 ODPH_ERR(
"Too many SHM blocks. Maximum is %u\n", shm_capa.
max_blocks);
220 for (i = 0; i < num_shm; i++) {
221 name[9] =
'0' + i / 10;
222 name[10] =
'0' + i % 10;
227 ODPH_ERR(
"SHM[%u] reserve failed.\n", i);
231 global->shm[i] = shm;
235 ODPH_ERR(
"SHM[%u] addr failed.\n", i);
239 global->shm_addr[i] = addr;
241 printf(
" shm addr[%u] %p\n", i, addr);
245 global->num_shm = num_shm;
252 static int free_shm(test_global_t *global)
256 for (i = 0; i < global->num_shm; i++) {
258 ODPH_ERR(
"SHM[%u] free failed.\n", i);
266 static int run_test(
void *arg)
272 test_thread_ctx_t *thread_ctx = arg;
273 test_global_t *global = thread_ctx->global;
274 test_options_t *test_options = &global->test_options;
275 uint32_t num_round = test_options->num_round;
276 uint64_t data_len = test_options->data_len;
277 uint64_t half_len = data_len / 2;
278 int mode = test_options->mode;
279 uint8_t *addr = thread_ctx->shm_addr;
289 for (i = 0; i < num_round; i++)
290 memset(addr, thr + i, data_len);
292 for (i = 0; i < num_round; i++) {
294 memcpy(&addr[half_len], addr, half_len);
296 memcpy(addr, &addr[half_len], half_len);
305 thread_ctx->nsec = nsec;
310 static int start_workers(test_global_t *global,
odp_instance_t instance)
312 odph_thread_common_param_t param;
314 test_options_t *test_options = &global->test_options;
315 int num_cpu = test_options->num_cpu;
316 odph_thread_param_t thr_param[num_cpu];
318 odph_thread_common_param_init(¶m);
319 param.instance = instance;
320 param.cpumask = &global->cpumask;
322 for (i = 0; i < num_cpu; i++) {
323 test_thread_ctx_t *thread_ctx = &global->thread_ctx[i];
325 thread_ctx->global = global;
326 thread_ctx->shm_addr = global->shm_addr[0];
327 if (global->test_options.private)
328 thread_ctx->shm_addr = global->shm_addr[i];
330 odph_thread_param_init(&thr_param[i]);
332 thr_param[i].start = run_test;
333 thr_param[i].arg = thread_ctx;
336 ret = odph_thread_create(global->thread_tbl, ¶m, thr_param, num_cpu);
337 if (ret != num_cpu) {
338 ODPH_ERR(
"Failed to create all threads %i\n", ret);
345 static void print_stat(test_global_t *global)
350 test_options_t *test_options = &global->test_options;
351 int num_cpu = test_options->num_cpu;
352 uint32_t num_round = test_options->num_round;
353 uint64_t data_len = test_options->data_len;
354 uint64_t nsec_sum = 0;
357 nsec_sum += global->thread_ctx[i].nsec;
360 printf(
"No results.\n");
364 data_touch = num_round * data_len;
365 nsec_ave = nsec_sum / num_cpu;
368 printf(
"RESULTS - per thread (MB per sec):\n");
369 printf(
"----------------------------------\n");
370 printf(
" 1 2 3 4 5 6 7 8 9 10");
373 if (global->thread_ctx[i].nsec) {
377 printf(
"%8.1f ", data_touch / (global->thread_ctx[i].nsec / 1000.0));
383 printf(
"RESULTS - average over %i threads:\n", num_cpu);
384 printf(
"----------------------------------\n");
385 printf(
" duration: %.6f sec\n", nsec_ave / 1000000000);
386 printf(
" bandwidth per cpu: %.3f MB/s\n", data_touch / (nsec_ave / 1000.0));
387 printf(
" total bandwidth: %.3f MB/s\n", (num_cpu * data_touch) / (nsec_ave / 1000.0));
391 int main(
int argc,
char **argv)
393 odph_helper_options_t helper_options;
397 test_global_t *global;
400 argc = odph_parse_options(argc, argv);
401 if (odph_options(&helper_options)) {
402 ODPH_ERR(
"Reading ODP helper options failed.\n");
416 init.
mem_model = helper_options.mem_model;
420 ODPH_ERR(
"Global init failed.\n");
426 ODPH_ERR(
"Local init failed.\n");
430 shm =
odp_shm_reserve(
"mem_perf_global",
sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
432 ODPH_ERR(
"Shared mem reserve failed.\n");
437 if (global == NULL) {
438 ODPH_ERR(
"Shared mem alloc failed\n");
442 memset(global, 0,
sizeof(test_global_t));
444 if (parse_options(argc, argv, &global->test_options))
449 if (set_num_cpu(global))
452 if (create_shm(global))
456 if (start_workers(global, instance))
460 odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
464 if (free_shm(global))
468 ODPH_ERR(
"Shared mem free failed.\n");
473 ODPH_ERR(
"term local failed.\n");
478 ODPH_ERR(
"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_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
void odp_shm_print_all(void)
Print all shared memory blocks.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#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.
void odp_sys_info_print(void)
Print system info.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
odp_time_t odp_time_local(void)
Current local time.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
Shared memory capabilities.
uint32_t max_blocks
Maximum number of shared memory blocks.
uint64_t max_size
Maximum memory block size in bytes.
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()