* Copyright (c) 2022 Huawei Device 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 "include/OhosCommonTest.h"
#include <cfloat>
#include <cinttypes>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <functional>
#include <hilog/log.h>
#include <iostream>
#include <malloc.h>
#include <map>
#include <numeric>
#include <regex>
#include <sstream>
#include <string>
#include <sys/time.h>
#include <unistd.h>
#include <vector>
namespace ActsDrawingNativeConstants {
const std::string DEFAULT_TAG = "ActsNewDrawingNativeTest";
}
namespace {
constexpr size_t FORMAT_PUBLIC_RESERVED = 25;
constexpr size_t PRINTF_BUFFER_SIZE = 1024;
constexpr int MAX_HEAD_SIZE = 50;
constexpr uint32_t FAIL_PRINT_LIMIT = 100;
constexpr float QUANT_TOL_THRESHOLD = 0.04f;
constexpr uint32_t PREVIEW_ELEMENT_LIMIT = 10;
constexpr uint64_t DATA_SIZE_INIT = 1;
constexpr float DEFAULT_RTOL = 1e-05f;
constexpr float DEFAULT_ATOL = 1e-08f;
constexpr float EPSILON_OFFSET = 1.0f;
constexpr int INDEX_OFFSET_ONE = 1;
constexpr int CSV_SUCCESS_FLAG = 1;
constexpr int CSV_FAIL_FLAG = 0;
constexpr float ZERO_THRESHOLD = 0.0f;
constexpr int FLOAT32_BYTE_SIZE = 4;
constexpr int FLOAT32_BYTE_OFFSET_1 = 1;
constexpr int FLOAT32_BYTE_OFFSET_2 = 2;
constexpr int FLOAT32_BYTE_OFFSET_3 = 3;
constexpr uint64_t MICROSECONDS_PER_SECOND = 1000000;
constexpr int ERROR_CODE_NEGATIVE_ONE = -1;
constexpr int ERROR_CODE_NEGATIVE_TWO = -2;
constexpr int ERROR_CODE_NEGATIVE_THREE = -3;
constexpr int SUCCESS_CODE = 0;
constexpr int STRING_OFFSET_ONE = 1;
constexpr int FILE_SEEK_START = 0;
struct AllCloseInt8Options {
uint8_t* a;
uint8_t* b;
uint64_t count;
float rTol;
float aTol;
bool isQuant;
};
struct AllCloseFloatOptions {
float* a;
float* b;
uint64_t count;
float rTol;
float aTol;
bool isQuant;
};
}
void ConvertToPublic(const char* fmt, char* out, size_t outSize)
{
const char* p = fmt;
char* q = out;
while (*p && static_cast<size_t>(q - out) < outSize - FORMAT_PUBLIC_RESERVED) {
if (*p == '%' && *(p + 1) != '{') {
p++;
SafeMemcpy(q, outSize - (q - out), "%{public}", outSize - (q - out) - 1);
q += strlen("%{public}");
while (*p && strchr("0123456789.+-hlLjzt", *p)) {
*q++ = *p++;
}
if (*p) {
*q++ = *p++;
}
continue;
}
*q++ = *p++;
}
*q = '\0';
}
void OHPrintf(const char* fmt, ...)
{
char fmtPublic[PRINTF_BUFFER_SIZE];
ConvertToPublic(fmt, fmtPublic, sizeof(fmtPublic));
va_list args;
va_start(args, fmt);
OH_LOG_VPrint(LOG_APP, LOG_DEBUG, LOG_DOMAIN, ActsDrawingNativeConstants::DEFAULT_TAG.c_str(), fmtPublic, args);
va_end(args);
}
* GetDimInfo: get dim info from data file(int64_t)
* fp: the testing datafile object
* dim_info: array to store the info of the dim in datafile, like
* [4,3,3,6,3,162(3*3*6*3)],4 is dim size,3,3,6,3 is the dim shape data_size:
* the size of the testing data including the data file
* */
void GetDimInfo(FILE* fp, std::vector<int64_t>* dimInfo)
{
uint32_t* dimBuffer = reinterpret_cast<uint32_t*>(malloc(MAX_HEAD_SIZE * sizeof(uint32_t)));
size_t readSize = fread(dimBuffer, sizeof(uint32_t), MAX_HEAD_SIZE, fp);
if (readSize == 0) {
free(dimBuffer);
return;
}
dimInfo->push_back(*dimBuffer);
uint64_t dataSize = DATA_SIZE_INIT;
uint32_t index = INDEX_OFFSET_ONE;
for (; index <= dimInfo->at(0); index++) {
dimInfo->push_back(*(dimBuffer + index));
dataSize *= *(dimBuffer + index);
}
dimInfo->push_back(dataSize);
free(dimBuffer);
}
* readTestDataFile: 从HISI .t 数据文件中读取测试数据(int64)
* infile: HISI .t 数据文件的路径
* return: dim_info: 存储数据文件中维度信息的数组,如 [4,3,3,,3],4 是维度大小,3,3,6,3 是维度形状
*/
void* ReadTestDataFile(std::string infile, std::vector<int64_t>* dimInfoOut)
{
OHPrintf("\n [common.cpp] Loading data from: %s\n", infile.c_str());
FILE* fp = fopen(infile.c_str(), "r");
if (fp == nullptr) {
OHPrintf("ERROR: cant't open file %s\n", infile.c_str());
return nullptr;
} else {
std::vector<int64_t> dimInfo;
std::vector<int64_t>* dimInfoPtr = &dimInfo;
GetDimInfo(fp, dimInfoPtr);
uint64_t dataSize = dimInfoPtr->at(dimInfoPtr->size() - 1);
fclose(fp);
fp = fopen(infile.c_str(), "r");
if (fp == nullptr) {
OHPrintf("ERROR: cant't open file %s\n", infile.c_str());
return nullptr;
}
uint32_t* memory = reinterpret_cast<uint32_t*>(malloc((dimInfo[0] + INDEX_OFFSET_ONE) * sizeof(uint32_t)));
size_t ret = fread(memory, sizeof(uint32_t), (dimInfo[0] + INDEX_OFFSET_ONE), fp);
if (ret == 0 || dataSize == 0 || dataSize > SIZE_MAX / sizeof(uint32_t)) {
free(memory);
fclose(fp);
return nullptr;
}
uint32_t* data = reinterpret_cast<uint32_t*>(malloc(dataSize * sizeof(uint32_t)));
size_t ret2 = fread(data, sizeof(uint32_t), dataSize, fp);
if (ret2 == 0) {
free(data);
fclose(fp);
return nullptr;
}
free(memory);
fclose(fp);
for (int i = 0; i < dimInfo[0]; i++) {
dimInfoOut->push_back(dimInfo[i + INDEX_OFFSET_ONE]);
}
OHPrintf("\n [common.cpp] Read test data file Over, get dimInfo as: (");
int count = dimInfoOut->size();
for (int i = 0; i < count; i++) {
OHPrintf("%" PRId64, dimInfoOut->at(i));
}
OHPrintf(")\n");
return data;
}
}
* allclose
* param:
* a:compared file a
* b:compared file b
* count: the count size which will compare
* rtol: 相对容差
* atol: 绝对容差
* return:
* true or false
* */
namespace {
struct FloatCompareStats {
uint32_t failCount = 0;
float tol = 0;
float tol1 = 0;
float tol2 = 0;
bool nanOccurInAccuray = false;
float sum = 0.0f;
float rtol;
float atol;
};
void ProcessFloatElement(float aVal, float bVal, uint32_t idx, FloatCompareStats& stats)
{
float diff = fabs(aVal - bVal);
if (isnan(aVal) || isinf(aVal)) {
stats.failCount++;
stats.nanOccurInAccuray = true;
if (stats.failCount < FAIL_PRINT_LIMIT) {
OHPrintf(" i = %2u: %+f | %+f\n", idx, aVal, bVal);
}
} else if (diff > (stats.atol + stats.rtol * fabs(bVal))) {
stats.tol += diff / (fabs(bVal) + EPSILON_OFFSET);
stats.tol1 += diff;
stats.tol2 += diff / fabs(bVal);
stats.failCount++;
if (stats.failCount < FAIL_PRINT_LIMIT) {
OHPrintf(" i = %2u: %+f | %+f\n", idx, aVal, bVal);
}
}
}
struct PrintFloatStatsAndWriteCsvParams {
uint64_t count;
float sumAll;
float maximum;
float minimum;
};
void PrintFloatStatsAndWriteCsv(const FloatCompareStats& stats, uint64_t& c, PrintFloatStatsAndWriteCsvParams params)
{
OHPrintf(" ......\n");
OHPrintf("\n *** Total fail_count: %u\n", stats.failCount);
if (stats.failCount != 0) {
float avgTol = stats.tol / stats.failCount;
float avgAbsDiff = stats.tol1 / stats.failCount;
float avgRelDiff = stats.tol2 / stats.failCount;
OHPrintf("\n fabs(a[i] - b[i])/(fabs(b[i])+1) : %f\n", avgTol);
OHPrintf("\n fabs(a[i] - b[i]) : %f\n", avgAbsDiff);
OHPrintf("\n fabs(a[i] - b[i])/fabs(b[i]) : %f\n", avgRelDiff);
}
c += params.count;
float avg = (params.count > 0) ? (stats.sum / params.count) : 0.0f;
OHPrintf("\n avg : %f\n", avg);
OHPrintf("\n min : %f\n", params.minimum);
OHPrintf("\n max : %f\n", params.maximum);
if (c != 0) {
OHPrintf("\n avg_all : %f\n", params.sumAll / c);
}
OHPrintf("\n");
fstream file;
file.open("cout.csv", ios::app);
file << "," << CSV_SUCCESS_FLAG << "," << CSV_FAIL_FLAG << "," << params.maximum;
float csvAvg = (stats.failCount == 0 && c > 0) ? (params.sumAll / c)
: ((stats.failCount > 0) ? (stats.tol / stats.failCount) : 0.0f);
file << "," << csvAvg;
file.close();
}
bool CheckFloatFinalResult(const FloatCompareStats& stats, float rtol, float atol, bool isquant)
{
if (stats.nanOccurInAccuray) {
OHPrintf("\n[common.cpp] eval output include some NAN/INF\n");
return false;
}
if (stats.failCount > 0) {
OHPrintf("\n *** These data compare failed: atol = %f, rtol = %f\n", atol, rtol);
OHPrintf("\n");
if (isquant && (stats.tol / stats.failCount) < QUANT_TOL_THRESHOLD) {
return true;
}
return false;
}
return true;
}
}
* allclose
* param:
* a:compared file a
* b:compared file b
* count: the count size which will compare
* rtol: 相对容差
* atol: 绝对容差
* return:
* true or false
* */
bool AllCloseFloat(const AllCloseFloatOptions options)
{
if (options.a == nullptr || options.a == nullptr) {
return false;
}
FloatCompareStats stats;
static float sumAll;
static float maximum = 0;
static float minimum = 0;
static uint64_t c = 0;
for (uint32_t i = 0; i < options.count; ++i) {
float diff = fabs(options.a[i] - options.b[i]);
float denom = options.aTol + options.rTol * fabs(options.b[i]);
if (denom == 0.0f) {
denom = EPSILON_OFFSET;
}
float normDiff = diff / denom;
stats.sum += normDiff;
sumAll += normDiff;
maximum = max(maximum, normDiff);
minimum = min(minimum, normDiff);
stats.rtol = options.rTol;
stats.atol = options.aTol;
ProcessFloatElement(options.a[i], options.b[i], i, stats);
if (i == options.count - INDEX_OFFSET_ONE) {
PrintFloatStatsAndWriteCsvParams params;
params.count = options.count;
params.sumAll = sumAll;
params.maximum = maximum;
params.minimum = minimum;
PrintFloatStatsAndWriteCsv(stats, c, params);
}
}
return CheckFloatFinalResult(stats, options.rTol, options.aTol, options.isQuant);
}
namespace {
struct Int8CompareStats {
uint32_t failCount = 0;
float tol = 0;
float tol1 = 0;
float tol2 = 0;
bool nanOccurInAccuray = false;
float sum = 0.0f;
};
void ProcessInt8Element(uint8_t aVal, uint8_t bVal, uint32_t idx, Int8CompareStats& stats)
{
if (isnan(aVal) || isinf(aVal)) {
stats.failCount++;
stats.nanOccurInAccuray = true;
if (stats.failCount < FAIL_PRINT_LIMIT) {
OHPrintf(" i = %2u: %+f | %+f\n", idx, static_cast<float>(aVal), static_cast<float>(bVal));
}
} else if (fabs(aVal - bVal) > ZERO_THRESHOLD) {
stats.tol += fabs(aVal - bVal) / (fabs(bVal) + EPSILON_OFFSET);
stats.tol1 += fabs(aVal - bVal);
stats.tol2 += fabs(aVal - bVal) / fabs(bVal);
stats.failCount++;
OHPrintf("%2d", static_cast<int>(fabs(aVal - bVal)));
OHPrintf(" i = %2u: %2d | %2d\n", idx, aVal, bVal);
}
}
struct PrintInt8StatsAndWriteCsvParams {
uint64_t count;
float sumAll;
float maximum;
float minimum;
uint64_t& countAll;
explicit PrintInt8StatsAndWriteCsvParams(uint64_t& countAll) : countAll(countAll) {}
};
void PrintInt8StatsAndWriteCsv(const Int8CompareStats& stats, PrintInt8StatsAndWriteCsvParams params)
{
OHPrintf(" ……\n");
OHPrintf("\n *** Total fail_count: %u\n", stats.failCount);
if (stats.failCount != 0) {
float avgTol = stats.tol / stats.failCount;
float avgAbsDiff = stats.tol1 / stats.failCount;
float avgRelDiff = stats.tol2 / stats.failCount;
OHPrintf("\n fabs(a[i] - b[i])/(fabs(b[i])+1) : %f\n", avgTol);
OHPrintf("\n fabs(a[i] - b[i]) : %f\n", avgAbsDiff);
OHPrintf("\n fabs(a[i] - b[i])/fabs(b[i]) : %f\n", avgRelDiff);
}
params.countAll += params.count;
float avg = (params.count > 0) ? (stats.sum / params.count) : 0.0f;
OHPrintf("\n avg : %f\n", avg);
OHPrintf("\n min : %f\n", params.minimum);
OHPrintf("\n max : %f\n", params.maximum);
OHPrintf("\n avg_all : %f\n", params.sumAll / params.countAll);
OHPrintf("\n");
fstream file;
file.open("cout.csv", ios::app);
file << "," << CSV_SUCCESS_FLAG << "," << CSV_FAIL_FLAG << "," << params.maximum;
float csvAvg = (stats.failCount == 0 && params.countAll > 0)
? (params.sumAll / params.countAll)
: ((stats.failCount > 0) ? (stats.tol / stats.failCount) : 0.0f);
file << "," << csvAvg;
file.close();
}
bool CheckInt8FinalResult(const Int8CompareStats& stats, float rtol, float atol, bool isquant)
{
if (stats.nanOccurInAccuray) {
OHPrintf("\n[common.cpp] eval output include some NAN/INF\n");
return false;
}
if (stats.failCount > 0) {
OHPrintf("\n *** These data compare failed: atol = %f, rtol = %f\n", atol, rtol);
OHPrintf("\n");
if (isquant && (stats.tol / stats.failCount) < QUANT_TOL_THRESHOLD) {
return true;
}
return false;
}
return true;
}
}
bool AllCloseInt8(const AllCloseInt8Options options)
{
if (options.a == nullptr || options.b == nullptr) {
return false;
}
Int8CompareStats stats;
static float sumAll;
static float maximum = 0;
static float minimum = 0;
static uint64_t countAll = 0;
for (uint32_t i = 0; i < options.count; ++i) {
float diff = fabs(options.a[i] - options.b[i]);
float denom = options.aTol + options.rTol * fabs(options.b[i]);
if (denom == 0.0f) {
denom = EPSILON_OFFSET;
}
float normDiff = diff / denom;
stats.sum += normDiff;
sumAll += normDiff;
maximum = max(static_cast<double>(maximum), static_cast<double>(normDiff));
minimum = min(static_cast<double>(minimum), static_cast<double>(normDiff));
ProcessInt8Element(options.a[i], options.b[i], i, stats);
if (i == options.count - INDEX_OFFSET_ONE) {
PrintInt8StatsAndWriteCsvParams params = PrintInt8StatsAndWriteCsvParams(countAll);
params.count = options.count;
params.sumAll = sumAll;
params.maximum = maximum;
params.minimum = minimum;
PrintInt8StatsAndWriteCsv(stats, params);
}
}
return CheckInt8FinalResult(stats, options.rTol, options.aTol, options.isQuant);
}
* CompFp32WithTData: compare the data with the data in hisi .t file
* param:
* actualOutputData: the result of ge
* expectedDataFile: the path of hisi .t result file
* rtol: 相对容差
* atol: 绝对容差
* return:
* true of false
* */
bool CompFp32WithTData(float* actualOutputData,
const std::string& expectedDataFile,
float rTol = DEFAULT_RTOL,
float aTol = DEFAULT_ATOL,
bool isQuant = false)
{
std::vector<int64_t> dimInfo;
std::vector<int64_t>* dimInfoPtr = &dimInfo;
float* expectedOutputData = reinterpret_cast<float*>(ReadTestDataFile(expectedDataFile, dimInfoPtr));
uint32_t i = 0;
uint64_t dataSize =
accumulate(dimInfo.begin(), dimInfo.end(), static_cast<uint64_t>(1), std::multiplies<uint64_t>());
OHPrintf("[common.cpp] expected output data:");
for (; i < dataSize && i < PREVIEW_ELEMENT_LIMIT; i++) {
OHPrintf("%4f ", expectedOutputData[i]);
}
OHPrintf("\n");
AllCloseFloatOptions options;
options.a = actualOutputData;
options.b = expectedOutputData;
options.count = dataSize;
options.rTol = rTol;
options.aTol = aTol;
options.isQuant = isQuant;
bool ret = AllCloseFloat(options);
free(expectedOutputData);
return ret;
}
bool CompUint8WithTData(uint8_t* actualOutputData,
const std::string& expectedDataFile,
float rTol = DEFAULT_RTOL,
float aTol = DEFAULT_ATOL,
bool isQuant = false)
{
std::vector<int64_t> dimInfo;
std::vector<int64_t>* dimInfoPtr = &dimInfo;
auto dataFile = ReadTestDataFile(expectedDataFile, dimInfoPtr);
if (dataFile == nullptr) {
return false;
}
uint8_t* expectedOutputData = reinterpret_cast<uint8_t*>(dataFile);
uint32_t i = 0;
uint64_t dataSize = accumulate(dimInfo.begin(), dimInfo.end(), DATA_SIZE_INIT, std::multiplies<uint64_t>());
OHPrintf("\n [common.cpp] expected output data:\n");
for (; i < dataSize && i < PREVIEW_ELEMENT_LIMIT; i++) {
OHPrintf("%4hhu ", static_cast<unsigned char>(expectedOutputData[i]));
}
OHPrintf("\n");
AllCloseInt8Options options;
options.a = actualOutputData;
options.b = expectedOutputData;
options.count = dataSize;
options.rTol = rTol;
options.aTol = aTol;
options.isQuant = isQuant;
bool ret = AllCloseInt8(options);
free(expectedOutputData);
return ret;
}
* ReadFile: read file of model
* param:
* file: file location
* size: file size
* return:
* buf of file
* */
char* ReadFile(const char* file, size_t* size)
{
OHPrintf("[common.cpp] Loading data from: %s\n", file);
std::ifstream ifs(file);
if (!ifs.good()) {
return nullptr;
}
if (!ifs.is_open()) {
ifs.close();
return nullptr;
}
ifs.seekg(FILE_SEEK_START, std::ios::end);
*size = ifs.tellg();
char* buf = new char[*size];
if (buf == nullptr) {
ifs.close();
return nullptr;
}
ifs.seekg(FILE_SEEK_START, std::ios::beg);
ifs.read(buf, *size);
ifs.close();
OHPrintf("[common.cpp]Read Binary Data Over, get tensorSize as: %" PRId64 ".\n", static_cast<int64_t>(*size));
return buf;
}
void PackNCHWToNHWCFp32(const char* src, char* dst, int batch, int plane, int channel)
{
for (int n = 0; n < batch; n++) {
for (int c = 0; c < channel; c++) {
for (int hw = 0; hw < plane; hw++) {
int nhwcIndex = n * channel * plane + hw * channel + c;
int nchwIndex = n * channel * plane + c * plane + hw;
dst[nhwcIndex * FLOAT32_BYTE_SIZE] = src[nchwIndex * FLOAT32_BYTE_SIZE];
dst[nhwcIndex * FLOAT32_BYTE_SIZE + FLOAT32_BYTE_OFFSET_1] =
src[nchwIndex * FLOAT32_BYTE_SIZE + FLOAT32_BYTE_OFFSET_1];
dst[nhwcIndex * FLOAT32_BYTE_SIZE + FLOAT32_BYTE_OFFSET_2] =
src[nchwIndex * FLOAT32_BYTE_SIZE + FLOAT32_BYTE_OFFSET_2];
dst[nhwcIndex * FLOAT32_BYTE_SIZE + FLOAT32_BYTE_OFFSET_3] =
src[nchwIndex * FLOAT32_BYTE_SIZE + FLOAT32_BYTE_OFFSET_3];
}
}
}
return;
}
uint64_t GetTimeInUs()
{
uint64_t time;
struct timeval tv;
gettimeofday(&tv, nullptr);
time = static_cast<uint64_t>(tv.tv_sec) * MICROSECONDS_PER_SECOND + tv.tv_usec;
return time;
}
std::map<std::string, float> CalculateIntVector(std::vector<float> vec)
{
float max = 0;
float min = FLT_MAX;
float sum = 0;
float avg;
for (auto v : vec) {
max = fmax(max, v);
min = fmin(min, v);
sum += v;
}
avg = vec.size() > SUCCESS_CODE ? sum / vec.size() : SUCCESS_CODE;
std::map<std::string, float> result = { { "MIN", min }, { "MAX", max }, { "AVG", avg }, { "SUM", sum } };
return result;
}
int SafeCopyNoInclude(char* dst, size_t maxLen, const char* src)
{
if (!dst || !src || maxLen == SUCCESS_CODE) {
return ERROR_CODE_NEGATIVE_ONE;
}
size_t i = 0;
for (; i < maxLen - STRING_OFFSET_ONE && src[i] != '\0'; i++) {
dst[i] = src[i];
}
dst[i] = '\0';
return SUCCESS_CODE;
}
int32_t GetInt32ArgImpl(napi_env env, napi_callback_info info, size_t index, int32_t defaultValue)
{
size_t argc = index + 1;
napi_value args[index + 1];
napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (status != napi_ok || argc <= index) {
napi_throw_type_error(env, nullptr, "Argument missing");
return defaultValue;
}
napi_valuetype valuetype;
napi_typeof(env, args[index], &valuetype);
if (valuetype != napi_number) {
napi_throw_type_error(env, nullptr, "Argument must be a number");
return defaultValue;
}
int32_t result = defaultValue;
status = napi_get_value_int32(env, args[index], &result);
if (status != napi_ok) {
napi_throw_type_error(env, nullptr, "Value is not int32");
return defaultValue;
}
return result;
}
int SafeMemcpy(void* dst, size_t dstSize, const void* src, size_t len)
{
if (!dst || !src) {
return ERROR_CODE_NEGATIVE_ONE;
}
if (len == 0) {
return SUCCESS_CODE;
}
if (len > dstSize) {
return ERROR_CODE_NEGATIVE_TWO;
}
unsigned char* dstPtr = (unsigned char*)dst;
const unsigned char* srcPtr = static_cast<const unsigned char*>(src);
if (dstPtr == srcPtr) {
return SUCCESS_CODE;
}
if (dstPtr < srcPtr) {
for (size_t i = 0; i < len; ++i) {
dstPtr[i] = srcPtr[i];
}
} else if (dstPtr > srcPtr) {
for (size_t i = len; i > 0; --i) {
dstPtr[i - 1] = srcPtr[i - 1];
}
}
return SUCCESS_CODE;
}