mindspore/predict/benchmark/benchmark.cc

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