API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_ml_perf.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2024 Nokia
3 */
4
13#include <odp_api.h>
14#include <odp/helper/odph_api.h>
15#include <errno.h>
16#include <inttypes.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21/* Max number of inputs and outputs */
22#define MAX_IO 8
23
24#define TEST_SKIP 77
25
26enum {
27 MODE_INFERENCE = 0,
28 MODE_INFERENCE_QUANT,
29 MODE_CREATE,
30 MODE_LOAD,
31 MODE_NUM,
32};
33
34typedef struct {
35 int mode, num_threads, rounds, latency, warmup;
36 char *model_name, *input_name, *reference_name;
37 float scale_q, scale_d;
38 int num_batch;
39} test_opt_t;
40
41static test_opt_t opt_def = {
42 .num_threads = 1,
43 .rounds = 50,
44 .warmup = 5,
45 .num_batch = 1,
46};
47
48typedef struct io_size {
49 uint64_t elems, size;
50 int elem_size;
51} io_size;
52
53typedef struct ODP_ALIGNED_CACHE {
54 uint64_t sum, min, max;
55} stat_t;
56
57typedef struct {
58 test_opt_t opt;
59 odp_barrier_t barrier;
60 odp_shm_t model_file_shm;
61 void *model_file_data;
62 uint64_t model_file_size;
63 odp_shm_t inp_file_shm;
64 void *inp_file_data;
65 uint64_t inp_file_size;
66 odp_shm_t ref_file_shm;
67 void *ref_file_data;
68 uint64_t ref_file_size;
72 int num_inp, num_out;
73 odp_ml_input_info_t inp_info[MAX_IO];
74 io_size inp[MAX_IO];
75 odp_ml_output_info_t out_info[MAX_IO];
76 io_size out[MAX_IO];
77 uint64_t inp_size_q, inp_size_d, out_size_q, out_size_d;
78 stat_t stat[ODP_THREAD_COUNT_MAX];
79} test_global_t;
80
81static test_global_t *glb;
82
83static void time_start(odp_time_t *t)
84{
86}
87
88static void time_elapsed(odp_time_t *t, int thr)
89{
91 uint64_t nsec = odp_time_diff_ns(stop, *t);
92
93 *t = stop;
94 glb->stat[thr].sum += nsec;
95 if (nsec < glb->stat[thr].min || !glb->stat[thr].min)
96 glb->stat[thr].min = nsec;
97 if (nsec > glb->stat[thr].max)
98 glb->stat[thr].max = nsec;
99}
100
101static void *read_file(const char *name, uint64_t *size)
102{
103 void *addr = NULL;
104 FILE *file = fopen(name, "rb");
105
106 if (!file) {
107 ODPH_ERR("Failed to open file %s: %s\n", name, strerror(errno));
108 return NULL;
109 }
110
111 if (fseek(file, 0, SEEK_END)) {
112 ODPH_ERR("Failed to get file size for file %s\n", name);
113 goto error;
114 }
115
116 long pos = ftell(file);
117
118 if (pos < 0) {
119 ODPH_ERR("Failed to get file size for file %s\n", name);
120 goto error;
121 }
122
123 rewind(file);
124 *size = pos;
125 addr = malloc(*size);
126
127 if (!addr) {
128 ODPH_ERR("Allocating %" PRIu64 " bytes failed\n", *size);
129 goto error;
130 }
131
132 if (fread(addr, *size, 1, file) != 1) {
133 ODPH_ERR("Reading %" PRIu64 " bytes failed\n", *size);
134 goto error;
135 }
136
137 fclose(file);
138 printf("Read %" PRIu64 " bytes from %s\n", *size, name);
139
140 return addr;
141
142error:
143 fclose(file);
144 free(addr);
145
146 return NULL;
147}
148
149static int read_file_to_shm(odp_shm_t *shm, void **data, uint64_t *size, const char *filename,
150 const char *name)
151{
152 void *file = read_file(filename, size);
153
154 if (!file)
155 return -1;
156
157 *shm = odp_shm_reserve(name, *size, ODP_CACHE_LINE_SIZE, 0);
158
159 if (*shm != ODP_SHM_INVALID)
160 *data = odp_shm_addr(*shm);
161
162 if (!*data) {
163 ODPH_ERR("Failed to reserve shm for %s\n", filename);
164 free(file);
165 return -1;
166 }
167
168 memcpy(*data, file, *size);
169 free(file);
170
171 return 0;
172}
173
174static void usage(const char *prog)
175{
176 printf("\n"
177 "Usage: %s [options]\n"
178 "\n"
179 "Mandatory OPTIONS:\n"
180 " -M, --model Model file\n"
181 " -I, --input Input file\n"
182 "\n"
183 "Optional OPTIONS\n"
184 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default 1.\n"
185 " -r, --rounds Number of test rounds. Default %u.\n"
186 " -m, --mode Test mode. Default 0.\n"
187 " 0: Inference\n"
188 " 1: Quantization-inference-dequantization\n"
189 " 2: Create-destroy\n"
190 " 3: Load-unload\n"
191 " -l, --latency Measure each round, report min, avg, max\n"
192 " -w, --warmup Warmup rounds. Default %d.\n"
193 " -R, --reference Reference file. To verify correctness, output from the last\n"
194 " inference is compared to this file.\n"
195 " -q, --quant Quantization scale\n"
196 " -d, --dequant Dequantization scale\n"
197 " -b, --batches Number of batches\n"
198 " -h, --help Help\n"
199 "\n",
200 prog, opt_def.rounds, opt_def.warmup);
201}
202
203static int parse_args(int argc, char *argv[])
204{
205 static const struct option longopts[] = {
206 { "model", required_argument, NULL, 'M' },
207 { "input", required_argument, NULL, 'I' },
208 { "num_cpu", required_argument, NULL, 'c' },
209 { "rounds", required_argument, NULL, 'r' },
210 { "mode", required_argument, NULL, 'm' },
211 { "latency", no_argument, NULL, 'l' },
212 { "warmup", required_argument, NULL, 'w' },
213 { "reference", required_argument, NULL, 'R' },
214 { "quant", required_argument, NULL, 'q' },
215 { "dequant", required_argument, NULL, 'd' },
216 { "batches", required_argument, NULL, 'b' },
217 { "help", no_argument, NULL, 'h' },
218 { NULL, 0, NULL, 0 } };
219
220 static const char *shortopts = "+M:I:c:r:m:lw:R:q:d:b:h";
221
222 glb->opt = opt_def;
223
224 while (1) {
225 int c = getopt_long(argc, argv, shortopts, longopts, NULL);
226
227 if (c == -1)
228 break; /* No more options */
229
230 switch (c) {
231 case 'M':
232 glb->opt.model_name = optarg;
233 break;
234 case 'I':
235 glb->opt.input_name = optarg;
236 break;
237 case 'c':
238 glb->opt.num_threads = atoi(optarg);
239 break;
240 case 'r':
241 glb->opt.rounds = atoi(optarg);
242 break;
243 case 'm':
244 glb->opt.mode = atoi(optarg);
245 break;
246 case 'l':
247 glb->opt.latency = 1;
248 break;
249 case 'w':
250 glb->opt.warmup = atoi(optarg);
251 break;
252 case 'R':
253 glb->opt.reference_name = optarg;
254 break;
255 case 'q':
256 glb->opt.scale_q = atof(optarg);
257 break;
258 case 'd':
259 glb->opt.scale_d = atof(optarg);
260 break;
261 case 'b':
262 glb->opt.num_batch = atoi(optarg);
263 break;
264 case 'h':
265 usage(argv[0]);
266 return 1;
267 default:
268 usage(argv[0]);
269 return -1;
270 }
271 }
272
273 optind = 1; /* reset 'extern optind' from the getopt lib */
274
275 if (!glb->opt.model_name || !glb->opt.input_name) {
276 ODPH_ERR("Model and input files are mandatory\n");
277 exit(EXIT_FAILURE);
278 }
279
280 if (glb->opt.mode < 0 || glb->opt.mode >= MODE_NUM) {
281 ODPH_ERR("Invalid mode %d\n", glb->opt.mode);
282 exit(EXIT_FAILURE);
283 }
284
285 if (glb->opt.warmup < 0) {
286 ODPH_ERR("Invalid number of warmup rounds: %d\n", glb->opt.warmup);
287 exit(EXIT_FAILURE);
288 }
289
290 printf("Options:\n");
291 printf("--------\n");
292 printf("model_name: %s\n", glb->opt.model_name);
293 printf("input_name: %s\n", glb->opt.input_name);
294 printf("num_cpu: %d\n", glb->opt.num_threads);
295 printf("rounds: %u\n", glb->opt.rounds);
296 printf("mode: %u\n", glb->opt.mode);
297 printf("latency: %d\n", glb->opt.latency);
298 printf("warmup: %d\n", glb->opt.warmup);
299 printf("reference_name: %s\n", glb->opt.reference_name);
300 printf("scale_q: %g\n", glb->opt.scale_q);
301 printf("scale_d: %g\n", glb->opt.scale_d);
302 printf("num_batch: %d\n", glb->opt.num_batch);
303 printf("\n");
304
305 return 0;
306}
307
308static int check_num_batch(void)
309{
310 int min_batch = 1, max_batch = 1;
311
312 for (int i = 0; i < glb->num_inp; i++) {
313 odp_ml_shape_info_t *shape = &glb->inp_info[i].shape;
314
315 for (int j = 0; j < (int)shape->num_dim; j++) {
316 if (shape->dim[j] == ODP_ML_DIM_DYNAMIC) {
317 min_batch = shape->dim_min[j];
318 max_batch = shape->dim_max[j];
319 break;
320 }
321 }
322 }
323
324 if (glb->opt.num_batch < min_batch || glb->opt.num_batch > max_batch) {
325 ODPH_ERR("Number of batches %d out of range [%d, %d]\n", glb->opt.num_batch,
326 min_batch, max_batch);
327 return -1;
328 }
329
330 return 0;
331}
332
333static void calc_io_size(void)
334{
335 for (int i = 0; i < glb->num_inp; i++) {
336 uint64_t elems = 1;
337 odp_ml_input_info_t *info = &glb->inp_info[i];
338 odp_ml_shape_info_t *shape = &info->shape;
339 io_size *inp = &glb->inp[i];
340
341 printf("Input %d: %s, shape:", i, info->name);
342
343 for (int j = 0; j < (int)shape->num_dim; j++) {
344 printf(" %d", shape->dim[j]);
345 if (shape->dim[j] != ODP_ML_DIM_DYNAMIC)
346 elems *= shape->dim[j];
347 }
348
349 if (shape->type == ODP_ML_SHAPE_BATCH)
350 elems *= glb->opt.num_batch;
351 inp->elems = elems;
352 inp->elem_size = info->data_type_size;
353 inp->size = elems * info->data_type_size;
354 glb->inp_size_q += inp->size;
355 glb->inp_size_d += elems * sizeof(float);
356
357 printf(", elems: %" PRIu64 ", datatype size: %d, size: %" PRIu64 "\n", inp->elems,
358 inp->elem_size, inp->size);
359 }
360
361 printf("Input size_q: %" PRIu64 ", size_d: %" PRIu64 "\n", glb->inp_size_q,
362 glb->inp_size_d);
363
364 for (int i = 0; i < glb->num_out; i++) {
365 uint64_t elems = 1;
366 odp_ml_output_info_t *info = &glb->out_info[i];
367 odp_ml_shape_info_t *shape = &info->shape;
368 io_size *out = &glb->out[i];
369
370 printf("Output %d: %s, shape:", i, info->name);
371
372 for (int j = 0; j < (int)shape->num_dim; j++) {
373 printf(" %d", shape->dim[j]);
374 if (shape->dim[j] != ODP_ML_DIM_DYNAMIC)
375 elems *= shape->dim[j];
376 }
377
378 if (shape->type == ODP_ML_SHAPE_BATCH)
379 elems *= glb->opt.num_batch;
380 out->elems = elems;
381 out->elem_size = info->data_type_size;
382 out->size = elems * info->data_type_size;
383 glb->out_size_q += out->size;
384 glb->out_size_d += elems * sizeof(float);
385
386 printf(", elems: %" PRIu64 ", datatype size: %d, size: %" PRIu64 "\n", out->elems,
387 out->elem_size, out->size);
388 }
389
390 printf("Output size_q: %" PRIu64 ", size_d: %" PRIu64 "\n", glb->out_size_q,
391 glb->out_size_d);
392}
393
394static int data_type_supported(odp_ml_data_type_t data_type)
395{
396 switch (data_type) {
400 return 1;
401 default:
402 return 0;
403 }
404}
405
406static void quantize_input(uint8_t *inp_q_addr, float *inp_d_addr)
407{
408 for (int i = 0; i < glb->num_inp; i++) {
409 float scale_q = glb->opt.scale_q;
410 uint64_t elems = glb->inp[i].elems;
411 odp_ml_data_type_t data_type = glb->inp_info[i].data_type;
412
413 switch (data_type) {
415 odp_ml_fp32_to_int8((int8_t *)inp_q_addr, inp_d_addr, elems,
416 scale_q, 0);
417 break;
419 odp_ml_fp32_to_uint8((uint8_t *)inp_q_addr, inp_d_addr, elems,
420 scale_q, 0);
421 break;
423 odp_ml_fp32_to_fp16((uint16_t *)(void *)inp_q_addr, inp_d_addr, elems);
424 break;
425 default:
426 ODPH_ERR("Unsupported type %d for input %d\n", data_type, i);
427 }
428
429 inp_q_addr += glb->inp[i].size;
430 inp_d_addr += elems;
431 }
432}
433
434static void dequantize_output(float *out_d_addr, uint8_t *out_q_addr)
435{
436 for (int i = 0; i < glb->num_out; i++) {
437 float scale_d = glb->opt.scale_d;
438 uint64_t elems = glb->out[i].elems;
439 odp_ml_data_type_t data_type = glb->out_info[i].data_type;
440
441 switch (data_type) {
443 odp_ml_fp32_from_int8(out_d_addr, (int8_t *)out_q_addr, elems,
444 scale_d, 0);
445 break;
447 odp_ml_fp32_from_uint8(out_d_addr, (uint8_t *)out_q_addr, elems,
448 scale_d, 0);
449 break;
451 odp_ml_fp32_from_fp16(out_d_addr, (uint16_t *)(void *)out_q_addr, elems);
452 break;
453 default:
454 ODPH_ERR("Unsupported type %d for output %d\n", data_type, i);
455 }
456
457 out_q_addr += glb->out[i].size;
458 out_d_addr += elems;
459 }
460}
461
462static int test_ml(void *ptr)
463{
465 odp_ml_data_seg_t inp_seg[MAX_IO];
466 uint8_t *inp_addr;
467 odp_ml_data_seg_t out_seg[MAX_IO];
468 uint8_t *out_addr;
469 int thread_idx = (int)(uintptr_t)ptr;
470 void *input = NULL, *output = NULL, *output_file = NULL;
471 void *output_final = NULL;
472 uint64_t out_size_final = 0;
473 int ret = 0;
474
475 inp_addr = glb->inp_file_data;
476
477 if (glb->opt.scale_q > 0.0) {
478 input = malloc(glb->inp_size_q);
479 if (!input) {
480 ODPH_ERR("Allocating %" PRIu64 " bytes failed\n", glb->inp_size_q);
481 ret = -1;
482 goto error;
483 }
484
485 inp_addr = input;
486 }
487
488 for (int i = 0; i < glb->num_inp; i++) {
489 inp_seg[i].addr = inp_addr;
490 inp_seg[i].size = glb->inp[i].size;
491 inp_addr += glb->inp[i].size;
492 }
493
494 output = malloc(glb->out_size_q);
495
496 if (!output) {
497 ODPH_ERR("Allocating %" PRIu64 " bytes failed\n", glb->out_size_q);
498 ret = -1;
499 goto error;
500 }
501
502 out_addr = output;
503 output_final = output;
504 out_size_final = glb->out_size_q;
505
506 if (glb->opt.scale_d > 0.0) {
507 output_file = malloc(glb->out_size_d);
508 if (!output_file) {
509 ODPH_ERR("Allocating %" PRIu64 " bytes failed\n", glb->out_size_d);
510 ret = -1;
511 goto error;
512 }
513
514 output_final = output_file;
515 out_size_final = glb->out_size_d;
516 }
517
518 for (int i = 0; i < glb->num_out; i++) {
519 out_seg[i].addr = out_addr;
520 out_seg[i].size = glb->out[i].size;
521 out_addr += glb->out[i].size;
522 }
523
524 odp_ml_data_t data = {
525 .input_seg = inp_seg,
526 .num_input_seg = glb->num_inp,
527 .output_seg = out_seg,
528 .num_output_seg = glb->num_out,
529 };
530 odp_ml_run_param_t run_param;
531
532 odp_ml_run_param_init(&run_param);
533 run_param.batch_size = glb->opt.num_batch;
534
535 if (glb->opt.mode != MODE_INFERENCE_QUANT && glb->opt.scale_q > 0.0)
536 quantize_input(input, glb->inp_file_data);
537
538 odp_barrier_wait(&glb->barrier);
539
540 for (int i = 0; i < glb->opt.rounds + glb->opt.warmup; i++) {
541 if (i == glb->opt.warmup)
542 time_start(&time);
543
544 if (glb->opt.mode == MODE_INFERENCE_QUANT && glb->opt.scale_q > 0.0)
545 quantize_input(input, glb->inp_file_data);
546
547 int r;
548
549 while (!(r = odp_ml_run(glb->mdl, &data, &run_param)))
550 ;
551 if (r != 1) {
552 ODPH_ERR("odp_ml_run() failed\n");
553 ret = -1;
554 goto error;
555 }
556
557 if (glb->opt.mode == MODE_INFERENCE_QUANT && glb->opt.scale_d > 0.0)
558 dequantize_output(output_file, output);
559
560 if (glb->opt.latency && i >= glb->opt.warmup)
561 time_elapsed(&time, thread_idx);
562 }
563
564 if (!glb->opt.latency)
565 time_elapsed(&time, thread_idx);
566
567 if (glb->opt.mode != MODE_INFERENCE_QUANT && glb->opt.scale_d > 0.0)
568 dequantize_output(output_file, output);
569
570 if (glb->ref_file_data) {
571 if (out_size_final != glb->ref_file_size) {
572 ODPH_ERR("Output size mismatch: %" PRIu64
573 " differs from reference file size %" PRIu64 "\n",
574 out_size_final, glb->ref_file_size);
575 ret = -1;
576 goto error;
577 }
578
579 if (memcmp(glb->ref_file_data, output_final, out_size_final)) {
580 ODPH_ERR("Output differs from reference\n");
581 ret = -1;
582 goto error;
583 }
584 }
585
586error:
587 free(input);
588 free(output);
589 free(output_file);
590
591 return ret;
592}
593
594static int test_ml_create(void *ptr)
595{
597 int thread_idx = (int)(uintptr_t)ptr;
598 odp_ml_model_t mdl;
599 odp_ml_model_param_t model_param;
600 int ret = 0;
601
602 odp_ml_model_param_init(&model_param);
603 model_param.model = glb->model_file_data;
604 model_param.size = glb->model_file_size;
605
606 odp_barrier_wait(&glb->barrier);
607
608 for (int i = 0; i < glb->opt.rounds + glb->opt.warmup; i++) {
609 if (i == glb->opt.warmup)
610 time_start(&time);
611
612 mdl = odp_ml_model_create(glb->opt.model_name, &model_param);
613 if (mdl == ODP_ML_MODEL_INVALID) {
614 ODPH_ERR("odp_ml_model_create() failed\n");
615 ret = -1;
616 goto error;
617 }
618
619 if (odp_ml_model_destroy(mdl)) {
620 ODPH_ERR("odp_ml_model_destroy() failed\n");
621 ret = -1;
622 goto error;
623 }
624
625 if (glb->opt.latency && i >= glb->opt.warmup)
626 time_elapsed(&time, thread_idx);
627 }
628
629 if (!glb->opt.latency)
630 time_elapsed(&time, thread_idx);
631
632error:
633 return ret;
634}
635
636static int test_ml_load(void *ptr)
637{
639 int thread_idx = (int)(uintptr_t)ptr;
640 odp_ml_model_t mdl;
641 odp_ml_model_param_t model_param;
642 int ret = 0;
643
644 odp_ml_model_param_init(&model_param);
645 model_param.model = glb->model_file_data;
646 model_param.size = glb->model_file_size;
647
648 mdl = odp_ml_model_create(glb->opt.model_name, &model_param);
649 if (mdl == ODP_ML_MODEL_INVALID) {
650 ODPH_ERR("odp_ml_model_create() failed\n");
651 ret = -1;
652 goto error;
653 }
654
655 odp_barrier_wait(&glb->barrier);
656
657 for (int i = 0; i < glb->opt.rounds + glb->opt.warmup; i++) {
658 if (i == glb->opt.warmup)
659 time_start(&time);
660
661 if (odp_ml_model_load(mdl, NULL)) {
662 ODPH_ERR("odp_ml_model_load() failed\n");
663 ret = -1;
664 goto error;
665 }
666
667 if (odp_ml_model_unload(mdl, NULL)) {
668 ODPH_ERR("odp_ml_model_unload() failed\n");
669 ret = -1;
670 goto error;
671 }
672
673 if (glb->opt.latency && i >= glb->opt.warmup)
674 time_elapsed(&time, thread_idx);
675 }
676
677 if (!glb->opt.latency)
678 time_elapsed(&time, thread_idx);
679
680 if (odp_ml_model_destroy(mdl)) {
681 ODPH_ERR("odp_ml_model_destroy() failed\n");
682 ret = -1;
683 goto error;
684 }
685
686error:
687 return ret;
688}
689
690static void print_results_avg(void)
691{
692 int num_threads = glb->opt.num_threads;
693 int rounds = glb->opt.rounds;
694
695 printf("thread %15s %15s\n", "avg (nsec)", "rounds / sec");
696 printf("--------------------------------------\n");
697 if (num_threads > 1) {
698 uint64_t avg = 0;
699
700 for (int i = 0; i < num_threads; i++)
701 avg += glb->stat[i].sum;
702
703 avg /= rounds * num_threads;
704 printf("%-6s %15" PRIu64 " %15.1f\n", "all", avg, (float)ODP_TIME_SEC_IN_NS / avg);
705 }
706 for (int i = 0; i < num_threads; i++) {
707 glb->stat[i].sum /= rounds;
708 printf("%-6d %15" PRIu64 " %15.1f\n", i, glb->stat[i].sum,
709 (float)ODP_TIME_SEC_IN_NS / glb->stat[i].sum);
710 }
711}
712
713static void print_stat(stat_t *stat)
714{
715 printf("%15" PRIu64 " %15" PRIu64 " %15" PRIu64, stat->min, stat->sum, stat->max);
716}
717
718static void print_results_min_avg_max(void)
719{
720 int num_threads = glb->opt.num_threads;
721 int rounds = glb->opt.rounds;
722
723 printf("thread %15s %15s %15s\n", "min (nsec)", "avg (nsec)", "max (nsec)");
724 printf("------------------------------------------------------\n");
725 if (num_threads > 1) {
726 stat_t s = { 0 };
727
728 for (int i = 0; i < num_threads; i++) {
729 s.sum += glb->stat[i].sum;
730 if (glb->stat[i].min && (glb->stat[i].min < s.min || !s.min))
731 s.min = glb->stat[i].min;
732 if (glb->stat[i].max > s.max)
733 s.max = glb->stat[i].max;
734 }
735
736 s.sum /= rounds * num_threads;
737 printf("%-6s ", "all");
738 print_stat(&s);
739 printf("\n");
740 }
741 for (int i = 0; i < num_threads; i++) {
742 glb->stat[i].sum /= rounds;
743 printf("%-6d ", i);
744 print_stat(&glb->stat[i]);
745 printf("\n");
746 }
747}
748
749static void print_results(void)
750{
751 printf("\n");
752
753 if (glb->opt.latency)
754 print_results_min_avg_max();
755 else
756 print_results_avg();
757
758 printf("\n");
759}
760
761int main(int argc, char *argv[])
762{
763 odp_instance_t inst;
764 odph_helper_options_t helper_options;
765 odp_init_t init;
766 odp_shm_t shm_glb;
767 int ret = 0;
768
769 /* Let helper collect its own arguments (e.g. --odph_proc) */
770 argc = odph_parse_options(argc, argv);
771
772 if (odph_options(&helper_options)) {
773 ODPH_ERR("Failed to read ODP helper options.\n");
774 exit(EXIT_FAILURE);
775 }
776
777 /* List features not to be used */
778 odp_init_param_init(&init);
779 init.not_used.feat.cls = 1;
780 init.not_used.feat.compress = 1;
781 init.not_used.feat.crypto = 1;
782 init.not_used.feat.dma = 1;
783 init.not_used.feat.ipsec = 1;
784 init.not_used.feat.schedule = 1;
785 init.not_used.feat.timer = 1;
786 init.not_used.feat.tm = 1;
787 init.mem_model = helper_options.mem_model;
788
789 /* Init ODP before calling anything else */
790 if (odp_init_global(&inst, &init, NULL)) {
791 ODPH_ERR("Global init failed.\n");
792 exit(EXIT_FAILURE);
793 }
794
795 /* Init this thread */
797 ODPH_ERR("Local init failed.\n");
798 exit(EXIT_FAILURE);
799 }
800
802
803 shm_glb = odp_shm_reserve("test_globals", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
804
805 if (shm_glb != ODP_SHM_INVALID)
806 glb = (test_global_t *)odp_shm_addr(shm_glb);
807
808 if (!glb) {
809 ODPH_ERR("Failed to reserve shm\n");
810 ret = -1;
811 goto odp_term;
812 }
813
814 memset(glb, 0, sizeof(test_global_t));
815
816 ret = parse_args(argc, argv);
817 if (ret) {
818 if (ret > 0)
819 ret = 0;
820 goto odp_term;
821 }
822
823 if (read_file_to_shm(&glb->model_file_shm, &glb->model_file_data, &glb->model_file_size,
824 glb->opt.model_name, "ml_perf_model")) {
825 ret = -1;
826 goto odp_term;
827 }
828
829 if (read_file_to_shm(&glb->inp_file_shm, &glb->inp_file_data, &glb->inp_file_size,
830 glb->opt.input_name, "ml_perf_input")) {
831 ret = -1;
832 goto odp_term;
833 }
834
835 if (glb->opt.reference_name) {
836 if (read_file_to_shm(&glb->ref_file_shm, &glb->ref_file_data, &glb->ref_file_size,
837 glb->opt.reference_name, "ml_perf_reference")) {
838 ret = -1;
839 goto odp_term;
840 }
841 }
842
843 if (odp_ml_capability(&glb->capa)) {
844 ODPH_ERR("odp_ml_capability() failed\n");
845 ret = -1;
846 goto odp_term;
847 }
848
849 if (glb->capa.max_models < 1) {
850 ODPH_ERR("ML not supported (maximum number of models is zero)\n");
851 ret = TEST_SKIP;
852 goto odp_term;
853 }
854
855 if (glb->capa.max_model_size < glb->model_file_size) {
856 ODPH_ERR("Model size %" PRIu64 " exceeds maximum %" PRIu64 "\n",
857 glb->model_file_size, glb->capa.max_model_size);
858 ret = -1;
859 goto odp_term;
860 }
861
862 if (glb->capa.min_input_align > 1) {
863 ODPH_ERR("Minimum input alignment %u not supported\n", glb->capa.min_input_align);
864 ret = -1;
865 goto odp_term;
866 }
867
868 if (glb->capa.min_output_align > 1) {
869 ODPH_ERR("Minimum output alignment %u not supported\n", glb->capa.min_output_align);
870 ret = -1;
871 goto odp_term;
872 }
873
874 if ((glb->opt.mode == MODE_CREATE || glb->opt.mode == MODE_LOAD) &&
875 (int)glb->capa.max_models < glb->opt.num_threads) {
876 ODPH_ERR("Maximum number of models %u less than number of threads %d\n",
877 glb->capa.max_models, glb->opt.num_threads);
878 ret = -1;
879 goto odp_term;
880 }
881
882 if (glb->opt.mode == MODE_LOAD && (int)glb->capa.max_models_loaded < glb->opt.num_threads) {
883 ODPH_ERR("Maximum number of models loaded %u less than number of threads %d\n",
884 glb->capa.max_models_loaded, glb->opt.num_threads);
885 ret = -1;
886 goto odp_term;
887 }
888
889 odp_ml_config_t ml_config;
890 odp_ml_model_param_t model_param;
891
892 odp_ml_config_init(&ml_config);
893 ml_config.max_model_size = glb->capa.max_model_size;
896
897 if (odp_ml_config(&ml_config)) {
898 ODPH_ERR("odp_ml_config() failed\n");
899 ret = -1;
900 goto odp_term;
901 }
902
903 odp_ml_model_param_init(&model_param);
904 model_param.model = glb->model_file_data;
905 model_param.size = glb->model_file_size;
906
907 glb->mdl = odp_ml_model_create(glb->opt.model_name, &model_param);
908 if (glb->mdl == ODP_ML_MODEL_INVALID) {
909 ODPH_ERR("odp_ml_model_create() failed\n");
910 ret = -1;
911 goto odp_term;
912 }
913
914 odp_ml_model_print(glb->mdl);
915
916 if (odp_ml_model_load(glb->mdl, NULL)) {
917 ODPH_ERR("odp_ml_model_load() failed\n");
918 ret = -1;
919 goto odp_term;
920 }
921
922 if (odp_ml_model_info(glb->mdl, &glb->info)) {
923 ODPH_ERR("odp_ml_model_info() failed\n");
924 ret = -1;
925 goto odp_term;
926 }
927
928 glb->num_inp = odp_ml_model_input_info(glb->mdl, glb->inp_info, MAX_IO);
929
930 if (glb->num_inp < 0 || glb->num_inp > MAX_IO) {
931 ODPH_ERR("odp_ml_model_input_info() failed, or too many inputs\n");
932 ret = -1;
933 goto odp_term;
934 }
935
936 glb->num_out = odp_ml_model_output_info(glb->mdl, glb->out_info, MAX_IO);
937
938 if (glb->num_out < 0 || glb->num_out > MAX_IO) {
939 ODPH_ERR("odp_ml_model_output_info() failed, or too many outputs\n");
940 ret = -1;
941 goto odp_term;
942 }
943
944 if (check_num_batch()) {
945 ret = -1;
946 goto odp_term;
947 }
948
949 calc_io_size();
950
951 if (glb->opt.scale_q > 0.0) {
952 for (int i = 0; i < glb->num_inp; i++) {
953 if (!data_type_supported(glb->inp_info[i].data_type)) {
954 ODPH_ERR("Unsupported data type %d for input %d quantization\n",
955 glb->inp_info[i].data_type, i);
956 ret = -1;
957 goto odp_term;
958 }
959 }
960 for (int i = 0; i < glb->num_out; i++) {
961 if (!data_type_supported(glb->out_info[i].data_type)) {
962 ODPH_ERR("Unsupported data type %d for output %d dequantization\n",
963 glb->out_info[i].data_type, i);
964 ret = -1;
965 goto odp_term;
966 }
967 }
968 }
969
970 if ((glb->opt.scale_q > 0.0 && glb->inp_file_size != glb->inp_size_d) ||
971 (!(glb->opt.scale_q > 0.0) && glb->inp_file_size != glb->inp_size_q)) {
972 ODPH_ERR("Input file size mismatch\n");
973 ret = -1;
974 goto odp_term;
975 }
976
977 if (glb->opt.mode != MODE_INFERENCE && glb->opt.mode != MODE_INFERENCE_QUANT) {
978 const odp_ml_model_t mdl = glb->mdl;
979
980 glb->mdl = ODP_ML_MODEL_INVALID;
981
982 if (odp_ml_model_unload(mdl, NULL)) {
983 ODPH_ERR("odp_ml_model_unload() failed\n");
984 ret = -1;
985 goto odp_term;
986 }
987
988 if (odp_ml_model_destroy(mdl)) {
989 ODPH_ERR("odp_ml_model_destroy() failed\n");
990 ret = -1;
991 goto odp_term;
992 }
993 }
994
995 int num_threads = glb->opt.num_threads;
996
997 odp_barrier_init(&glb->barrier, num_threads);
998
999 odp_cpumask_t cpumask;
1000 odph_thread_common_param_t thr_common;
1001 odph_thread_param_t thr_param[ODP_THREAD_COUNT_MAX];
1002 odph_thread_t thr_worker[ODP_THREAD_COUNT_MAX];
1003 odph_thread_join_result_t res[ODP_THREAD_COUNT_MAX];
1004
1005 if (odp_cpumask_default_worker(&cpumask, num_threads) != num_threads) {
1006 ODPH_ERR("Failed to get default CPU mask.\n");
1007 ret = -1;
1008 goto odp_term;
1009 }
1010
1011 odph_thread_common_param_init(&thr_common);
1012 thr_common.instance = inst;
1013 thr_common.cpumask = &cpumask;
1014
1015 for (int i = 0; i < num_threads; i++) {
1016 odph_thread_param_init(&thr_param[i]);
1017 thr_param[i].thr_type = ODP_THREAD_WORKER;
1018 thr_param[i].arg = (void *)(uintptr_t)i;
1019 switch (glb->opt.mode) {
1020 case MODE_INFERENCE:
1021 case MODE_INFERENCE_QUANT:
1022 thr_param[i].start = test_ml;
1023 break;
1024 case MODE_CREATE:
1025 thr_param[i].start = test_ml_create;
1026 break;
1027 case MODE_LOAD:
1028 thr_param[i].start = test_ml_load;
1029 break;
1030 }
1031 }
1032
1033 memset(&thr_worker, 0, sizeof(thr_worker));
1034
1035 if (odph_thread_create(thr_worker, &thr_common, thr_param, num_threads) != num_threads) {
1036 ODPH_ERR("Failed to create worker threads.\n");
1037 ret = -1;
1038 goto odp_term;
1039 }
1040
1041 if (odph_thread_join_result(thr_worker, res, num_threads) != num_threads) {
1042 ODPH_ERR("Failed to join worker threads.\n");
1043 ret = -1;
1044 goto odp_term;
1045 }
1046
1047 for (int i = 0; i < num_threads; i++) {
1048 if (res[i].is_sig || res[i].ret != 0) {
1049 ODPH_ERR("Worker thread failure%s: %d\n",
1050 res[i].is_sig ? " (signaled)" : "", res[i].ret);
1051 ret = -1;
1052 goto odp_term;
1053 }
1054 }
1055
1056 print_results();
1057
1058odp_term:
1059
1060 if (glb->mdl != ODP_ML_MODEL_INVALID) {
1061 if (odp_ml_model_unload(glb->mdl, NULL)) {
1062 ODPH_ERR("odp_ml_model_unload() failed\n");
1063 ret = -1;
1064 }
1065
1066 if (odp_ml_model_destroy(glb->mdl)) {
1067 ODPH_ERR("odp_ml_model_destroy() failed\n");
1068 ret = -1;
1069 }
1070 }
1071
1072 if (glb->model_file_shm != ODP_SHM_INVALID && odp_shm_free(glb->model_file_shm)) {
1073 ODPH_ERR("odp_shm_free() failed\n");
1074 ret = -1;
1075 }
1076
1077 if (glb->inp_file_shm != ODP_SHM_INVALID && odp_shm_free(glb->inp_file_shm)) {
1078 ODPH_ERR("odp_shm_free() failed\n");
1079 ret = -1;
1080 }
1081
1082 if (glb->ref_file_shm != ODP_SHM_INVALID && odp_shm_free(glb->ref_file_shm)) {
1083 ODPH_ERR("odp_shm_free() failed\n");
1084 ret = -1;
1085 }
1086
1087 if (shm_glb != ODP_SHM_INVALID && odp_shm_free(shm_glb)) {
1088 ODPH_ERR("odp_shm_free() failed\n");
1089 ret = -1;
1090 }
1091
1092 if (odp_term_local()) {
1093 ODPH_ERR("Local term failed\n");
1094 return -1;
1095 }
1096
1097 if (odp_term_global(inst)) {
1098 ODPH_ERR("Global term failed\n");
1099 return -1;
1100 }
1101
1102 return ret;
1103}
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_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
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_ml_run_param_init(odp_ml_run_param_t *param)
Initialize model run parameters.
void odp_ml_model_param_init(odp_ml_model_param_t *param)
Initialize ML model parameters.
void odp_ml_config_init(odp_ml_config_t *config)
Initialize ML configuration parameters.
void odp_ml_fp32_to_fp16(uint16_t *dst_fp16, const float *src_fp32, uint32_t num)
Quantize 32-bit float to 16-bit float.
void odp_ml_fp32_to_int8(int8_t *dst_i8, const float *src_fp32, uint32_t num, float scale, int8_t zerop)
Quantize 32-bit float to int8_t.
#define ODP_ML_DIM_DYNAMIC
Dimension size is dynamic.
void odp_ml_fp32_from_fp16(float *dst_fp32, const uint16_t *src_fp16, uint32_t num)
De-quantize 32-bit float from 16-bit float.
void odp_ml_fp32_from_uint8(float *dst_fp32, const uint8_t *src_u8, uint32_t num, float scale, uint8_t zerop)
De-quantize 32-bit float from uint8_t.
#define ODP_ML_COMPL_MODE_SYNC
Synchronous operation.
int odp_ml_config(const odp_ml_config_t *config)
Configure ML offload.
#define ODP_ML_MODEL_INVALID
Invalid ML model.
int odp_ml_run(odp_ml_model_t model, const odp_ml_data_t *data, const odp_ml_run_param_t *param)
Run the model in synchronous mode.
odp_ml_data_type_t
Model input / output data type enumeration.
uint32_t odp_ml_model_output_info(odp_ml_model_t model, odp_ml_output_info_t info[], uint32_t num)
Retrieve model output information.
odp_ml_model_t odp_ml_model_create(const char *name, const odp_ml_model_param_t *param)
Create an ML model.
void odp_ml_fp32_to_uint8(uint8_t *dst_u8, const float *src_fp32, uint32_t num, float scale, uint8_t zerop)
Quantize 32-bit float to uint8_t.
int odp_ml_model_load(odp_ml_model_t model, odp_ml_load_result_t *result)
Load ML model.
uint32_t odp_ml_model_input_info(odp_ml_model_t model, odp_ml_input_info_t info[], uint32_t num)
Retrieve model input information.
int odp_ml_model_unload(odp_ml_model_t model, odp_ml_load_result_t *result)
Unload ML model.
void odp_ml_model_print(odp_ml_model_t model)
Print debug information about the model.
int odp_ml_capability(odp_ml_capability_t *capa)
Query ML capabilities.
int odp_ml_model_info(odp_ml_model_t model, odp_ml_model_info_t *info)
Retrieve model information.
int odp_ml_model_destroy(odp_ml_model_t model)
Destroy an ML model.
void odp_ml_fp32_from_int8(float *dst_fp32, const int8_t *src_i8, uint32_t num, float scale, int8_t zerop)
De-quantize 32-bit float from int8_t.
@ ODP_ML_SHAPE_BATCH
Dynamic batch size.
@ ODP_ML_DATA_TYPE_FP16
16-bit floating point number
@ ODP_ML_DATA_TYPE_UINT8
8-bit unsigned integer
@ ODP_ML_DATA_TYPE_INT8
8-bit integer
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.
#define ODP_TIME_NULL
Zero time stamp.
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.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
Machine learning capabilities.
Machine learning configuration parameters.
uint64_t max_model_size
Maximum model binary size in bytes.
odp_ml_compl_mode_t load_mode_mask
Load / unload completion modes.
odp_ml_compl_mode_t run_mode_mask
Run completion modes.
Model input / output data segment.
void * addr
Segment start address.
uint64_t size
Segment size in bytes.
Model input / output data for a model inference run.
odp_ml_data_seg_t * input_seg
Model input data segments.
Model input information.
uint32_t data_type_size
Size of model input data type in bytes.
char name[ODP_ML_MODEL_IO_NAME_LEN]
Model input name.
odp_ml_shape_info_t shape
Model input data shape.
Model information.
Machine learning model parameters.
uint64_t size
Size of the model binary in bytes.
void * model
Model binary.
Model output information.
odp_ml_shape_info_t shape
Model output data shape.
uint32_t data_type_size
Size of model output data type in bytes.
char name[ODP_ML_MODEL_IO_NAME_LEN]
Model output name.
Parameters for model run.
uint32_t batch_size
Batch size.
Model input / output data shape information.
uint32_t dim_min[ODP_ML_MAX_DIMS]
Minimum dimension sizes.
odp_ml_shape_type_t type
Shape type.
uint32_t dim_max[ODP_ML_MAX_DIMS]
Maximum dimension sizes.
uint32_t num_dim
Number of dimensions.
uint32_t dim[ODP_ML_MAX_DIMS]
Dimension sizes.
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 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()
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()