API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_random.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2021-2024 Nokia
3 */
4
13#include <stdio.h>
14#include <string.h>
15#include <stdint.h>
16#include <inttypes.h>
17#include <getopt.h>
18
19#include <odp_api.h>
20#include <odp/helper/odph_api.h>
21
22#define PSEUDO_RANDOM (-1)
23
24#define MB (1024ull * 1024ull)
25
26typedef struct test_global_t test_global_t;
27
28typedef struct thread_arg_t {
29 test_global_t *global;
30 int thread_idx;
31 uint8_t *data;
32
33} thread_arg_t;
34
35struct test_global_t {
36 odp_barrier_t barrier;
38 uint8_t *data;
39 uint32_t rounds;
40
41 thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
42
43 struct {
44 uint64_t nsec[ODP_THREAD_COUNT_MAX];
45 uint64_t sum[ODP_THREAD_COUNT_MAX];
46 uint64_t min[ODP_THREAD_COUNT_MAX];
47 uint64_t max[ODP_THREAD_COUNT_MAX];
48 } stat;
49};
50
51/* Command line options */
52typedef struct {
53 int mode;
54 int num_threads;
55 uint32_t size;
56 uint32_t rounds;
57 uint32_t msec;
58 uint64_t delay;
59
60} options_t;
61
62static options_t options;
63static const options_t options_def = {
64 .mode = 0,
65 .num_threads = 1,
66 .size = 256,
67 .rounds = 100000,
68 .msec = 500,
69 .delay = 0,
70};
71
72static void print_usage(void)
73{
74 printf("\n"
75 "random data performance test\n"
76 "\n"
77 "Usage: odp_random [options]\n"
78 "\n"
79 " -m, --mode Test mode select (default: 0):\n"
80 " 0: Data throughput\n"
81 " 1: Data generation latency (size: 8B by default)\n"
82 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default 1.\n"
83 " -s, --size Size of buffer in bytes. Default %u.\n"
84 " -r, --rounds Number of test rounds. Default %u.\n"
85 " Divided by 100 for ODP_RANDOM_TRUE.\n"
86 " -t, --time Target test duration (msec). 0: use rounds from -r option. Default %u.\n"
87 " Based on a measurement on one thread. Test duration may be\n"
88 " significantly longer with multiple threads.\n"
89 " -d, --delay Delay (nsec) between buffer fills. Default %" PRIu64 ".\n"
90 " Affects only latency mode.\n"
91 " -h, --help This help.\n"
92 "\n",
93 options_def.size, options_def.rounds, options_def.msec, options_def.delay);
94}
95
96static int parse_options(int argc, char *argv[])
97{
98 int opt;
99 int ret = 0;
100
101 static const struct option longopts[] = {
102 { "mode", required_argument, NULL, 'm' },
103 { "num_cpu", required_argument, NULL, 'c' },
104 { "size", required_argument, NULL, 's' },
105 { "rounds", required_argument, NULL, 'r' },
106 { "time", required_argument, NULL, 't' },
107 { "delay", required_argument, NULL, 'd' },
108 { "help", no_argument, NULL, 'h' },
109 { NULL, 0, NULL, 0 }
110 };
111
112 static const char *shortopts = "+m:c:s:r:t:d:h";
113
114 options = options_def;
115 options.size = 0;
116
117 while (1) {
118 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
119
120 if (opt == -1)
121 break;
122
123 switch (opt) {
124 case 'm':
125 options.mode = atoi(optarg);
126 break;
127 case 'c':
128 options.num_threads = atol(optarg);
129 break;
130 case 's':
131 options.size = atol(optarg);
132 break;
133 case 'r':
134 options.rounds = atol(optarg);
135 break;
136 case 't':
137 options.msec = atol(optarg);
138 break;
139 case 'd':
140 options.delay = atol(optarg);
141 break;
142 case 'h':
143 /* fall through */
144 default:
145 print_usage();
146 ret = -1;
147 break;
148 }
149 }
150
151 if (options.num_threads < 1 || options.num_threads > ODP_THREAD_COUNT_MAX) {
152 ODPH_ERR("Bad number of threads: %i\n", options.num_threads);
153 return -1;
154 }
155
156 if (options.size == 0) {
157 options.size = options_def.size;
158
159 if (options.mode)
160 options.size = 8;
161 }
162
163 printf("\nOptions:\n");
164 printf("------------------------\n");
165 printf(" mode: %i\n", options.mode);
166 printf(" num_cpu: %i\n", options.num_threads);
167 printf(" size: %u\n", options.size);
168 printf(" rounds: %u\n", options.rounds);
169 printf(" time: %u\n", options.msec);
170 printf(" delay: %" PRIu64 "\n", options.delay);
171 printf("\n");
172
173 return ret;
174}
175
176static inline void random_data_loop(odp_random_kind_t type, uint32_t rounds,
177 uint8_t *data, uint32_t size)
178{
179 uint32_t i;
180 int32_t ret;
181
182 if ((int)type == PSEUDO_RANDOM) {
183 uint64_t seed = 0;
184
185 for (i = 0; i < rounds; i++) {
186 uint32_t pos = 0;
187
188 while (pos < size) {
189 ret = odp_random_test_data(data + pos, size - pos, &seed);
190
191 if (ret < 0) {
192 ODPH_ERR("odp_random_test_data() failed\n");
193 exit(EXIT_FAILURE);
194 }
195
196 pos += ret;
197 }
198 }
199 } else {
200 for (i = 0; i < rounds; i++) {
201 uint32_t pos = 0;
202
203 while (pos < size) {
204 ret = odp_random_data(data + pos, size - pos, type);
205
206 if (ret < 0) {
207 ODPH_ERR("odp_random_data() failed\n");
208 exit(EXIT_FAILURE);
209 }
210
211 pos += ret;
212 }
213 }
214 }
215}
216
217static int test_random_perf(void *ptr)
218{
219 odp_time_t start;
220 uint64_t nsec;
221 thread_arg_t *thread_arg = ptr;
222 test_global_t *global = thread_arg->global;
223 odp_random_kind_t type = global->type;
224 int thread_idx = thread_arg->thread_idx;
225 uint8_t *data = thread_arg->data;
226 uint32_t size = options.size;
227 uint32_t rounds = global->rounds;
228
229 /* One warm up round */
230 random_data_loop(type, 1, data, size);
231
232 odp_barrier_wait(&global->barrier);
233
234 /* Test run */
235 start = odp_time_local();
236
237 random_data_loop(type, rounds, data, size);
238
239 nsec = odp_time_diff_ns(odp_time_local(), start);
240
241 global->stat.nsec[thread_idx] = nsec;
242
243 return 0;
244}
245
246static inline void random_data_latency(test_global_t *global, int thread_idx,
247 uint32_t rounds, uint8_t *data, uint32_t size)
248{
249 uint32_t i;
250 int32_t ret;
251 odp_time_t t1, t2, start;
252 uint64_t nsec;
253 odp_random_kind_t type = global->type;
254 uint64_t delay = options.delay;
255 uint64_t min = UINT64_MAX;
256 uint64_t max = 0;
257 uint64_t sum = 0;
258 uint64_t seed = 0;
259
260 start = odp_time_local();
261
262 for (i = 0; i < rounds; i++) {
263 uint32_t pos = 0;
264
265 if (delay)
266 odp_time_wait_ns(delay);
267
268 if ((int)type == PSEUDO_RANDOM) {
270 while (pos < size) {
271 ret = odp_random_test_data(data + pos, size - pos, &seed);
272
273 if (ret < 0) {
274 ODPH_ERR("odp_random_test_data() failed\n");
275 exit(EXIT_FAILURE);
276 }
277
278 pos += ret;
279 }
281 } else {
283 while (pos < size) {
284 ret = odp_random_data(data + pos, size - pos, type);
285
286 if (ret < 0) {
287 ODPH_ERR("odp_random_data() failed\n");
288 exit(EXIT_FAILURE);
289 }
290
291 pos += ret;
292 }
294 }
295
296 nsec = odp_time_diff_ns(t2, t1);
297 sum += nsec;
298
299 if (nsec > max)
300 max = nsec;
301 if (nsec < min)
302 min = nsec;
303 }
304
305 nsec = odp_time_diff_ns(odp_time_local(), start);
306
307 global->stat.nsec[thread_idx] = nsec;
308 global->stat.sum[thread_idx] = sum;
309 global->stat.min[thread_idx] = min;
310 global->stat.max[thread_idx] = max;
311}
312
313static int test_random_latency(void *ptr)
314{
315 thread_arg_t *thread_arg = ptr;
316 test_global_t *global = thread_arg->global;
317 odp_random_kind_t type = global->type;
318 int thread_idx = thread_arg->thread_idx;
319 uint8_t *data = thread_arg->data;
320 uint32_t size = options.size;
321 uint32_t rounds = global->rounds;
322
323 /* One warm up round */
324 random_data_loop(type, 1, data, size);
325
326 odp_barrier_wait(&global->barrier);
327
328 /* Test run */
329 random_data_latency(global, thread_idx, rounds, data, size);
330
331 return 0;
332}
333
334static uint32_t type_rounds(test_global_t *global, odp_random_kind_t type)
335{
336 uint32_t rounds = options.rounds;
337
338 if (type == ODP_RANDOM_TRUE)
339 rounds /= 100;
340
341 if (options.msec) {
342 /*
343 * Determine number of rounds to run based on the target test
344 * duration. Random data may be very fast or very slow, so
345 * start with just one round and increase exponentially until
346 * we hit a time limit. Then use the total number of rounds we
347 * reached to calculate a suitable number of rounds for the
348 * actual test.
349 */
350
351 uint8_t *buf = (uint8_t *)malloc(options.size);
352
353 if (!buf) {
354 ODPH_ERR("malloc() failed\n");
355 exit(EXIT_FAILURE);
356 }
357
358 /* One warm up round */
359 random_data_loop(type, 1, buf, options.size);
360
361 uint32_t r = 1, tr = 0;
362 odp_time_t time;
363 odp_time_t start_time = odp_time_local();
364 odp_time_t end_time = odp_time_add_ns(start_time, 100 * ODP_TIME_MSEC_IN_NS);
365
366 while (1) {
367 if (options.mode)
368 random_data_latency(global, 0, r, buf, options.size);
369 else
370 random_data_loop(type, r, buf, options.size);
371 tr += r;
372 time = odp_time_local();
373 if (odp_time_cmp(time, end_time) >= 0)
374 break;
375 r *= 2;
376 }
377
378 rounds = (uint64_t)tr * options.msec * ODP_TIME_MSEC_IN_NS /
379 odp_time_diff_ns(time, start_time);
380 free(buf);
381 }
382
383 return ODPH_MAX(1u, rounds);
384}
385
386static void test_type(odp_instance_t instance, test_global_t *global, odp_random_kind_t type)
387{
388 int i;
389 uint32_t rounds;
390 int num_threads = options.num_threads;
391 uint32_t size = options.size;
392
393 global->type = type;
394 rounds = type_rounds(global, type);
395
396 memset(&global->stat, 0, sizeof(global->stat));
397 global->rounds = rounds;
398 odp_barrier_init(&global->barrier, num_threads);
399
400 odp_cpumask_t cpumask;
401 odph_thread_common_param_t thr_common;
402 odph_thread_param_t thr_param[num_threads];
403 odph_thread_t thr_worker[num_threads];
404
405 if (odp_cpumask_default_worker(&cpumask, num_threads) != num_threads) {
406 ODPH_ERR("Failed to get default CPU mask.\n");
407 exit(EXIT_FAILURE);
408 }
409
410 odph_thread_common_param_init(&thr_common);
411 thr_common.instance = instance;
412 thr_common.cpumask = &cpumask;
413
414 for (i = 0; i < num_threads; i++) {
415 odph_thread_param_init(&thr_param[i]);
416 thr_param[i].thr_type = ODP_THREAD_WORKER;
417 thr_param[i].arg = &global->thread_arg[i];
418
419 if (options.mode == 0)
420 thr_param[i].start = test_random_perf;
421 else
422 thr_param[i].start = test_random_latency;
423 }
424
425 memset(&thr_worker, 0, sizeof(thr_worker));
426
427 if (odph_thread_create(thr_worker, &thr_common, thr_param, num_threads) != num_threads) {
428 ODPH_ERR("Failed to create worker threads.\n");
429 exit(EXIT_FAILURE);
430 }
431
432 odph_thread_join_result_t res[num_threads];
433
434 if (odph_thread_join_result(thr_worker, res, num_threads) != num_threads) {
435 ODPH_ERR("Failed to join worker threads.\n");
436 exit(EXIT_FAILURE);
437 }
438
439 for (i = 0; i < num_threads; i++) {
440 if (res[i].is_sig || res[i].ret != 0) {
441 ODPH_ERR("Worker thread failure%s: %d\n", res[i].is_sig ?
442 " (signaled)" : "", res[i].ret);
443 exit(EXIT_FAILURE);
444 }
445 }
446
447 double mb, seconds, nsec = 0;
448
449 for (i = 0; i < num_threads; i++)
450 nsec += global->stat.nsec[i];
451
452 nsec /= num_threads;
453
454 switch (type) {
455 case ODP_RANDOM_BASIC:
456 printf("ODP_RANDOM_BASIC\n");
457 break;
459 printf("ODP_RANDOM_CRYPTO\n");
460 break;
461 case ODP_RANDOM_TRUE:
462 printf("ODP_RANDOM_TRUE\n");
463 break;
464 default:
465 printf("odp_random_test_data\n");
466 }
467
468 printf("--------------------\n");
469 printf("threads: %d size: %u B rounds: %u ", num_threads, size, rounds);
470 mb = (uint64_t)num_threads * (uint64_t)size * (uint64_t)rounds;
471 mb /= MB;
472 seconds = (double)nsec / (double)ODP_TIME_SEC_IN_NS;
473 printf("MB: %.3f seconds: %.3f ", mb, seconds);
474 printf("MB/s: %.3f ", mb / seconds);
475 printf("MB/s/thread: %.3f\n", mb / seconds / (double)num_threads);
476
477 if (options.mode) {
478 double ave;
479 uint64_t min = UINT64_MAX;
480 uint64_t max = 0;
481 uint64_t sum = 0;
482
483 printf(" latency (nsec)\n");
484 printf(" thread min max ave\n");
485 for (i = 0; i < num_threads; i++) {
486 ave = (double)global->stat.sum[i] / rounds;
487 sum += global->stat.sum[i];
488
489 if (global->stat.min[i] < min)
490 min = global->stat.min[i];
491
492 if (global->stat.max[i] > max)
493 max = global->stat.max[i];
494
495 printf("%8i %8" PRIu64 " %8" PRIu64 " %10.1f\n", i, global->stat.min[i],
496 global->stat.max[i], ave);
497 }
498
499 printf(" all %8" PRIu64 " %8" PRIu64 " %10.1f\n",
500 min, max, ((double)sum / rounds) / num_threads);
501 }
502
503 printf("\n");
504}
505
506int main(int argc, char **argv)
507{
508 odph_helper_options_t helper_options;
509 odp_instance_t instance;
510 odp_init_t init;
511 odp_shm_t shm_glb, shm_data;
512 test_global_t *global;
513 int num_threads, i;
514 uint64_t tot_size, size;
515 uint8_t *addr;
516
517 /* Let helper collect its own arguments (e.g. --odph_proc) */
518 argc = odph_parse_options(argc, argv);
519
520 if (odph_options(&helper_options)) {
521 ODPH_ERR("Failed to read ODP helper options.\n");
522 exit(EXIT_FAILURE);
523 }
524
525 if (parse_options(argc, argv))
526 exit(EXIT_FAILURE);
527
528 /* List features not to be used */
529 odp_init_param_init(&init);
530 init.not_used.feat.cls = 1;
531 init.not_used.feat.compress = 1;
532 init.not_used.feat.crypto = 1;
533 init.not_used.feat.ipsec = 1;
534 init.not_used.feat.schedule = 1;
535 init.not_used.feat.stash = 1;
536 init.not_used.feat.timer = 1;
537 init.not_used.feat.tm = 1;
538 init.mem_model = helper_options.mem_model;
539
540 /* Init ODP before calling anything else */
541 if (odp_init_global(&instance, &init, NULL)) {
542 ODPH_ERR("Global init failed.\n");
543 exit(EXIT_FAILURE);
544 }
545
546 /* Init this thread */
547 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
548 ODPH_ERR("Local init failed.\n");
549 exit(EXIT_FAILURE);
550 }
551
553
554 global = NULL;
555 shm_glb = odp_shm_reserve("test_globals", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
556
557 if (shm_glb != ODP_SHM_INVALID)
558 global = (test_global_t *)odp_shm_addr(shm_glb);
559
560 if (!global) {
561 ODPH_ERR("Failed to reserve shm\n");
562 exit(EXIT_FAILURE);
563 }
564
565 memset(global, 0, sizeof(test_global_t));
566
567 num_threads = options.num_threads;
568 addr = NULL;
569 size = ODP_CACHE_LINE_SIZE + ODP_CACHE_LINE_ROUNDUP(options.size);
570 tot_size = num_threads * size;
571 shm_data = odp_shm_reserve("test_data", tot_size, ODP_CACHE_LINE_SIZE, 0);
572
573 if (shm_data != ODP_SHM_INVALID)
574 addr = odp_shm_addr(shm_data);
575
576 if (!addr) {
577 ODPH_ERR("Failed to reserve shm: size %" PRIu64 " bytes\n", tot_size);
578 exit(EXIT_FAILURE);
579 }
580
581 for (i = 0; i < num_threads; i++) {
582 global->thread_arg[i].global = global;
583 global->thread_arg[i].thread_idx = i;
584 global->thread_arg[i].data = addr + i * size;
585 }
586
587 switch (odp_random_max_kind()) {
588 case ODP_RANDOM_TRUE:
589 test_type(instance, global, ODP_RANDOM_TRUE);
590 /* fall through */
592 test_type(instance, global, ODP_RANDOM_CRYPTO);
593 /* fall through */
594 default:
595 test_type(instance, global, ODP_RANDOM_BASIC);
596 test_type(instance, global, PSEUDO_RANDOM);
597 }
598
599 if (odp_shm_free(shm_data)) {
600 ODPH_ERR("odp_shm_free() failed\n");
601 exit(EXIT_FAILURE);
602 }
603
604 if (odp_shm_free(shm_glb)) {
605 ODPH_ERR("odp_shm_free() failed\n");
606 exit(EXIT_FAILURE);
607 }
608
609 if (odp_term_local()) {
610 ODPH_ERR("Local terminate failed.\n");
611 exit(EXIT_FAILURE);
612 }
613
614 if (odp_term_global(instance)) {
615 ODPH_ERR("Global terminate failed.\n");
616 exit(EXIT_FAILURE);
617 }
618
619 return 0;
620}
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.
#define ODP_CACHE_LINE_ROUNDUP(x)
Round up to cache line size.
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.
odp_random_kind_t odp_random_max_kind(void)
Query random max kind.
int32_t odp_random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind)
Generate random byte data.
odp_random_kind_t
Random kind selector.
int32_t odp_random_test_data(uint8_t *buf, uint32_t len, uint64_t *seed)
Generate repeatable random data for testing purposes.
@ ODP_RANDOM_BASIC
Basic random, presumably pseudo-random generated by SW.
@ ODP_RANDOM_TRUE
True random, generated from a HW entropy source.
@ ODP_RANDOM_CRYPTO
Cryptographic quality random.
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.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
@ 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(void)
Current local time.
odp_time_t odp_time_add_ns(odp_time_t time, uint64_t ns)
Add nanoseconds into time.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
odp_time_t odp_time_local_strict(void)
Current local time (strict)
int odp_time_cmp(odp_time_t t2, odp_time_t t1)
Compare two times.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
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 timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
struct odp_feature_t::@174 feat
Individual feature bits.
uint32_t schedule
Scheduler APIs, e.g., odp_schedule_xxx()
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()