forked from OSSInnovation/mindspore
452 lines
12 KiB
C++
452 lines
12 KiB
C++
|
/**
|
||
|
* Copyright 2019 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 "benchmark/benchmark.h"
|
||
|
#include <random>
|
||
|
#include <limits>
|
||
|
#include <algorithm>
|
||
|
#include <utility>
|
||
|
#include <memory>
|
||
|
#include "include/session.h"
|
||
|
|
||
|
namespace mindspore {
|
||
|
namespace predict {
|
||
|
STATUS Benchmark::GenerateRandomData(size_t size, void *data) {
|
||
|
MS_ASSERT(data != nullptr);
|
||
|
char *castedData = static_cast<char *>(data);
|
||
|
for (size_t i = 0; i < size; i++) {
|
||
|
castedData[i] = static_cast<char>(i);
|
||
|
}
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::GenerateInputData() {
|
||
|
for (Tensor *tensor : msInputs) {
|
||
|
MS_ASSERT(tensor != nullptr);
|
||
|
auto ret = tensor->MallocData();
|
||
|
if (ret != RET_OK) {
|
||
|
MS_LOGE("MallocData for inTensor failed %d", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
MS_ASSERT(tensor->GetData() != nullptr);
|
||
|
auto tensorByteSize = tensor->GetDataSize();
|
||
|
auto status = GenerateRandomData(tensorByteSize, tensor->GetData());
|
||
|
if (status != RET_OK) {
|
||
|
MS_LOGE("GenerateRandomData for inTensor failed %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
}
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::LoadInput() {
|
||
|
size_t size = 0;
|
||
|
char *graphBuf = ReadFile(_flags->modelPath.c_str(), &size);
|
||
|
if (graphBuf == nullptr) {
|
||
|
MS_LOGE("Load graph failed, path %s", _flags->modelPath.c_str());
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
|
||
|
this->msInputs = session->GetInput();
|
||
|
|
||
|
if (_flags->inDataPath.empty()) {
|
||
|
auto status = GenerateInputData();
|
||
|
if (status != RET_OK) {
|
||
|
delete graphBuf;
|
||
|
MS_LOGE("Generate input data error %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
} else {
|
||
|
auto status = ReadInputFile();
|
||
|
if (status != RET_OK) {
|
||
|
delete graphBuf;
|
||
|
MS_LOGE("ReadInputFile error, %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
}
|
||
|
delete graphBuf;
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::ReadInputFile() {
|
||
|
MS_ASSERT(msInputs.size() <= 1);
|
||
|
if (msInputs.empty()) {
|
||
|
return RET_OK;
|
||
|
}
|
||
|
Tensor *inTensor = msInputs.at(0);
|
||
|
MS_ASSERT(inTensor != nullptr);
|
||
|
|
||
|
size_t size;
|
||
|
char *binBuf = ReadFile(_flags->inDataPath.c_str(), &size);
|
||
|
if (binBuf == nullptr) {
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
auto tensorDataSize = inTensor->GetDataSize();
|
||
|
if (size != tensorDataSize) {
|
||
|
MS_LOGE("Input binary file size error, required: %zu, in fact: %zu", tensorDataSize, size);
|
||
|
delete binBuf;
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
inTensor->SetData(binBuf);
|
||
|
binBuf = nullptr;
|
||
|
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
// calibData is FP32
|
||
|
STATUS Benchmark::ReadCalibData() {
|
||
|
const char *calibDataPath = _flags->calibDataPath.c_str();
|
||
|
// read calib data
|
||
|
std::ifstream inFile(calibDataPath);
|
||
|
if (!inFile.good()) {
|
||
|
MS_LOGE("file: %s is not exist", calibDataPath);
|
||
|
return RET_PARAM_INVALID;
|
||
|
}
|
||
|
|
||
|
if (!inFile.is_open()) {
|
||
|
MS_LOGE("file: %s open failed", calibDataPath);
|
||
|
inFile.close();
|
||
|
return RET_PARAM_INVALID;
|
||
|
}
|
||
|
|
||
|
std::string line;
|
||
|
MS_LOGI("Start reading calibData file");
|
||
|
std::string tensorName;
|
||
|
while (!inFile.eof()) {
|
||
|
getline(inFile, line);
|
||
|
std::stringstream stringLine1(line);
|
||
|
size_t dim = 0;
|
||
|
stringLine1 >> tensorName >> dim;
|
||
|
std::vector<size_t> dims;
|
||
|
size_t shapeSize = 1;
|
||
|
for (size_t i = 0; i < dim; i++) {
|
||
|
size_t tmpDim;
|
||
|
stringLine1 >> tmpDim;
|
||
|
dims.push_back(tmpDim);
|
||
|
shapeSize *= tmpDim;
|
||
|
}
|
||
|
|
||
|
getline(inFile, line);
|
||
|
std::stringstream stringLine2(line);
|
||
|
std::vector<float> tensorData;
|
||
|
for (size_t i = 0; i < shapeSize; i++) {
|
||
|
float tmpData;
|
||
|
stringLine2 >> tmpData;
|
||
|
tensorData.push_back(tmpData);
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<CheckTensor> checkTensor(new CheckTensor(dims, tensorData));
|
||
|
this->calibData.insert(std::make_pair(tensorName, checkTensor.release()));
|
||
|
}
|
||
|
inFile.close();
|
||
|
MS_LOGI("Finish reading calibData file");
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
// tensorData need to be converter first
|
||
|
float Benchmark::CompareData(const std::string &nodeName, std::vector<int64_t> msShape, float *msTensorData) {
|
||
|
auto iter = this->calibData.find(nodeName);
|
||
|
if (iter != this->calibData.end()) {
|
||
|
std::vector<size_t> castedMSShape;
|
||
|
size_t shapeSize = 1;
|
||
|
for (int64_t dim : msShape) {
|
||
|
castedMSShape.push_back(size_t(dim));
|
||
|
shapeSize *= dim;
|
||
|
}
|
||
|
|
||
|
CheckTensor *calibTensor = iter->second;
|
||
|
if (calibTensor->shape != castedMSShape) {
|
||
|
std::ostringstream oss;
|
||
|
oss << "Shape of mslite output(";
|
||
|
for (auto dim : castedMSShape) {
|
||
|
oss << dim << ",";
|
||
|
}
|
||
|
oss << ") and shape source model output(";
|
||
|
for (auto dim : calibTensor->shape) {
|
||
|
oss << dim << ",";
|
||
|
}
|
||
|
oss << ") are different";
|
||
|
MS_LOGE("%s", oss.str().c_str());
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
float meanBias = 0;
|
||
|
std::ostringstream outputData;
|
||
|
outputData << "Data of node " << nodeName << " : ";
|
||
|
for (size_t j = 0; j < shapeSize; j++) {
|
||
|
if (j < printNum) {
|
||
|
outputData << msTensorData[j] << " ";
|
||
|
}
|
||
|
if (fabs(calibTensor->data.at(j)) > minFloatThr) {
|
||
|
double bias = fabs(msTensorData[j] - calibTensor->data.at(j)) / fabs(calibTensor->data.at(j));
|
||
|
meanBias += bias;
|
||
|
}
|
||
|
}
|
||
|
meanBias /= shapeSize;
|
||
|
MS_LOGI("%s", outputData.str().c_str());
|
||
|
|
||
|
if (meanBias <= minFloatThr) {
|
||
|
MS_LOGI("Mean bias of node %s : 0%%", nodeName.c_str());
|
||
|
} else {
|
||
|
MS_LOGI("Mean bias of node %s : %f%%", nodeName.c_str(), meanBias * percentage);
|
||
|
}
|
||
|
return meanBias;
|
||
|
} else {
|
||
|
MS_LOGI("%s is not in Source Model output", nodeName.c_str());
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::CompareOutput(const std::map<NODE_ID, std::vector<Tensor *>> &msOutputs) {
|
||
|
float totalBias = 0;
|
||
|
int totalSize = 0;
|
||
|
bool hasError = false;
|
||
|
for (const auto &msOutput : msOutputs) {
|
||
|
std::string nodeName = msOutput.first;
|
||
|
auto tensors = msOutput.second;
|
||
|
for (auto tensor : tensors) {
|
||
|
MS_ASSERT(tensor->GetData() != nullptr);
|
||
|
float bias = CompareData(nodeName, tensor->GetDims(), static_cast<float *>(tensor->GetData()));
|
||
|
if (bias >= 0) {
|
||
|
totalBias += bias;
|
||
|
totalSize++;
|
||
|
} else {
|
||
|
hasError = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!hasError) {
|
||
|
float meanBias;
|
||
|
if (totalSize != 0) {
|
||
|
meanBias = totalBias / totalSize * percentage;
|
||
|
} else {
|
||
|
meanBias = 0;
|
||
|
}
|
||
|
|
||
|
MS_LOGI("Mean bias all node : %f%%", meanBias);
|
||
|
|
||
|
if (meanBias > 1) {
|
||
|
MS_LOGE("Mean bias of all nodes is too big: %f%%", meanBias);
|
||
|
return RET_ERROR;
|
||
|
} else {
|
||
|
return RET_OK;
|
||
|
}
|
||
|
} else {
|
||
|
MS_LOGE("Error in CompareData");
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::MarkPerformance() {
|
||
|
MS_LOGI("Running warm up loops...");
|
||
|
for (int i = 0; i < _flags->warmUpLoopCount; i++) {
|
||
|
auto status = session->Run(msInputs);
|
||
|
if (status != RET_OK) {
|
||
|
MS_LOGE("Inference error %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MS_LOGI("Running benchmark loops...");
|
||
|
uint64_t timeMin = maxTimeThr;
|
||
|
uint64_t timeMax = 0;
|
||
|
uint64_t timeAvg = 0;
|
||
|
for (int i = 0; i < _flags->loopCount; i++) {
|
||
|
uint64_t start = GetTimeUs();
|
||
|
auto status = session->Run(msInputs);
|
||
|
if (status != RET_OK) {
|
||
|
MS_LOGE("Inference error %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
uint64_t end = GetTimeUs();
|
||
|
uint64_t time = end - start;
|
||
|
timeMin = std::min(timeMin, time);
|
||
|
timeMax = std::max(timeMax, time);
|
||
|
timeAvg += time;
|
||
|
|
||
|
msOutputs = session->GetAllOutput();
|
||
|
if (cleanData) {
|
||
|
for (auto &msOutput : msOutputs) {
|
||
|
for (auto &outputTensor : msOutput.second) {
|
||
|
delete outputTensor;
|
||
|
}
|
||
|
}
|
||
|
msOutputs.clear();
|
||
|
}
|
||
|
}
|
||
|
if (_flags->loopCount > 0) {
|
||
|
timeAvg /= _flags->loopCount;
|
||
|
MS_LOGI("MinRunTime = %f ms, MaxRuntime = %f ms, AvgRunTime = %f ms", timeMin / US2MS, timeMax / US2MS,
|
||
|
timeAvg / US2MS);
|
||
|
}
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::MarkAccuracy() {
|
||
|
MS_LOGI("MarkAccuracy");
|
||
|
|
||
|
auto status = session->Run(msInputs);
|
||
|
if (status != RET_OK) {
|
||
|
MS_LOGE("Inference error %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
msOutputs = session->GetAllOutput();
|
||
|
|
||
|
ReadCalibData();
|
||
|
status = CompareOutput(msOutputs);
|
||
|
if (cleanData) {
|
||
|
for (auto &msOutput : msOutputs) {
|
||
|
for (auto &outputTensor : msOutput.second) {
|
||
|
delete outputTensor;
|
||
|
}
|
||
|
}
|
||
|
msOutputs.clear();
|
||
|
}
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::CleanData() {
|
||
|
if (cleanData) {
|
||
|
for (auto &msInput : msInputs) {
|
||
|
delete msInput;
|
||
|
}
|
||
|
msInputs.clear();
|
||
|
for (auto &data : calibData) {
|
||
|
data.second->shape.clear();
|
||
|
data.second->data.clear();
|
||
|
delete data.second;
|
||
|
}
|
||
|
calibData.clear();
|
||
|
}
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::RunBenchmark() {
|
||
|
// Load graph
|
||
|
std::string comment = modelName;
|
||
|
|
||
|
MS_LOGI("start reading model file");
|
||
|
size_t size = 0;
|
||
|
char *graphBuf = ReadFile(_flags->modelPath.c_str(), &size);
|
||
|
if (graphBuf == nullptr) {
|
||
|
MS_LOGE("Load graph failed while running %s", comment.c_str());
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
|
||
|
uint64_t startPrepareTime = GetTimeUs();
|
||
|
session = CreateSession(graphBuf, size, ctx);
|
||
|
if (session == nullptr) {
|
||
|
delete graphBuf;
|
||
|
MS_LOGE("new session failed while running %s", comment.c_str());
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
uint64_t endPrepareTime = GetTimeUs();
|
||
|
MS_LOGI("PrepareTime = %f ms, ", (endPrepareTime - startPrepareTime) / US2MS);
|
||
|
|
||
|
// Load input
|
||
|
MS_LOGI("start generate input data");
|
||
|
auto status = LoadInput();
|
||
|
if (status != RET_OK) {
|
||
|
delete graphBuf;
|
||
|
MS_LOGE("Generate input data error");
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
if (!_flags->calibDataPath.empty()) {
|
||
|
status = MarkAccuracy();
|
||
|
if (status != RET_OK) {
|
||
|
delete graphBuf;
|
||
|
MS_LOGE("Run MarkAccuracy error: %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
} else {
|
||
|
status = MarkPerformance();
|
||
|
if (status != RET_OK) {
|
||
|
delete graphBuf;
|
||
|
MS_LOGE("Run MarkPerformance error: %d", status);
|
||
|
return status;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CleanData();
|
||
|
delete graphBuf;
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
STATUS Benchmark::Init() {
|
||
|
if (this->_flags == nullptr) {
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
MS_LOGI("ModelPath = %s", this->_flags->modelPath.c_str());
|
||
|
MS_LOGI("InDataPath = %s", this->_flags->inDataPath.c_str());
|
||
|
MS_LOGI("TensorDataType = %s", this->_flags->tensorDataTypeIn.c_str());
|
||
|
MS_LOGI("LoopCount = %d", this->_flags->loopCount);
|
||
|
MS_LOGI("WarmUpLoopCount = %d", this->_flags->warmUpLoopCount);
|
||
|
MS_LOGI("NumThreads = %d", this->_flags->numThreads);
|
||
|
MS_LOGI("calibDataPath = %s", this->_flags->calibDataPath.c_str());
|
||
|
|
||
|
this->_flags->inDataType = this->_flags->inDataTypeIn == "img" ? kImage : kBinary;
|
||
|
if (this->_flags->tensorDataTypeIn == "float") {
|
||
|
this->_flags->tensorDataType = DataType_DT_FLOAT;
|
||
|
}
|
||
|
|
||
|
if (_flags->modelPath.empty()) {
|
||
|
MS_LOGE("modelPath is required");
|
||
|
return RET_ERROR;
|
||
|
}
|
||
|
|
||
|
modelName = _flags->modelPath.substr(_flags->modelPath.find_last_of("/") + 1);
|
||
|
|
||
|
return RET_OK;
|
||
|
}
|
||
|
|
||
|
int RunBenchmark(int argc, const char **argv) {
|
||
|
BenchmarkFlags flags;
|
||
|
Option<std::string> err = flags.ParseFlags(argc, argv);
|
||
|
|
||
|
if (err.IsSome()) {
|
||
|
std::cerr << err.Get() << std::endl;
|
||
|
std::cerr << flags.Usage() << std::endl;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (flags.help) {
|
||
|
std::cerr << flags.Usage() << std::endl;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Benchmark mBenchmark(&flags);
|
||
|
auto status = mBenchmark.Init();
|
||
|
if (status != RET_OK) {
|
||
|
MS_LOGE("Benchmark init Error : %d", status);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
status = mBenchmark.RunBenchmark();
|
||
|
if (status != RET_OK) {
|
||
|
MS_LOGE("Run Benchmark Error : %d", status);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
MS_LOGI("end of benchmark");
|
||
|
return 0;
|
||
|
}
|
||
|
} // namespace predict
|
||
|
} // namespace mindspore
|