From f950c3c32fa5343431a749975f0ecb2dd00d5a73 Mon Sep 17 00:00:00 2001 From: yanke-xu Date: Thu, 25 Dec 2025 11:59:00 +0000 Subject: [PATCH] profiling debug golden data fix Signed-off-by: yanke-xu --- entry/src/main/cpp/CMakeLists.txt | 2 +- entry/src/main/cpp/HIAIModelManager.cpp | 66 ++- entry/src/main/cpp/HIAIModelManager.h | 34 +- entry/src/main/cpp/ProfilingTest.cpp | 90 +++- entry/src/main/cpp/fp16.cpp | 268 ++++++++++++ entry/src/main/cpp/fp16.h | 537 ++++++++++++++++++++++++ entry/src/main/ets/model/PathModel.ets | 2 + entry/src/main/ets/utils/FileUtils.ets | 113 ++++- entry/src/main/ets/view/FilePicker.ets | 64 ++- 9 files changed, 1136 insertions(+), 40 deletions(-) create mode 100644 entry/src/main/cpp/fp16.cpp create mode 100644 entry/src/main/cpp/fp16.h diff --git a/entry/src/main/cpp/CMakeLists.txt b/entry/src/main/cpp/CMakeLists.txt index c41624c..0a4daae 100644 --- a/entry/src/main/cpp/CMakeLists.txt +++ b/entry/src/main/cpp/CMakeLists.txt @@ -10,7 +10,7 @@ include_directories(${HMOS_SDK_NATIVE}/sysroot/usr/lib) message("$HMOS_SDK_NATIVE") FIND_LIBRARY(cann-lib hiai_foundation) -add_library(entry SHARED ProfilingTest.cpp HIAIModelManager.cpp) +add_library(entry SHARED ProfilingTest.cpp HIAIModelManager.cpp fp16.cpp) target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so librawfile.z.so diff --git a/entry/src/main/cpp/HIAIModelManager.cpp b/entry/src/main/cpp/HIAIModelManager.cpp index 9f7a742..4329e41 100644 --- a/entry/src/main/cpp/HIAIModelManager.cpp +++ b/entry/src/main/cpp/HIAIModelManager.cpp @@ -58,19 +58,26 @@ size_t GetDeviceID() { return deviceID; } -OH_NN_ReturnCode SetInputTensorData(std::vector &inputTensors, std::map &inputDataMap) { - size_t i = 0; - for (auto iter = inputDataMap.begin(); iter != inputDataMap.end(); iter++) { - void *data = OH_NNTensor_GetDataBuffer(inputTensors[i]); +OH_NN_ReturnCode SetInputTensorData(std::vector &inputTensors, std::vector> &inputDataMap) { + if (inputTensors.size() != inputDataMap.size()) { + OH_LOG_ERROR(LOG_APP, "Input tensors and data map size mismatch"); + return OH_NN_FAILED; + } + for (size_t irIndex = 0; irIndex < inputDataMap.size(); irIndex++) { + void *data = OH_NNTensor_GetDataBuffer(inputTensors[irIndex]); + if (data == nullptr) { + OH_LOG_ERROR(LOG_APP, "OH_NNTensor_GetDataBuffer failed, result is nullptr."); + return OH_NN_FAILED; + } size_t size = 0; - OH_NNTensor_GetSize(inputTensors[i], &size); - if (size != iter->second) { - OH_LOG_ERROR(LOG_APP, "OH_NNTensor_GetSize failed"); + OH_NNTensor_GetSize(inputTensors[irIndex], &size); + if (size != inputDataMap[irIndex].second) { + OH_LOG_ERROR(LOG_APP, "OH_NNTensor_GetSize failed %{public}zu %{public}zu %{public}zu", irIndex, size, inputDataMap[irIndex].second); return OH_NN_FAILED; } - memcpy(data, iter->first, iter->second); - i++; + memcpy(data, inputDataMap[irIndex].first, inputDataMap[irIndex].second); } + OH_LOG_INFO(LOG_APP, "SetInputTensorData success"); return OH_NN_SUCCESS; } @@ -152,7 +159,6 @@ OH_NN_ReturnCode HIAIModelManager::LoadModelFromBuffer(uint8_t *modelData, size_ OH_NNCompilation_Destroy(&compilation); return OH_NN_FAILED; } - OH_NNCompilation_Destroy(&compilation); OH_LOG_INFO(LOG_APP, "LoadModelFromBuffer success"); gettimeofday(&tEnd, nullptr); @@ -163,7 +169,11 @@ OH_NN_ReturnCode HIAIModelManager::LoadModelFromBuffer(uint8_t *modelData, size_ } OH_NN_ReturnCode HIAIModelManager::InitLabels(void *data, size_t size) { - inputDataMap_[data] = size; + if (data == nullptr) { + OH_LOG_INFO(LOG_APP, "Init labels fail, data is nullptr."); + return OH_NN_SUCCESS; + } + inputDataMap_.emplace_back(data, size); OH_LOG_INFO(LOG_APP, "Init labels success"); return OH_NN_SUCCESS; } @@ -255,8 +265,8 @@ OH_NN_ReturnCode HIAIModelManager::RunModel(double &avgRunSyncTime) { return OH_NN_SUCCESS; } -std::vector> HIAIModelManager::GetResult() { - std::vector> outputs; +std::vector> HIAIModelManager::GetResult() { + std::vector> outputs; for (auto tensor : outputTensors_) { void *tensorData = OH_NNTensor_GetDataBuffer(tensor); if (tensorData == nullptr) { @@ -270,13 +280,7 @@ std::vector> HIAIModelManager::GetResult() { break; } - float *outputResult = static_cast(tensorData); - int floatSize = 4; - std::vector output(size / floatSize, 0.0); - for (size_t i = 0; i < size / floatSize; ++i) { - output[i] = outputResult[i]; - } - outputs.push_back(output); + outputs.emplace_back(tensorData, size); } if (outputs.size() != outputTensors_.size()) { @@ -287,6 +291,24 @@ std::vector> HIAIModelManager::GetResult() { return outputs; } +std::vector HIAIModelManager::GetDataType() { + std::vector dataTypes; + for (auto tensor : outputTensors_) { + OH_NN_DataType dataType; + OH_NN_ReturnCode ret = OH_NNTensorDesc_GetDataType(OH_NNTensor_GetTensorDesc(tensor), &dataType); + if (ret != OH_NN_SUCCESS) { + OH_LOG_ERROR(LOG_APP, "Failed to get tensor data type."); + break; + } + dataTypes.push_back(dataType); + } + if (dataTypes.size() != outputTensors_.size()) { + OH_LOG_ERROR(LOG_APP, "data type size mismatch"); + dataTypes.clear(); + } + return dataTypes; +} + OH_NN_ReturnCode HIAIModelManager::UnloadModel(double &avgUnLoadTime) { struct timeval tStart {}; struct timeval tEnd {}; @@ -296,7 +318,9 @@ OH_NN_ReturnCode HIAIModelManager::UnloadModel(double &avgUnLoadTime) { inputTensors_.clear(); DestroyTensors(outputTensors_); outputTensors_.clear(); - OH_NNExecutor_Destroy(&executor_); + if (executor_ != nullptr) { + OH_NNExecutor_Destroy(&executor_); + } inputDataMap_.clear(); gettimeofday(&tEnd, nullptr); diff --git a/entry/src/main/cpp/HIAIModelManager.h b/entry/src/main/cpp/HIAIModelManager.h index 1f3bce2..157f0ce 100644 --- a/entry/src/main/cpp/HIAIModelManager.h +++ b/entry/src/main/cpp/HIAIModelManager.h @@ -19,6 +19,8 @@ #include #include #include "neural_network_runtime/neural_network_core.h" +#include +#include "fp16.h" #undef LOG_DOMAIN #define LOG_DOMAIN 0x0000 @@ -45,16 +47,44 @@ public: OH_NN_ReturnCode RunModel(double &avgRunSyncTime); - std::vector> GetResult(); + std::vector> GetResult(); + + std::vector GetDataType(); OH_NN_ReturnCode UnloadModel(double &avgRunLoadTime); + + template + float GetMold(T* vec, int count) { + float sum = 0.0; + for (int i = 0; i < count; ++i) { + sum += static_cast(vec[i]) * static_cast(vec[i]); + } + return std::sqrt(sum); + } + + template + float GetSimilarity(T* result, T* gold, int count) { + float dotProduct = 0.0; + for (int i = 0; i < count; ++i) { + dotProduct += static_cast(result[i]) * static_cast(gold[i]); + } + + float moldResult = GetMold(result, count); + float moldGold = GetMold(gold, count); + if (moldResult == 0.0f || moldGold == 0.0f) { + OH_LOG_WARN(LOG_APP, "Zero mold detected, returning 0.0f"); + return 0.0f; + } + float similarity = dotProduct / (moldResult * moldGold); + return similarity; + } private: size_t deviceID_{0}; OH_NNExecutor *executor_{nullptr}; std::vector inputTensors_; std::vector outputTensors_; - std::map inputDataMap_; + std::vector> inputDataMap_; }; #endif // HIAI_MODEL_MANAGER_H diff --git a/entry/src/main/cpp/ProfilingTest.cpp b/entry/src/main/cpp/ProfilingTest.cpp index 5def2ed..1c7176b 100644 --- a/entry/src/main/cpp/ProfilingTest.cpp +++ b/entry/src/main/cpp/ProfilingTest.cpp @@ -165,14 +165,14 @@ static napi_value getUnLoadTime(napi_env env, napi_callback_info info) { } static napi_value GetResult(napi_env env, napi_callback_info info) { - std::vector> outputData = HIAIModelManager::GetInstance().GetResult(); + std::vector> outputdata = HIAIModelManager::GetInstance().GetResult(); napi_value out = nullptr; napi_create_array(env, &out); - for (size_t i = 0; i < outputData.size(); i++) { + for (size_t i = 0; i < outputdata.size(); i++) { napi_value result; - void *resultData; - napi_create_arraybuffer(env, outputData[i].size() * sizeof(float), &resultData, &result); - memcpy(resultData, outputData[i].data(), outputData[i].size() * sizeof(float)); + void* resultData; + napi_create_arraybuffer(env, outputdata[i].second, &resultData, &result); + memcpy(resultData, outputdata[i].first, outputdata[i].second); napi_set_element(env, out, i, result); } return out; @@ -197,6 +197,84 @@ static napi_value UnloadModel(napi_env env, napi_callback_info info) { return successCode; } +static napi_value GetDataType(napi_env env, napi_callback_info info) { + std::vector dataTypes = HIAIModelManager::GetInstance().GetDataType(); + napi_value out = nullptr; + napi_create_array(env, &out); + for (size_t i = 0; i < dataTypes.size(); ++i) { + napi_value type; + napi_create_int32(env, dataTypes[i], &type); + napi_set_element(env, out, i, type); + } + return out; +} + +static napi_value CompareBuffers(napi_env env, napi_callback_info info) { + napi_status status; + size_t argc = 3; + napi_value args[3]; + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok) { + return nullptr; + } + + napi_valuetype resultType, goldType, dataTypeType; + napi_typeof(env, args[0], &resultType); + napi_typeof(env, args[1], &goldType); + napi_typeof(env, args[2], &dataTypeType); + if (resultType != napi_object || goldType != napi_object || dataTypeType != napi_number) { + napi_throw_error(env, nullptr, "Invalid argument types."); + return nullptr; + } + + void* resultData = nullptr; + size_t resultLength = 0; + status = napi_get_arraybuffer_info(env, args[0], &resultData, &resultLength); + if (status != napi_ok) { + return nullptr; + } + void* goldData = nullptr; + size_t goldLength = 0; + status = napi_get_arraybuffer_info(env, args[1], &goldData, &goldLength); + if (status != napi_ok) { + return nullptr; + } + + if (resultLength != goldLength) { + napi_throw_error(env, nullptr, "Buffer lengths do not match."); + return nullptr; + } + + int32_t dataTypeInt = 0; + status = napi_get_value_int32(env, args[2], &dataTypeInt); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "Invalid data type value."); + return nullptr; + } + OH_NN_DataType mappedType = static_cast(dataTypeInt); + + float similarity = 0.0f; + if (mappedType == OH_NN_UINT8) { + uint8_t* resultUint8 = static_cast(resultData); + uint8_t* goldUint8 = static_cast(goldData); + similarity = HIAIModelManager::GetInstance().GetSimilarity(resultUint8, goldUint8, resultLength / sizeof(uint8_t)); + } else if (mappedType == OH_NN_FLOAT16) { + fp16_t* resultFloat16 = static_cast(resultData); + fp16_t* goldFloat16 = static_cast(goldData); + similarity = HIAIModelManager::GetInstance().GetSimilarity(resultFloat16, goldFloat16, resultLength / sizeof(fp16_t)); + } else if (mappedType == OH_NN_FLOAT32) { + float* resultFloat = static_cast(resultData); + float* goldFloat = static_cast(goldData); + similarity = HIAIModelManager::GetInstance().GetSimilarity(resultFloat, goldFloat, resultLength / sizeof(float)); + } + + char resultStr[256]; + snprintf(resultStr, sizeof(resultStr), "Similarity: %.4f%%", similarity * 100.0f); + napi_value result; + napi_create_string_utf8(env, resultStr, NAPI_AUTO_LENGTH, &result); + return result; +} + EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { @@ -210,6 +288,8 @@ static napi_value Init(napi_env env, napi_value exports) { {"getInitTime", nullptr, getInitTime, nullptr, nullptr, nullptr, napi_default, nullptr}, {"getLoadTime", nullptr, getLoadTime, nullptr, nullptr, nullptr, napi_default, nullptr}, {"getUnLoadTime", nullptr, getUnLoadTime, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"CompareBuffers", nullptr, CompareBuffers, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetDataType", nullptr, GetDataType, nullptr, nullptr, nullptr, napi_default, nullptr}, }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; diff --git a/entry/src/main/cpp/fp16.cpp b/entry/src/main/cpp/fp16.cpp new file mode 100644 index 0000000..c6d9e33 --- /dev/null +++ b/entry/src/main/cpp/fp16.cpp @@ -0,0 +1,268 @@ +/** + * Copyright 2019-2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fp16.h" + +/* + * @ingroup fp16_t global filed + * @brief round mode of last valid digital + */ +fp16RoundMode_t g_RoundMode = ROUND_TO_NEAREST; + +void ExtractFP16(const uint16_t& val, uint16_t* s, int16_t* e, uint16_t* m) +{ + // 1.Extract + *s = FP16_EXTRAC_SIGN(val); + *e = FP16_EXTRAC_EXP(val); + *m = FP16_EXTRAC_MAN(val); + + // Denormal + if ((*e) == 0) { + *e = 1; + } +} + +/* + * @ingroup fp16_t static method + * @param [in] man truncated mantissa + * @param [in] shift_out left shift bits based on ten bits + * @brief judge whether to add one to the result while converting fp16_t to other datatype + * @return Return true if add one, otherwise false + */ +static bool IsRoundOne(uint64_t man, uint16_t truncLen) +{ + uint64_t mask0 = 0x4; + uint64_t mask1 = 0x2; + uint64_t mask2; + uint16_t shiftOut = truncLen - 2; + mask0 = mask0 << shiftOut; + mask1 = mask1 << shiftOut; + mask2 = mask1 - 1; + + bool lastBit = ((man & mask0) > 0); + bool truncHigh = false; + bool truncLeft = false; + if (g_RoundMode == ROUND_TO_NEAREST) { + truncHigh = ((man & mask1) > 0); + truncLeft = ((man & mask2) > 0); + } + return (truncHigh && (truncLeft || lastBit)); +} + +/* + * @ingroup fp16_t public method + * @param [in] exp exponent of fp16_t value + * @param [in] man exponent of fp16_t value + * @brief normalize fp16_t value + * @return + */ +static void Fp16Normalize(int16_t& exp, uint16_t& man) +{ + if (exp >= FP16_MAX_EXP) { + exp = FP16_MAX_EXP - 1; + man = FP16_MAX_MAN; + } else if (exp == 0 && man == FP16_MAN_HIDE_BIT) { + exp++; + man = 0; + } +} + +/* + * @ingroup fp16_t math conversion static method + * @param [in] fpVal uint16_t value of fp16_t object + * @brief Convert fp16_t to float/fp32 + * @return Return float/fp32 value of fpVal which is the value of fp16_t object + */ +static float fp16ToFloat(const uint16_t& fpVal) +{ + float ret; + + uint16_t hfSign, hfMan; + int16_t hfExp; + ExtractFP16(fpVal, &hfSign, &hfExp, &hfMan); + + while (hfMan && !(hfMan & FP16_MAN_HIDE_BIT)) { + hfMan <<= 1; + hfExp--; + } + + uint32_t sRet, eRet, mRet, fVal; + + sRet = hfSign; + if (!hfMan) { + eRet = 0; + mRet = 0; + } else { + eRet = static_cast(hfExp - FP16_EXP_BIAS + FP32_EXP_BIAS); + mRet = hfMan & FP16_MAN_MASK; + mRet = mRet << (FP32_MAN_LEN - FP16_MAN_LEN); + } + fVal = FP32_CONSTRUCTOR(sRet, eRet, mRet); + float* pfVal = reinterpret_cast(&fVal); + ret = *pfVal; + + return ret; +} + +/* + * @ingroup fp16_t math conversion static method + * @param [in] fpVal uint16_t value of fp16_t object + * @brief Convert fp16_t to uint8_t + * @return Return uint8_t value of fpVal which is the value of fp16_t object + */ +static uint8_t fp16ToUInt8(const uint16_t& fpVal) +{ + uint8_t ret; + uint8_t mRet = 0; + uint16_t hfE, hfM; + + // 1.get s_ret and shift it to bit0. + uint8_t sRet = FP16_EXTRAC_SIGN(fpVal); + // 2.get hf_e and hf_m + hfE = FP16_EXTRAC_EXP(fpVal); + hfM = FP16_EXTRAC_MAN(fpVal); + + if (FP16_IS_DENORM(fpVal) || sRet == 1) { // Denormalized number + return 0; + } + + if (FP16_IS_INVALID(fpVal)) { // Inf or NaN + mRet = ~0; + return mRet; + } + + uint64_t longIntM = hfM; + uint8_t overflowFlag = 0; + uint16_t shiftOut = 0; + + while (hfE != FP16_EXP_BIAS) { + if (hfE > FP16_EXP_BIAS) { + hfE--; + longIntM = longIntM << 1; + if (longIntM >= 0x40000Lu) { // overflow 0100 0000 0000 0000 0000 + longIntM = 0x3FFFFLu; // 11 1111 1111 1111 1111 10(fp16_t-man)+8(uint8)=18bit + overflowFlag = 1; + mRet = ~0; + break; + } + } else { + hfE++; + shiftOut++; + } + } + if (!overflowFlag) { + mRet = static_cast(((longIntM >> (FP16_MAN_LEN + shiftOut)) & BIT_LEN8_MAX)); + } + // Generate final result + ret = mRet; + + return ret; +} + +void fp16AddmRet(uint16_t& mRet, int16_t& eRet, uint32_t& mTrunc, const uint16_t& mMin, const uint16_t& mMax) +{ + while (mRet < mMin && eRet > 0) { // the value of m_ret should not be smaller than 2^23 + mRet = mRet << 1; + mRet += (FP32_SIGN_MASK & mTrunc) >> FP32_SIGN_INDEX; + mTrunc = mTrunc << 1; + eRet = eRet - 1; + } + while (mRet >= mMax) { // the value of m_ret should be smaller than 2^24 + mTrunc = mTrunc >> 1; + mTrunc = mTrunc | (FP32_SIGN_MASK * (mRet & 1)); + mRet = mRet >> 1; + eRet = eRet + 1; + } +} + +// evaluation +__attribute__((__visibility__("default"))) fp16_t& fp16_t::operator=(const fp16_t& fp) +{ + if (&fp == this) { + return *this; + } + val = fp.val; + return *this; +} + +__attribute__((__visibility__("default"))) fp16_t& fp16_t::operator=(const float& fVal) +{ + uint16_t sRet, mRet; + int16_t eRet; + uint32_t eF, mF; + uint32_t ui32V = *(reinterpret_cast(const_cast(&fVal))); // 1:8:23bit sign:exp:man + uint32_t mLenDelta; + + sRet = static_cast((ui32V & FP32_SIGN_MASK) >> FP32_SIGN_INDEX); // 4Byte->2Byte + eF = (ui32V & FP32_EXP_MASK) >> FP32_MAN_LEN; // 8 bit exponent + mF = (ui32V & FP32_MAN_MASK); // 23 bit mantissa dont't need to care about denormal + mLenDelta = FP32_MAN_LEN - FP16_MAN_LEN; + + bool needRound = false; + // Exponent overflow/NaN converts to signed inf/NaN + if (eF > 0x8Fu) { // 0x8Fu:142=127+15 + eRet = FP16_MAX_EXP - 1; + mRet = FP16_MAX_MAN; + } else if (eF <= 0x70u) { // 0x70u:112=127-15 Exponent underflow converts to denormalized half or signed zero + eRet = 0; + if (eF >= 0x67) { // 0x67:103=127-24 Denormal + mF = (mF | FP32_MAN_HIDE_BIT); + uint16_t shiftOut = FP32_MAN_LEN; + uint64_t mTmp = (static_cast(mF)) << (eF - 0x67); + + needRound = IsRoundOne(mTmp, shiftOut); + mRet = static_cast(mTmp >> shiftOut); + if (needRound) { + mRet++; + } + } else if (eF == 0x66 && mF > 0) { // 0x66:102 Denormal 0(eF - 0x70u); + + needRound = IsRoundOne(mF, mLenDelta); + mRet = static_cast(mF >> mLenDelta); + if (needRound) { + mRet++; + } + if ((mRet & FP16_MAN_HIDE_BIT) != 0) { + eRet++; + } + } + + Fp16Normalize(eRet, mRet); + val = FP16_CONSTRUCTOR(sRet, static_cast(eRet), mRet); + return *this; +} + +// convert +__attribute__((__visibility__("default"))) fp16_t::operator float() const +{ + return fp16ToFloat(val); +} + +__attribute__((__visibility__("default"))) float fp16_t::toFloat() +{ + return fp16ToFloat(val); +} + +__attribute__((__visibility__("default"))) uint8_t fp16_t::toUInt8() +{ + return fp16ToUInt8(val); +} diff --git a/entry/src/main/cpp/fp16.h b/entry/src/main/cpp/fp16.h new file mode 100644 index 0000000..a6961b2 --- /dev/null +++ b/entry/src/main/cpp/fp16.h @@ -0,0 +1,537 @@ +/** + * Copyright 2019-2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _FP16_T_H_ +#define _FP16_T_H_ + +#include +#include + +/* + * @ingroup fp16 basic parameter + * @brief fp16 exponent bias + */ +const int FP16_EXP_BIAS = 15; +/* + * @ingroup fp16 basic parameter + * @brief the exponent bit length of fp16 is 5 + */ +const int FP16_EXP_LEN = 5; +/* + * @ingroup fp16 basic parameter + * @brief the mantissa bit length of fp16 is 10 + */ +const int FP16_MAN_LEN = 10; +/* + * @ingroup fp16 basic parameter + * @brief bit index of sign in fp16 + */ +const int FP16_SIGN_INDEX = 15; +/* + * @ingroup fp16 basic parameter + * @brief sign mask of fp16 (1 00000 00000 00000) + */ +const int FP16_SIGN_MASK = 0x8000; +/* + * @ingroup fp16 basic parameter + * @brief exponent mask of fp16 ( 11111 00000 00000) + */ +const int FP16_EXP_MASK = 0x7C00; +/* + * @ingroup fp16 basic parameter + * @brief mantissa mask of fp16 ( 11111 11111) + */ +const int FP16_MAN_MASK = 0x03FF; +/* + * @ingroup fp16 basic parameter + * @brief hide bit of mantissa of fp16( 1 00000 00000) + */ +const int FP16_MAN_HIDE_BIT = 0x0400; +/* + * @ingroup fp16 basic parameter + * @brief maximum value (0111 1011 1111 1111) + */ +const int FP16_MAX = 0x7BFF; +/* + * @ingroup fp16 basic parameter + * @brief minimum value (1111 1011 1111 1111) + */ +const int FP16_MIN = 0xFBFF; +/* + * @ingroup fp16 basic parameter + * @brief absolute maximum value (0111 1111 1111 1111) + */ +const int FP16_ABS_MAX = 0x7FFF; +/* + * @ingroup fp16 basic parameter + * @brief maximum exponent value of fp16 is 15(11111) + */ +const int FP16_MAX_EXP = 0x001F; +/* + * @ingroup fp16 basic parameter + * @brief maximum valid exponent value of fp16 is 14(11110) + */ +const int FP16_MAX_VALID_EXP = 0x001E; +/* + * @ingroup fp16 basic parameter + * @brief maximum mantissa value of fp16(11111 11111) + */ +const int FP16_MAX_MAN = 0x03FF; +/* + * @ingroup fp16 basic parameter + * @brief absolute minimum normal value of fp16 + * (E=1,M=0 D=2^(-14)=0.00006103515625) + */ +#define FP16_MIN_NORMAL ((1.0f / (2 << 14))) + +/* + * @ingroup fp16 basic operator + * @brief get sign of fp16 + */ +#define FP16_EXTRAC_SIGN(x) (((x) >> 15) & 1) +/* + * @ingroup fp16 basic operator + * @brief get exponent of fp16 + */ +#define FP16_EXTRAC_EXP(x) (((x) >> 10) & FP16_MAX_EXP) +/* + * @ingroup fp16 basic operator + * @brief get mantissa of fp16 + */ +#define FP16_EXTRAC_MAN(x) ((((x) >> 0) & 0x3FF) | (((((x) >> 10) & 0x1F) > 0 ? 1 : 0) * 0x400)) +/* + * @ingroup fp16 basic operator + * @brief constructor of fp16 from sign exponent and mantissa + */ +#define FP16_CONSTRUCTOR(s, e, m) (((s) << FP16_SIGN_INDEX) | ((e) << FP16_MAN_LEN) | ((m) & FP16_MAX_MAN)) + +/* + * @ingroup fp16 special value judgment + * @brief whether a fp16 is zero + */ +#define FP16_IS_ZERO(x) (((x) & FP16_ABS_MAX) == 0) +/* + * @ingroup fp16 special value judgment + * @brief whether a fp16 is a denormalized value + */ +#define FP16_IS_DENORM(x) ((((x) & FP16_EXP_MASK) == 0)) +/* + * @ingroup fp16 special value judgment + * @brief whether a fp16 is infinite + */ +#define FP16_IS_INF(x) (((x) & FP16_ABS_MAX) == FP16_ABS_MAX) +/* + * @ingroup fp16 special value judgment + * @brief whether a fp16 is NaN + */ +#define FP16_IS_NAN(x) ((((x) & FP16_EXP_MASK) == FP16_EXP_MASK) && ((x) & FP16_MAN_MASK)) +/* + * @ingroup fp16 special value judgment + * @brief whether a fp16 is invalid + */ +#define FP16_IS_INVALID(x) (((x) & FP16_EXP_MASK) == FP16_EXP_MASK) +/* + * @ingroup fp32 basic parameter + * @brief fp32 exponent bias + */ +const int FP32_EXP_BIAS = 127; +/* + * @ingroup fp32 basic parameter + * @brief the exponent bit length of float/fp32 is 8 + */ +const int FP32_EXP_LEN = 8; +/* + * @ingroup fp32 basic parameter + * @brief the mantissa bit length of float/fp32 is 23 + */ +const int FP32_MAN_LEN = 23; +/* + * @ingroup fp32 basic parameter + * @briefbit index of sign in float/fp32 + */ +const int FP32_SIGN_INDEX = 31; +/* + * @ingroup fp32 basic parameter + * @brief sign mask of fp32 1 0000 0000 0000 0000 0000 0000 000) + */ +const unsigned int FP32_SIGN_MASK = 0x80000000u; +/* + * @ingroup fp32 basic parameter + * @brief exponent mask of fp32 ( 1111 1111 0000 0000 0000 0000 000) + */ +const unsigned int FP32_EXP_MASK = 0x7F800000u; +/* + * @ingroup fp32 basic parameter + * @brief mantissa mask of fp32 ( 1111 1111 1111 1111 111) + */ +const unsigned int FP32_MAN_MASK = 0x007FFFFFu; +/* + * @ingroup fp32 basic parameter + * @brief hide bit of mantissa of fp32 ( 1 0000 0000 0000 0000 000) + */ +const unsigned int FP32_MAN_HIDE_BIT = 0x00800000u; +/* + * @ingroup fp32 basic parameter + * @brief absolute maximum value (0 1111 1111 1111 1111 1111 1111 111) + */ +const unsigned int FP32_ABS_MAX = 0x7FFFFFFFu; +/* + * @ingroup fp32 basic parameter + * @brief maximum exponent value of fp32 is 255(1111 1111) + */ +const int FP32_MAX_EXP = 0xFF; +/* + * @ingroup fp32 basic parameter + * @brief maximum mantissa value of fp32 (1111 1111 1111 1111 1111 111) + */ +const int FP32_MAX_MAN = 0x7FFFFF; +/* + * @ingroup fp32 special value judgment + * @brief whether a fp32 is NaN + */ +#define FP32_IS_NAN(x) ((((x) & FP32_EXP_MASK) == FP32_EXP_MASK) && ((x) & FP32_MAN_MASK)) +/* + * @ingroup fp32 special value judgment + * @brief whether a fp32 is infinite + */ +#define FP32_IS_INF(x) ((((x) & FP32_EXP_MASK) == FP32_EXP_MASK) && (!((x) & FP32_MAN_MASK))) +/* + * @ingroup fp32 special value judgment + * @brief whether a fp32 is a denormalized value + */ +#define FP32_IS_DENORM(x) ((((x)&FP32_EXP_MASK) == 0)) +/* + * @ingroup fp32 basic operator + * @brief get sign of fp32 + */ +#define FP32_EXTRAC_SIGN(x) (((x) >> FP32_SIGN_INDEX) & 1) +/* + * @ingroup fp32 basic operator + * @brief get exponent of fp16 + */ +#define FP32_EXTRAC_EXP(x) (((x)&FP32_EXP_MASK) >> FP32_MAN_LEN) +/* + * @ingroup fp32 basic operator + * @brief get mantissa of fp16 + */ +#define FP32_EXTRAC_MAN(x) \ + (((x)&FP32_MAN_MASK) | (((((x) >> FP32_MAN_LEN) & FP32_MAX_EXP) > 0 ? 1 : 0) * FP32_MAN_HIDE_BIT)) +/* + * @ingroup fp32 basic operator + * @brief constructor of fp32 from sign exponent and mantissa + */ +#define FP32_CONSTRUCTOR(s, e, m) (((s) << FP32_SIGN_INDEX) | ((e) << FP32_MAN_LEN) | ((m) & FP32_MAX_MAN)) + +/* + * @ingroup fp64 basic parameter + * @brief fp64 exponent bias + */ +const int FP64_EXP_BIAS = 1023; +/* + * @ingroup fp64 basic parameter + * @brief the exponent bit length of double/fp64 is 11 + */ +const int FP64_EXP_LEN = 11; +/* + * @ingroup fp64 basic parameter + * @brief the mantissa bit length of double/fp64 is 52 + */ +const int FP64_MAN_LEN = 52; +/* + * @ingroup fp64 basic parameter + * @brief bit index of sign in double/fp64 is 63 + */ +const int FP64_SIGN_INDEX = 63; +/* + * @ingroup fp64 basic parameter + * @brief sign mask of fp64 (1 000 (total 63bits 0)) + */ +#define FP64_SIGN_MASK (0x8000000000000000LLu) +/* + * @ingroup fp64 basic parameter + * @brief exponent mask of fp64 (0 1 11111 11111 0000?-?-(total 52bits 0)) + */ +#define FP64_EXP_MASK (0x7FF0000000000000LLu) +/* + * @ingroup fp64 basic parameter + * @brief mantissa mask of fp64 ( 1111?-?-(total 52bits 1)) + */ +#define FP64_MAN_MASK (0x000FFFFFFFFFFFFFLLu) +/* + * @ingroup fp64 basic parameter + * @brief hide bit of mantissa of fp64 ( 1 0000?-?-(total 52bits 0)) + */ +#define FP64_MAN_HIDE_BIT (0x0010000000000000LLu) +/* + * @ingroup fp64 basic parameter + * @brief absolute maximum value (0 111?-?-(total 63bits 1)) + */ +#define FP64_ABS_MAX (0x7FFFFFFFFFFFFFFFLLu) +/* + * @ingroup fp64 basic parameter + * @brief maximum exponent value of fp64 is 2047(1 11111 11111) + */ +const int FP64_MAX_EXP = 0x07FF; +/* + * @ingroup fp64 basic parameter + * @brief maximum mantissa value of fp64 (111?-?-(total 52bits 1)) + */ +#define FP64_MAX_MAN (0xFFFFFFFFFFFLLu) +/* + * @ingroup fp64 special value judgment + * @brief whether a fp64 is NaN + */ +#define FP64_IS_NAN(x) ((((x) & FP64_EXP_MASK) == FP64_EXP_MASK) && ((x) & FP64_MAN_MASK)) +/* + * @ingroup fp64 special value judgment + * @brief whether a fp64 is infinite + */ +#define FP64_IS_INF(x) ((((x) & FP64_EXP_MASK) == FP64_EXP_MASK) && (!((x) & FP64_MAN_MASK))) + +/* + * @ingroup integer special value judgment + * @brief maximum positive value of int8_t (0111 1111) + */ +const int INT8_T_MAX = 0x7F; +/* + * @ingroup integer special value judgment + * @brief maximum value of a data with 8 bits length (1111 111) + */ +const int BIT_LEN8_MAX = 0xFF; +/* + * @ingroup integer special value judgment + * @brief maximum positive value of int16_t (0111 1111 1111 1111) + */ +const int INT16_T_MAX = 0x7FFF; +/* + * @ingroup integer special value judgment + * @brief maximum value of a data with 16 bits length (1111 1111 1111 1111) + */ +const int BIT_LEN16_MAX = 0xFFFF; +/* + * @ingroup integer special value judgment + * @brief maximum positive value of int32_t (0111 1111 1111 1111 1111 1111 1111 1111) + */ +#define INT32_T_MAX (0x7FFFFFFFu) +/* + * @ingroup integer special value judgment + * @brief maximum value of a data with 32 bits length (1111 1111 1111 1111 1111 1111 1111 1111) + */ +#define BIT_LEN32_MAX (0xFFFFFFFFu) +/* + * @ingroup print switch + * @brief print an error if input fp16 is overflow + */ +/* + * @ingroup fp16_t enum + * @brief round mode of last valid digital + */ +enum fp16RoundMode_t { + ROUND_TO_NEAREST = 0, /* < round to nearest even */ + ROUND_BY_TRUNCATED, /* < round by truncated */ + ROUND_MODE_RESERVED, +}; + +/* + * @ingroup fp16_t + * @brief Half precision float + * bit15: 1 bit SIGN +---+-----+------------+ + * bit14-10: 5 bit EXP | S |EEEEE|MM MMMM MMMM| + * bit0-9: 10bit MAN +---+-----+------------+ + * + */ +using fp16_t = struct tagFp16 { + uint16_t val; + +public: + /* + * @ingroup fp16_t constructor + * @brief Constructor without any param(default constructor) + */ + tagFp16(void) + { + val = 0x0u; + } + + /* + * @ingroup fp16_t constructor + * @brief Constructor with an uint16_t value + */ + tagFp16(const uint16_t& uiVal) : val(uiVal) + { + } + + /* + * @ingroup fp16_t constructor + * @brief Constructor with a fp16_t object(copy constructor) + */ + tagFp16(const tagFp16& fp) : val(fp.val) + { + } + /* + * @ingroup fp16_t math evaluation operator + * @param [in] fp fp16_t object to be copy to fp16_t + * @brief Override basic evaluation operator to copy fp16_t to a new fp16_t + * @return Return fp16_t result from fp + */ + __attribute__((__visibility__("default"))) tagFp16& operator=(const tagFp16& fp); + /* + * @ingroup fp16_t math evaluation operator + * @param [in] fVal float object to be converted to fp16_t + * @brief Override basic evaluation operator to convert float to fp16_t + * @return Return fp16_t result from fVal + */ + __attribute__((__visibility__("default"))) tagFp16& operator=(const float& fVal); + /* + * @ingroup fp16_t math conversion + * @brief Override convert operator to convert fp16_t to float/fp32 + * @return Return float/fp32 value of fp16_t + */ + __attribute__((__visibility__("default"))) operator float() const; + /* + * @ingroup fp16_t math conversion + * @brief Convert fp16_t to float/fp32 + * @return Return float/fp32 value of fp16_t + */ + __attribute__((__visibility__("default"))) float toFloat(); + /* + * @ingroup fp16_t math conversion + * @brief Convert fp16_t to uint8_t + * @return Return uint8_t value of fp16_t + */ + __attribute__((__visibility__("default"))) uint8_t toUInt8(); +}; + +/* + * @ingroup fp16_t public method + * @param [in] val signature is negative + * @param [in|out] s sign of fp16_t object + * @param [in|out] e exponent of fp16_t object + * @param [in|out] m mantissa of fp16_t object + * @brief Extract the sign, exponent and mantissa of a fp16_t object + */ +void ExtractFP16(const uint16_t& val, uint16_t* s, int16_t* e, uint16_t* m); + +/* + * @ingroup fp16_t public method + * @param [in] negative sign is negative + * @param [in|out] man mantissa to be reverse + * @brief Calculate a mantissa's complement (add ont to it's radix-minus-one complement) + * @return Return complement of man + */ +template +void ReverseMan(bool negative, T* man) +{ + if (negative) { + *man = (~(*man)) + 1; + } +} + +/* + * @ingroup fp16_t public method + * @param [in] ea exponent of one fp16_t/float number + * @param [in] ma mantissa of one fp16_t/float number + * @param [in] eb exponent of another fp16_t/float number + * @param [in] mb mantissa of another fp16_t/float number + * @brief choose mantissa to be shift right whoes exponent is less than another one + * @return Return mantissawhoes exponent is less than another one + */ +template +T* MinMan(const int16_t& ea, T* ma, const int16_t& eb, T* mb) +{ + return (ea > eb) ? mb : ma; +} +/* + * @ingroup fp16_t public method + * @param [in] man mantissa to be operate + * @param [in] shift right shift bits + * @brief right shift a mantissa + * @return Return right-shift mantissa + */ +template +T RightShift(T man, int16_t shift) +{ + size_t bits = sizeof(T) * 8; + T mask = ((static_cast(1u)) << (static_cast(bits - 1))); + for (int i = 0; i < shift; i++) { + man = ((man & mask) | (man >> 1)); + } + return man; +} +/* + * @ingroup fp16_t public method + * @param [in] ea exponent of one temp fp16_t number + * @param [in] ma mantissa of one temp fp16_t number + * @param [in] eb exponent of another temp fp16_t number + * @param [in] mb mantissa of another temp fp16_t number + * @brief Get mantissa sum of two temp fp16_t numbers, T support types: uint16_t/uint32_t/uint64_t + * @return Return mantissa sum + */ +template +T GetManSum(int16_t ea, const T& ma, int16_t eb, const T& mb) +{ + T sum = 0; + if (ea != eb) { + T m_tmp = 0; + int16_t e_tmp = std::abs(ea - eb); + if (ea > eb) { + m_tmp = mb; + m_tmp = RightShift(m_tmp, e_tmp); + sum = ma + m_tmp; + } else { + m_tmp = ma; + m_tmp = RightShift(m_tmp, e_tmp); + sum = m_tmp + mb; + } + } else { + sum = ma + mb; + } + return sum; +} +/* + * @ingroup fp16_t public method + * @param [in] bit0 whether the last preserved bit is 1 before round + * @param [in] bit1 whether the abbreviation's highest bit is 1 + * @param [in] bitLeft whether the abbreviation's bits which not contain highest bit grater than 0 + * @param [in] man mantissa of a fp16_t or float number, support types: uint16_t/uint32_t/uint64_t + * @param [in] shift abbreviation bits + * @brief Round fp16_t or float mantissa to nearest value + * @return Returns true if round 1,otherwise false; + */ +template +T ManRoundToNearest(bool bit0, bool bit1, bool bitLeft, T man, uint16_t shift = 0) +{ + man = (man >> shift) + ((bit1 && (bitLeft || bit0)) ? 1 : 0); + return man; +} +/* + * @ingroup fp16_t public method + * @param [in] man mantissa of a float number, support types: uint16_t/uint32_t/uint64_t + * @brief Get bit length of a uint32_t number + * @return Return bit length of man + */ +template +int16_t GetManBitLength(T man) +{ + int16_t len = 0; + while (man) { + man >>= 1; + len++; + } + return len; +} + +#endif /* _FP16_T_HPP_ */ diff --git a/entry/src/main/ets/model/PathModel.ets b/entry/src/main/ets/model/PathModel.ets index f5076e4..ec0fc55 100644 --- a/entry/src/main/ets/model/PathModel.ets +++ b/entry/src/main/ets/model/PathModel.ets @@ -16,6 +16,8 @@ class PathModel { public input: string | undefined = '' public omPath: string | undefined = '' public arrayBufferList: Array = [] + public goldenBufferList : Array = [] + public outputBufferList : Array = [] public optimization: number | undefined = 0 } diff --git a/entry/src/main/ets/utils/FileUtils.ets b/entry/src/main/ets/utils/FileUtils.ets index 3ab736b..5fd1a23 100644 --- a/entry/src/main/ets/utils/FileUtils.ets +++ b/entry/src/main/ets/utils/FileUtils.ets @@ -63,26 +63,56 @@ export class FileUtils { let inputFilePath: Array = []; const documentPicker = new picker.DocumentViewPicker(context); const option = new picker.DocumentSelectOptions(); - option.fileSuffixFilters = ['.bin']; // 只显示bin文件 + option.fileSuffixFilters = ['.bin']; const selectedFiles = await documentPicker.select(option); if (selectedFiles.length > 0) { for (let i = 0; i < selectedFiles.length; i++) { + const filePath = selectedFiles[i].toString(); let isRepeated = false; for (let j = 0; j < inputFilePath.length; j++) { - if (selectedFiles[i] == inputFilePath[j]) { + if (filePath === inputFilePath[j]) { isRepeated = true; break; } } if (!isRepeated) { - inputFilePath.push(selectedFiles[i].toString()); + inputFilePath.push(filePath); } } } return inputFilePath; } + /** + * Select output file(s) for the model + * @param context - Application context + * @returns Array of selected output file paths + */ + static async pickGoldenFiles(context: common.Context): Promise { + let goldenFilePath: Array = []; + const documentPicker = new picker.DocumentViewPicker(context); + const option = new picker.DocumentSelectOptions(); + // option.fileSuffixFilters = ['.bin', '.csv']; + + const selectedFiles = await documentPicker.select(option); + if (selectedFiles.length > 0) { + for (let i = 0; i < selectedFiles.length; i++) { + let isRepeated = false; + for (let j = 0; j < goldenFilePath.length; j++) { + if (selectedFiles[i] == goldenFilePath[j]) { + isRepeated = true; + break; + } + } + if (!isRepeated) { + goldenFilePath.push(selectedFiles[i].toString()); + } + } + } + return goldenFilePath; + } + /** * select model file * @param context context @@ -110,7 +140,7 @@ export class FileUtils { * @param context context * @param promptAction prompt */ - static runModel(modelFilePath: string, inputFilePath: Array, selectedOption: string, context: common.Context, + static runModel(modelFilePath: string, inputFilePath: Array, goldenFilePath: Array, selectedOption: string, context: common.Context, promptAction: PromptAction) { hilog.info(0x0000, 'CANNDemo', 'begin to load-infer-unload model.'); let profCsvFiles: Array = ['prof', 'csv']; @@ -133,6 +163,7 @@ export class FileUtils { } if (hiaiOhos.LoadModel(modelBuffer, PathContext.optimization) != 0) { hilog.info(0x0000, 'CANNDemo', 'failed to load model.') + hiaiOhos.UnloadModel() return } hilog.info(0x0000, 'CANNDemo', 'succeed in load model.') @@ -153,21 +184,45 @@ export class FileUtils { stream1.closeSync(); } catch (error) { hilog.error(0x0000, 'CANNDemo', 'obtain input files failed: %{public}s', error.message) + hiaiOhos.UnloadModel() return } } + PathContext.goldenBufferList = []; + for (let index = 0; index < goldenFilePath.length; index++) { + const goldenFile = new fileUri.FileUri(goldenFilePath[index]).path; + try { + let stream2 = fileIo.createStreamSync(goldenFile, 'r+'); + let fileStat2 = fileIo.lstatSync(goldenFile); + let goldenBuffer1: ArrayBuffer = new ArrayBuffer(fileStat2.size); + let readOption2: ReadOptions = { + length: fileStat2.size, + offset: 0 + }; + stream2.readSync(goldenBuffer1, readOption2); + PathContext.goldenBufferList.push(goldenBuffer1); + stream2.closeSync(); + } catch (error) { + hilog.error(0x0000, 'CANNDemo', 'obtain golden files failed: %{public}s', error.message); + hiaiOhos.UnloadModel(); + return; + } + } + let ret = hiaiOhos.InitLabels(PathContext.arrayBufferList); if (ret == 0) { hilog.info(0x0000, 'CANNDemo', 'InitLabels success.'); } else { hilog.error(0x0000, 'CANNDemo', 'InitLabels fail.'); + hiaiOhos.UnloadModel() return } ret = hiaiOhos.InitIOTensors() if (ret != 0) { hilog.info(0x0000, 'CANNDemo', 'failed to InitIOTensors.') + hiaiOhos.UnloadModel() return } hilog.info(0x0000, 'CANNDemo', 'succeed in InitIOTensors.') @@ -177,6 +232,7 @@ export class FileUtils { ret = hiaiOhos.RunModel() if (ret != 0) { hilog.info(0x0000, 'CANNDemo', 'failed to RunModel.') + hiaiOhos.UnloadModel() return } hilog.info(0x0000, 'CANNDemo', 'succeed in RunModel.') @@ -187,17 +243,27 @@ export class FileUtils { let uri = modelFilePath.substring(0, modelFilePath.lastIndexOf('/')); for (let i: number = 0; i < count; i++) { const omFilePath = new fileUri.FileUri(uri + '/' + 'output' + i + '.bin').path; + try { + if (fileIo.accessSync(omFilePath)) { + // fileIo.rmdirSync(omFilePath) + fileIo.unlinkSync(omFilePath) + } + } catch (error) { + hilog.error(0x0000, 'CANNDemo', 'delete %{public}s failed, %{public}s', omFilePath, error.message); + } try { const file = fileIo.openSync(omFilePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE); hilog.info(0x0000, 'CANNDemo output size', '%{public}d', result[i].byteLength); - let buf_result = buffer.from(result[i], i, result[i].byteLength); + let buf_result = buffer.from(result[i]); fileIo.writeSync(file.fd, buf_result.buffer); fileIo.closeSync(file.fd); } catch (e) { hilog.error(0x0000, 'CANNDemo', 'generate output.bin failed'); + hiaiOhos.UnloadModel() return } } + let dataTypes = hiaiOhos.GetDataType(); // unload model hilog.info(0x0000, 'CANNDemo', 'start unload model success.'); @@ -259,13 +325,44 @@ export class FileUtils { return } + let totalSimilarity = 0.0; + let validCount = 0; + let failedIndices: number[] = []; + for (let i = 0; i < PathContext.goldenBufferList.length; i++) { + try { + let compareResult = hiaiOhos.CompareBuffers( + result[i], + PathContext.goldenBufferList[i], + dataTypes[i] + ); + const match = compareResult.match(/Similarity:\s*([0-9.]+)/); + if (match && match[1]) { + const similarity = parseFloat(match[1]); + totalSimilarity += similarity; + validCount++; + } + } catch (error) { + failedIndices.push(i); + } + } + let message = "Model Inference Success\n"; + if (failedIndices.length > 0) { + const total = PathContext.goldenBufferList.length; + const successCount = total - failedIndices.length; + message += `Compare Result: ${successCount} success, ${failedIndices.length} fail\n`; + message += `Failed Index: ${failedIndices.join(', ')}`; + } else if (validCount > 0) { + const averageSimilarity = totalSimilarity / validCount; + message += `Average Similarity: ${averageSimilarity.toFixed(2)}%\n`; + message += `Compare Result: ${validCount} success, 0 fail`; + } try { promptAction.showToast({ - message: "Model Inference Success", - duration: 2000, + message: message, + duration: 4000, }); } catch (error) { - hilog.error(0x0000, 'CANNDemo', 'promptAction show toast failed: %{public}s', error.message) + hilog.error(0x0000, 'CANNDemo', 'promptAction show toast failed: %{public}s', error.message); } } diff --git a/entry/src/main/ets/view/FilePicker.ets b/entry/src/main/ets/view/FilePicker.ets index 603a2b1..c5eb351 100644 --- a/entry/src/main/ets/view/FilePicker.ets +++ b/entry/src/main/ets/view/FilePicker.ets @@ -20,6 +20,7 @@ import { FileUtils } from '../utils/FileUtils' @Component export struct FilePicker { @State inputFilePath: Array = []; + @State goldenFilePath: Array = []; @State modelFilePath: string = ''; @State isProcessing: boolean = true; @State selectedOption: string = '选择调优模式'; @@ -72,7 +73,7 @@ export struct FilePicker { .justifyContent(FlexAlign.Start) } .borderRadius(16) - .height('24%') + .height('18%') .width('90%') .margin({ top: 10 }) .align(Alignment.TopStart) @@ -87,7 +88,62 @@ export struct FilePicker { } .backgroundColor('#FFFFFF') .borderRadius(16) - .height('24%') + .height('18%') + .width('90%') + .margin({ top: 10 }) + .align(Alignment.TopStart) + } + + Row() { + Text($r('app.string.home_select_title_1_2')) + .textAlign(TextAlign.Start) + .fontSize(14) + Text($r('app.string.home_button_title_3_1')) + .onClick(async () => { + this.goldenFilePath = await FileUtils.pickGoldenFiles(this.getUIContext().getHostContext() as common.Context); + }) + .textAlign(TextAlign.End) + .fontColor('#0A59F7') + .fontSize(14) + } + .justifyContent(FlexAlign.SpaceBetween) + .width('90%') + .padding({ left: 14, right: 14 }) + .margin({top: 10}) + + if (this.goldenFilePath.length > 0) { + Scroll() { + Column() { + ForEach(this.goldenFilePath, (row: string) => { + Text(row.substring(row.lastIndexOf('/') + 1,)) + .fontSize(16) + .padding(8) + .maxLines(1) + .textOverflow({ + overflow: TextOverflow.Ellipsis + }) + .width('100%') + }, (row: Array) => JSON.stringify(row)) + } + .justifyContent(FlexAlign.Start) + } + .borderRadius(16) + .height('15%') + .width('90%') + .margin({ top: 10 }) + .align(Alignment.TopStart) + .backgroundColor('#FFFFFF') + } else { + Scroll() { + Text($r('app.string.home_data_txt_1_2')) + .fontSize(16) + .width('100%') + .fontColor('#99000000') + .padding({ top: 8, left: 16 }) + } + .backgroundColor('#FFFFFF') + .borderRadius(16) + .height('15%') .width('90%') .margin({ top: 10 }) .align(Alignment.TopStart) @@ -198,7 +254,7 @@ export struct FilePicker { .enabled(!this.isProcessing && this.inputFilePath.length !== 0 && this.modelFilePath !== '' && this.selectedOption != '选择调优模式') .onClick(() => { - FileUtils.runModel(this.modelFilePath, this.inputFilePath, this.selectedOption, + FileUtils.runModel(this.modelFilePath, this.inputFilePath, this.goldenFilePath, this.selectedOption, this.getUIContext().getHostContext()as common.Context, this.getUIContext().getPromptAction()); }) .width('48%') @@ -236,6 +292,8 @@ export struct FilePicker { this.modelFilePath = ''; this.selectedOption = '选择调优模式'; PathModel.arrayBufferList = []; + PathModel.goldenBufferList = []; + PathModel.outputBufferList = []; PathModel.optimization = 0; this.selectIndex = -1; } -- Gitee