aboutsummaryrefslogtreecommitdiff
path: root/test/validation/api/ml/ml.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/validation/api/ml/ml.c')
-rw-r--r--test/validation/api/ml/ml.c572
1 files changed, 572 insertions, 0 deletions
diff --git a/test/validation/api/ml/ml.c b/test/validation/api/ml/ml.c
new file mode 100644
index 000000000..5f8be1b64
--- /dev/null
+++ b/test/validation/api/ml/ml.c
@@ -0,0 +1,572 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Nokia
+ */
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+#include "odp_cunit_common.h"
+
+#define UAREA 0xaa
+#define NUM_COMPL 10u
+#define COMPL_POOL_NAME "ML compl pool"
+
+typedef struct global_t {
+ int disabled;
+ uint32_t num_compl;
+ odp_ml_capability_t ml_capa;
+} global_t;
+
+typedef struct {
+ uint32_t count;
+ uint8_t mark[NUM_COMPL];
+} uarea_init_t;
+
+static global_t global;
+
+static int ml_suite_init(void)
+{
+ memset(&global, 0, sizeof(global_t));
+
+ if (odp_ml_capability(&global.ml_capa)) {
+ ODPH_ERR("ML capability failed\n");
+ return -1;
+ }
+
+ if (global.ml_capa.max_models == 0) {
+ global.disabled = 1;
+ ODPH_DBG("ML test disabled\n");
+ return 0;
+ }
+
+ global.num_compl = ODPH_MIN(NUM_COMPL, global.ml_capa.pool.max_num);
+
+ return 0;
+}
+
+static int check_ml_support(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ return ODP_TEST_ACTIVE;
+}
+
+static void test_ml_capability(void)
+{
+ odp_ml_capability_t ml_capa;
+
+ memset(&ml_capa, 0, sizeof(odp_ml_capability_t));
+ CU_ASSERT(odp_ml_capability(&ml_capa) == 0);
+
+ if (ml_capa.max_models == 0)
+ return;
+
+ CU_ASSERT(ml_capa.max_model_size > 0);
+ CU_ASSERT(ml_capa.max_models_loaded > 0);
+ CU_ASSERT(ml_capa.max_inputs > 0);
+ CU_ASSERT(ml_capa.max_outputs > 0);
+ CU_ASSERT(ml_capa.max_segs_per_input > 0);
+ CU_ASSERT(ml_capa.max_segs_per_output > 0);
+ CU_ASSERT(ml_capa.min_input_align > 0);
+ CU_ASSERT(ml_capa.min_output_align > 0);
+
+ if ((ml_capa.load.compl_mode_mask | ml_capa.run.compl_mode_mask) &
+ ODP_ML_COMPL_MODE_EVENT) {
+ odp_pool_capability_t pool_capa;
+
+ CU_ASSERT_FATAL(odp_pool_capability(&pool_capa) == 0);
+
+ CU_ASSERT(ml_capa.pool.max_pools > 0);
+ CU_ASSERT(ml_capa.pool.max_pools <= pool_capa.max_pools);
+ CU_ASSERT(ml_capa.pool.max_num > 0);
+ CU_ASSERT(ml_capa.pool.max_cache_size >= ml_capa.pool.min_cache_size);
+ }
+
+ CU_ASSERT(ml_capa.load.compl_mode_mask);
+ CU_ASSERT(ml_capa.run.compl_mode_mask);
+
+ if (ml_capa.load.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ CU_ASSERT(ml_capa.load.compl_queue_plain || ml_capa.load.compl_queue_sched);
+
+ if (ml_capa.run.compl_mode_mask & ODP_ML_COMPL_MODE_EVENT)
+ CU_ASSERT(ml_capa.run.compl_queue_plain || ml_capa.run.compl_queue_sched);
+}
+
+static void test_ml_param(uint8_t fill)
+{
+ odp_ml_config_t config;
+ odp_ml_model_param_t model_param;
+ odp_ml_compl_pool_param_t pool_param;
+ odp_ml_compl_param_t compl_param;
+ odp_ml_run_param_t run_param;
+
+ memset(&config, fill, sizeof(config));
+ odp_ml_config_init(&config);
+ CU_ASSERT(config.max_models_created == 1);
+ CU_ASSERT(config.max_models_loaded == 1);
+ CU_ASSERT(config.load_mode_mask == 0);
+ CU_ASSERT(config.run_mode_mask == 0);
+
+ memset(&model_param, fill, sizeof(model_param));
+ odp_ml_model_param_init(&model_param);
+ CU_ASSERT(model_param.max_compl_id == 0);
+ CU_ASSERT(!model_param.extra_stat_enable);
+ CU_ASSERT(model_param.extra_param == NULL);
+ CU_ASSERT(model_param.extra_info.num_inputs == 0);
+ CU_ASSERT(model_param.extra_info.num_outputs == 0);
+ CU_ASSERT(model_param.extra_info.input_format == NULL);
+ CU_ASSERT(model_param.extra_info.output_format == NULL);
+
+ memset(&pool_param, fill, sizeof(pool_param));
+ odp_ml_compl_pool_param_init(&pool_param);
+ CU_ASSERT(pool_param.uarea_size == 0);
+ CU_ASSERT(pool_param.uarea_init.args == NULL);
+ CU_ASSERT(pool_param.uarea_init.init_fn == NULL);
+ CU_ASSERT(pool_param.cache_size <= global.ml_capa.pool.max_cache_size);
+ CU_ASSERT(pool_param.cache_size >= global.ml_capa.pool.min_cache_size);
+
+ memset(&compl_param, fill, sizeof(compl_param));
+ odp_ml_compl_param_init(&compl_param);
+ CU_ASSERT(compl_param.user_ptr == NULL);
+
+ memset(&run_param, fill, sizeof(run_param));
+ odp_ml_run_param_init(&run_param);
+ CU_ASSERT(run_param.batch_size == 0);
+ CU_ASSERT(run_param.result == NULL);
+}
+
+static void test_ml_param_init(void)
+{
+ test_ml_param(0x00);
+ test_ml_param(0xff);
+}
+
+static void test_ml_debug(void)
+{
+ odp_ml_print();
+}
+
+static void ml_compl_pool_create_max_pools(void)
+{
+ int ret;
+ uint32_t i, j;
+ odp_ml_compl_pool_param_t ml_pool_param;
+ uint32_t max_pools = global.ml_capa.pool.max_pools;
+ odp_pool_t compl_pools[max_pools];
+
+ odp_ml_compl_pool_param_init(&ml_pool_param);
+ ml_pool_param.num = global.num_compl;
+ for (i = 0; i < max_pools; i++) {
+ compl_pools[i] = odp_ml_compl_pool_create(NULL, &ml_pool_param);
+
+ if (compl_pools[i] == ODP_POOL_INVALID)
+ break;
+ }
+
+ CU_ASSERT(i == max_pools);
+
+ /* Destroy the created valid pools */
+ for (j = 0; j < i; j++) {
+ ret = odp_pool_destroy(compl_pools[j]);
+ CU_ASSERT(ret == 0);
+
+ if (ret == -1)
+ ODPH_ERR("ML completion pool destroy failed: %u / %u\n", j, i);
+ }
+}
+
+static void compl_pool_info(void)
+{
+ odp_pool_t pool;
+ odp_pool_t compl_pool;
+ odp_pool_info_t pool_info;
+ odp_ml_compl_pool_param_t pool_param;
+
+ /* Create an ML job completion pool */
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = global.num_compl;
+
+ compl_pool = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(compl_pool != ODP_POOL_INVALID);
+
+ /* Verify info about the created ML completion pool compl_pool */
+ pool = odp_pool_lookup(COMPL_POOL_NAME);
+ CU_ASSERT(pool == compl_pool);
+
+ memset(&pool_info, 0x66, sizeof(odp_pool_info_t));
+ CU_ASSERT_FATAL(odp_pool_info(compl_pool, &pool_info) == 0);
+
+ CU_ASSERT(!strcmp(pool_info.name, COMPL_POOL_NAME));
+ CU_ASSERT(pool_info.pool_ext == 0);
+ CU_ASSERT(pool_info.type == ODP_POOL_ML_COMPL);
+ CU_ASSERT(pool_info.ml_pool_param.num == NUM_COMPL);
+ CU_ASSERT(pool_info.ml_pool_param.uarea_size == 0);
+ CU_ASSERT(pool_info.ml_pool_param.cache_size == pool_param.cache_size);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(compl_pool) == 0);
+}
+
+static void compl_alloc_max(void)
+{
+ uint64_t u64;
+ odp_event_t event;
+ odp_pool_t compl_pool;
+ odp_ml_compl_pool_param_t pool_param;
+ const int num = ODPH_MIN(global.ml_capa.pool.max_num, 1000000u);
+ odp_ml_compl_t *compl = malloc(num * sizeof(odp_ml_compl_t));
+
+ CU_ASSERT_FATAL(compl != NULL);
+
+ /* Create an ML job completion pool */
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = num;
+
+ compl_pool = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(compl_pool != ODP_POOL_INVALID);
+
+ for (int i = 0; i < num; i++) {
+ compl[i] = odp_ml_compl_alloc(compl_pool);
+ CU_ASSERT_FATAL(compl[i] != ODP_ML_COMPL_INVALID);
+
+ u64 = odp_ml_compl_to_u64(compl[i]);
+ CU_ASSERT(u64 != odp_ml_compl_to_u64(ODP_ML_COMPL_INVALID));
+
+ event = odp_ml_compl_to_event(compl[i]);
+ CU_ASSERT(odp_event_type(event) == ODP_EVENT_ML_COMPL);
+ }
+
+ for (int i = 0; i < num; i++)
+ odp_ml_compl_free(compl[i]);
+
+ free(compl);
+
+ CU_ASSERT_FATAL(odp_pool_destroy(compl_pool) == 0);
+}
+
+static void compl_pool_lookup(void)
+{
+ odp_pool_t pool, pool_a, pool_b;
+ odp_ml_compl_pool_param_t pool_param;
+
+ /* Create an ML job completion pool */
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = global.num_compl;
+
+ pool_a = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(pool_a != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(COMPL_POOL_NAME);
+ CU_ASSERT(pool == pool_a);
+
+ /* Second pool with the same name */
+ pool_b = odp_ml_compl_pool_create(COMPL_POOL_NAME, &pool_param);
+ CU_ASSERT_FATAL(pool_b != ODP_POOL_INVALID);
+
+ pool = odp_pool_lookup(COMPL_POOL_NAME);
+ CU_ASSERT(pool == pool_a || pool == pool_b);
+
+ CU_ASSERT(odp_pool_destroy(pool_a) == 0);
+ CU_ASSERT(odp_pool_destroy(pool_b) == 0);
+}
+
+static int check_event_user_area(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (((global.ml_capa.load.compl_mode_mask | global.ml_capa.run.compl_mode_mask) &
+ ODP_ML_COMPL_MODE_EVENT) &&
+ (global.ml_capa.pool.max_uarea_size > 0))
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void test_ml_compl_user_area(void)
+{
+ uint32_t i;
+ void *addr;
+ void *prev;
+ odp_pool_t pool;
+ odp_ml_compl_pool_param_t pool_param;
+ uint32_t size = global.ml_capa.pool.max_uarea_size;
+ uint32_t num = global.num_compl;
+ odp_ml_compl_t compl_evs[num];
+
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.num = num;
+ pool_param.uarea_size = size;
+ pool = odp_ml_compl_pool_create(NULL, &pool_param);
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+
+ prev = NULL;
+ for (i = 0; i < num; i++) {
+ odp_event_t ev;
+ int flag = 0;
+
+ compl_evs[i] = odp_ml_compl_alloc(pool);
+
+ if (compl_evs[i] == ODP_ML_COMPL_INVALID)
+ break;
+
+ addr = odp_ml_compl_user_area(compl_evs[i]);
+
+ CU_ASSERT_FATAL(addr != NULL);
+ CU_ASSERT(prev != addr);
+
+ memset(addr, 0, size);
+
+ ev = odp_ml_compl_to_event(compl_evs[i]);
+ CU_ASSERT(odp_event_user_area(ev) == addr);
+ CU_ASSERT(odp_event_user_area_and_flag(ev, &flag) == addr);
+ CU_ASSERT(flag < 0);
+
+ prev = addr;
+ }
+ CU_ASSERT(i == num);
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_ml_compl_free(compl_evs[j]);
+
+ CU_ASSERT(odp_pool_destroy(pool) == 0);
+}
+
+static int check_event_user_area_init(void)
+{
+ if (global.disabled)
+ return ODP_TEST_INACTIVE;
+
+ if (global.ml_capa.pool.max_uarea_size > 0 && global.ml_capa.pool.uarea_persistence)
+ return ODP_TEST_ACTIVE;
+
+ return ODP_TEST_INACTIVE;
+}
+
+static void init_event_uarea(void *uarea, uint32_t size, void *args, uint32_t index)
+{
+ uarea_init_t *data = args;
+
+ data->count++;
+ data->mark[index] = 1;
+ memset(uarea, UAREA, size);
+}
+
+static void test_ml_compl_user_area_init(void)
+{
+ odp_ml_compl_pool_param_t pool_param;
+ uint32_t num = global.num_compl, i;
+ odp_pool_t pool;
+ uarea_init_t data;
+ odp_ml_compl_t compl_evs[num];
+ uint8_t *uarea;
+
+ memset(&data, 0, sizeof(uarea_init_t));
+ odp_ml_compl_pool_param_init(&pool_param);
+ pool_param.uarea_init.init_fn = init_event_uarea;
+ pool_param.uarea_init.args = &data;
+ pool_param.num = num;
+ pool_param.uarea_size = 1;
+ pool = odp_ml_compl_pool_create(NULL, &pool_param);
+
+ CU_ASSERT_FATAL(pool != ODP_POOL_INVALID);
+ CU_ASSERT(data.count == num);
+
+ for (i = 0; i < num; i++) {
+ CU_ASSERT(data.mark[i] == 1);
+
+ compl_evs[i] = odp_ml_compl_alloc(pool);
+
+ CU_ASSERT(compl_evs[i] != ODP_ML_COMPL_INVALID);
+
+ if (compl_evs[i] == ODP_ML_COMPL_INVALID)
+ break;
+
+ uarea = odp_ml_compl_user_area(compl_evs[i]);
+
+ CU_ASSERT(*uarea == UAREA);
+ }
+
+ for (uint32_t j = 0; j < i; j++)
+ odp_ml_compl_free(compl_evs[j]);
+
+ odp_pool_destroy(pool);
+}
+
+static void test_ml_fp32_to_uint8(void)
+{
+ uint8_t u8[8];
+ float fp[8] = {-20.f, -16.4f, -14.6f, -12.5f, 0, 31.4f, 80.f, 96.3f};
+ uint8_t expected[8] = {0, 0, 4, 10, 43, 127, 255, 255};
+ float scale = 0.3746f;
+ uint8_t zero_point = 43;
+
+ odp_ml_fp32_to_uint8(u8, fp, 8, scale, zero_point);
+ for (uint32_t i = 0; i < 8; i++)
+ CU_ASSERT(u8[i] == expected[i]);
+}
+
+static void test_ml_fp32_from_uint8(void)
+{
+ float fp[4];
+ float scale = 0.4f;
+ uint8_t zero_point = 43;
+ uint8_t u8[4] = {0, 43, 145, 255};
+ float expected[4] = {-17.2f, 0.0f, 40.8f, 84.8f};
+
+ odp_ml_fp32_from_uint8(fp, u8, 4, scale, zero_point);
+ for (uint32_t i = 0; i < 4; i++)
+ CU_ASSERT(fp[i] == expected[i]);
+}
+
+static void test_ml_fp32_to_int8(void)
+{
+ int8_t i8[5];
+ float scale = 0.0223f;
+ int8_t zero_point = 0;
+ float fp32[5] = {-3.4f, -2.5f, 0, 1.4f, 2.9f};
+ int8_t i8_expected[5] = {-127, -112, 0, 63, 127};
+
+ odp_ml_fp32_to_int8(i8, fp32, 5, scale, zero_point);
+
+ for (uint32_t i = 0; i < 5; i++)
+ CU_ASSERT(i8[i] == i8_expected[i]);
+}
+
+static void test_ml_fp32_to_int8_positive_zp(void)
+{
+ int8_t i8[6];
+ float scale = 0.0223f;
+ int8_t zero_point = 56;
+ float fp32[6] = {-4.1f, -3.4f, -2.5f, 0, 1.4f, 2.9f};
+ int8_t i8_expected[6] = {-127, -96, -56, 56, 119, 127};
+
+ odp_ml_fp32_to_int8(i8, fp32, 6, scale, zero_point);
+
+ for (uint32_t i = 0; i < 6; i++)
+ CU_ASSERT(i8[i] == i8_expected[i]);
+}
+
+static void test_ml_fp32_to_int8_negative_zp(void)
+{
+ int8_t i8[6];
+ float scale = 0.0223f;
+ int8_t zero_point = -56;
+ float fp32[6] = {-3.4f, -2.5f, 0, 1.4f, 2.9f, 4.1f};
+ int8_t i8_expected[6] = {-127, -127, -56, 7, 74, 127};
+
+ odp_ml_fp32_to_int8(i8, fp32, 6, scale, zero_point);
+
+ for (uint32_t i = 0; i < 6; i++)
+ CU_ASSERT(i8[i] == i8_expected[i]);
+}
+
+static void test_ml_fp32_from_int8(void)
+{
+ float fp32[6];
+ float scale = 0.05f;
+ int8_t zero_point = 56;
+ int8_t i8[6] = {-128, 46, 0, 56, 85, 127};
+ float fp32_expected[6] = {-9.2f, -0.5f, -2.8f, 0.0f, 1.45f, 3.55f};
+
+ odp_ml_fp32_from_int8(fp32, i8, 6, scale, zero_point);
+
+ for (uint32_t i = 0; i < 6; i++)
+ CU_ASSERT(fp32[i] == fp32_expected[i]);
+}
+
+static int approx_equal(double a, double b)
+{
+ const double tolerance = .01;
+
+ if (a < 0 && b < 0) {
+ a = -a;
+ b = -b;
+ }
+
+ if (a < b) {
+ double tmp = a;
+
+ a = b;
+ b = tmp;
+ }
+
+ return (a * (1 - tolerance) < b && a * (1 + tolerance) > b);
+}
+
+static void test_ml_fp32_fp16(void)
+{
+ float fp32[4];
+ uint16_t fp16[4];
+ float big = 1, small = 1;
+ const float phi = 1.618033988749;
+
+ fp32[0] = 0.0;
+ fp32[1] = -0.0;
+ memset(fp16, 1, sizeof(fp16));
+ odp_ml_fp32_to_fp16(fp16, fp32, 2);
+ memset(fp32, 1, sizeof(fp32));
+ odp_ml_fp32_from_fp16(fp32, fp16, 2);
+ CU_ASSERT(fp32[0] == 0);
+ CU_ASSERT(fp32[1] == 0);
+
+ /*
+ * 65504 is the largest normal number for fp16 with 5 exponent bits (IEEE 754-2008).
+ */
+ while (big < 65504 / 2) {
+ fp32[0] = big;
+ fp32[1] = -big;
+ fp32[2] = small;
+ fp32[3] = -small;
+ memset(fp16, 0, sizeof(fp16));
+ odp_ml_fp32_to_fp16(fp16, fp32, 4);
+ memset(fp32, 0, sizeof(fp32));
+ odp_ml_fp32_from_fp16(fp32, fp16, 4);
+ CU_ASSERT(approx_equal(fp32[0], big));
+ CU_ASSERT(approx_equal(fp32[1], -big));
+ CU_ASSERT(approx_equal(fp32[2], small));
+ CU_ASSERT(approx_equal(fp32[3], -small));
+ big *= phi;
+ small /= phi;
+ }
+}
+
+odp_testinfo_t ml_suite[] = {
+ ODP_TEST_INFO(test_ml_capability),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_param_init, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_debug, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(ml_compl_pool_create_max_pools, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(compl_pool_info, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(compl_alloc_max, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(compl_pool_lookup, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_compl_user_area, check_event_user_area),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_compl_user_area_init, check_event_user_area_init),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_uint8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_from_uint8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_int8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_int8_positive_zp, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_to_int8_negative_zp, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_from_int8, check_ml_support),
+ ODP_TEST_INFO_CONDITIONAL(test_ml_fp32_fp16, check_ml_support),
+ ODP_TEST_INFO_NULL
+};
+
+odp_suiteinfo_t ml_suites[] = {
+ {"ML", ml_suite_init, NULL, ml_suite},
+ ODP_SUITE_INFO_NULL
+};
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ /* parse common options: */
+ if (odp_cunit_parse_options(&argc, argv))
+ return -1;
+
+ ret = odp_cunit_register(ml_suites);
+
+ if (ret == 0)
+ ret = odp_cunit_run();
+
+ return ret;
+}