/** * 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 #include #include #include #include #include "include/session.h" namespace mindspore { namespace predict { STATUS Benchmark::GenerateRandomData(size_t size, void *data) { MS_ASSERT(data != nullptr); char *castedData = static_cast(data); for (size_t i = 0; i < size; i++) { castedData[i] = static_cast(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 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 tensorData; for (size_t i = 0; i < shapeSize; i++) { float tmpData; stringLine2 >> tmpData; tensorData.push_back(tmpData); } std::unique_ptr 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 msShape, float *msTensorData) { auto iter = this->calibData.find(nodeName); if (iter != this->calibData.end()) { std::vector 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> &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(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 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