You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

benchmark.cc 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /**
  2. * Copyright 2019 Huawei Technologies Co., Ltd
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "benchmark/benchmark.h"
  17. #include <random>
  18. #include <limits>
  19. #include <algorithm>
  20. #include <utility>
  21. #include <memory>
  22. #include "include/session.h"
  23. namespace mindspore {
  24. namespace predict {
  25. STATUS Benchmark::GenerateRandomData(size_t size, void *data) {
  26. MS_ASSERT(data != nullptr);
  27. char *castedData = static_cast<char *>(data);
  28. for (size_t i = 0; i < size; i++) {
  29. castedData[i] = static_cast<char>(i);
  30. }
  31. return RET_OK;
  32. }
  33. STATUS Benchmark::GenerateInputData() {
  34. for (Tensor *tensor : msInputs) {
  35. MS_ASSERT(tensor != nullptr);
  36. auto ret = tensor->MallocData();
  37. if (ret != RET_OK) {
  38. MS_LOGE("MallocData for inTensor failed %d", ret);
  39. return ret;
  40. }
  41. MS_ASSERT(tensor->GetData() != nullptr);
  42. auto tensorByteSize = tensor->GetDataSize();
  43. auto status = GenerateRandomData(tensorByteSize, tensor->GetData());
  44. if (status != RET_OK) {
  45. MS_LOGE("GenerateRandomData for inTensor failed %d", status);
  46. return status;
  47. }
  48. }
  49. return RET_OK;
  50. }
  51. STATUS Benchmark::LoadInput() {
  52. size_t size = 0;
  53. char *graphBuf = ReadFile(_flags->modelPath.c_str(), &size);
  54. if (graphBuf == nullptr) {
  55. MS_LOGE("Load graph failed, path %s", _flags->modelPath.c_str());
  56. return RET_ERROR;
  57. }
  58. this->msInputs = session->GetInput();
  59. if (_flags->inDataPath.empty()) {
  60. auto status = GenerateInputData();
  61. if (status != RET_OK) {
  62. delete graphBuf;
  63. MS_LOGE("Generate input data error %d", status);
  64. return status;
  65. }
  66. } else {
  67. auto status = ReadInputFile();
  68. if (status != RET_OK) {
  69. delete graphBuf;
  70. MS_LOGE("ReadInputFile error, %d", status);
  71. return status;
  72. }
  73. }
  74. delete graphBuf;
  75. return RET_OK;
  76. }
  77. STATUS Benchmark::ReadInputFile() {
  78. MS_ASSERT(msInputs.size() <= 1);
  79. if (msInputs.empty()) {
  80. return RET_OK;
  81. }
  82. Tensor *inTensor = msInputs.at(0);
  83. MS_ASSERT(inTensor != nullptr);
  84. size_t size;
  85. char *binBuf = ReadFile(_flags->inDataPath.c_str(), &size);
  86. if (binBuf == nullptr) {
  87. return RET_ERROR;
  88. }
  89. auto tensorDataSize = inTensor->GetDataSize();
  90. if (size != tensorDataSize) {
  91. MS_LOGE("Input binary file size error, required: %zu, in fact: %zu", tensorDataSize, size);
  92. delete binBuf;
  93. return RET_ERROR;
  94. }
  95. inTensor->SetData(binBuf);
  96. binBuf = nullptr;
  97. return RET_OK;
  98. }
  99. // calibData is FP32
  100. STATUS Benchmark::ReadCalibData() {
  101. const char *calibDataPath = _flags->calibDataPath.c_str();
  102. // read calib data
  103. std::ifstream inFile(calibDataPath);
  104. if (!inFile.good()) {
  105. MS_LOGE("file: %s is not exist", calibDataPath);
  106. return RET_PARAM_INVALID;
  107. }
  108. if (!inFile.is_open()) {
  109. MS_LOGE("file: %s open failed", calibDataPath);
  110. inFile.close();
  111. return RET_PARAM_INVALID;
  112. }
  113. std::string line;
  114. MS_LOGI("Start reading calibData file");
  115. std::string tensorName;
  116. while (!inFile.eof()) {
  117. getline(inFile, line);
  118. std::stringstream stringLine1(line);
  119. size_t dim = 0;
  120. stringLine1 >> tensorName >> dim;
  121. std::vector<size_t> dims;
  122. size_t shapeSize = 1;
  123. for (size_t i = 0; i < dim; i++) {
  124. size_t tmpDim;
  125. stringLine1 >> tmpDim;
  126. dims.push_back(tmpDim);
  127. shapeSize *= tmpDim;
  128. }
  129. getline(inFile, line);
  130. std::stringstream stringLine2(line);
  131. std::vector<float> tensorData;
  132. for (size_t i = 0; i < shapeSize; i++) {
  133. float tmpData;
  134. stringLine2 >> tmpData;
  135. tensorData.push_back(tmpData);
  136. }
  137. std::unique_ptr<CheckTensor> checkTensor(new CheckTensor(dims, tensorData));
  138. this->calibData.insert(std::make_pair(tensorName, checkTensor.release()));
  139. }
  140. inFile.close();
  141. MS_LOGI("Finish reading calibData file");
  142. return RET_OK;
  143. }
  144. // tensorData need to be converter first
  145. float Benchmark::CompareData(const std::string &nodeName, std::vector<int64_t> msShape, float *msTensorData) {
  146. auto iter = this->calibData.find(nodeName);
  147. if (iter != this->calibData.end()) {
  148. std::vector<size_t> castedMSShape;
  149. size_t shapeSize = 1;
  150. for (int64_t dim : msShape) {
  151. castedMSShape.push_back(size_t(dim));
  152. shapeSize *= dim;
  153. }
  154. CheckTensor *calibTensor = iter->second;
  155. if (calibTensor->shape != castedMSShape) {
  156. std::ostringstream oss;
  157. oss << "Shape of mslite output(";
  158. for (auto dim : castedMSShape) {
  159. oss << dim << ",";
  160. }
  161. oss << ") and shape source model output(";
  162. for (auto dim : calibTensor->shape) {
  163. oss << dim << ",";
  164. }
  165. oss << ") are different";
  166. MS_LOGE("%s", oss.str().c_str());
  167. return -1;
  168. }
  169. float meanBias = 0;
  170. std::ostringstream outputData;
  171. outputData << "Data of node " << nodeName << " : ";
  172. for (size_t j = 0; j < shapeSize; j++) {
  173. if (j < printNum) {
  174. outputData << msTensorData[j] << " ";
  175. }
  176. if (fabs(calibTensor->data.at(j)) > minFloatThr) {
  177. double bias = fabs(msTensorData[j] - calibTensor->data.at(j)) / fabs(calibTensor->data.at(j));
  178. meanBias += bias;
  179. }
  180. }
  181. meanBias /= shapeSize;
  182. MS_LOGI("%s", outputData.str().c_str());
  183. if (meanBias <= minFloatThr) {
  184. MS_LOGI("Mean bias of node %s : 0%%", nodeName.c_str());
  185. } else {
  186. MS_LOGI("Mean bias of node %s : %f%%", nodeName.c_str(), meanBias * percentage);
  187. }
  188. return meanBias;
  189. } else {
  190. MS_LOGI("%s is not in Source Model output", nodeName.c_str());
  191. return -1;
  192. }
  193. }
  194. STATUS Benchmark::CompareOutput(const std::map<NODE_ID, std::vector<Tensor *>> &msOutputs) {
  195. float totalBias = 0;
  196. int totalSize = 0;
  197. bool hasError = false;
  198. for (const auto &msOutput : msOutputs) {
  199. std::string nodeName = msOutput.first;
  200. auto tensors = msOutput.second;
  201. for (auto tensor : tensors) {
  202. MS_ASSERT(tensor->GetData() != nullptr);
  203. float bias = CompareData(nodeName, tensor->GetDims(), static_cast<float *>(tensor->GetData()));
  204. if (bias >= 0) {
  205. totalBias += bias;
  206. totalSize++;
  207. } else {
  208. hasError = true;
  209. break;
  210. }
  211. }
  212. }
  213. if (!hasError) {
  214. float meanBias;
  215. if (totalSize != 0) {
  216. meanBias = totalBias / totalSize * percentage;
  217. } else {
  218. meanBias = 0;
  219. }
  220. MS_LOGI("Mean bias all node : %f%%", meanBias);
  221. if (meanBias > 1) {
  222. MS_LOGE("Mean bias of all nodes is too big: %f%%", meanBias);
  223. return RET_ERROR;
  224. } else {
  225. return RET_OK;
  226. }
  227. } else {
  228. MS_LOGE("Error in CompareData");
  229. return RET_ERROR;
  230. }
  231. }
  232. STATUS Benchmark::MarkPerformance() {
  233. MS_LOGI("Running warm up loops...");
  234. for (int i = 0; i < _flags->warmUpLoopCount; i++) {
  235. auto status = session->Run(msInputs);
  236. if (status != RET_OK) {
  237. MS_LOGE("Inference error %d", status);
  238. return status;
  239. }
  240. }
  241. MS_LOGI("Running benchmark loops...");
  242. uint64_t timeMin = maxTimeThr;
  243. uint64_t timeMax = 0;
  244. uint64_t timeAvg = 0;
  245. for (int i = 0; i < _flags->loopCount; i++) {
  246. uint64_t start = GetTimeUs();
  247. auto status = session->Run(msInputs);
  248. if (status != RET_OK) {
  249. MS_LOGE("Inference error %d", status);
  250. return status;
  251. }
  252. uint64_t end = GetTimeUs();
  253. uint64_t time = end - start;
  254. timeMin = std::min(timeMin, time);
  255. timeMax = std::max(timeMax, time);
  256. timeAvg += time;
  257. msOutputs = session->GetAllOutput();
  258. if (cleanData) {
  259. for (auto &msOutput : msOutputs) {
  260. for (auto &outputTensor : msOutput.second) {
  261. delete outputTensor;
  262. }
  263. }
  264. msOutputs.clear();
  265. }
  266. }
  267. if (_flags->loopCount > 0) {
  268. timeAvg /= _flags->loopCount;
  269. MS_LOGI("MinRunTime = %f ms, MaxRuntime = %f ms, AvgRunTime = %f ms", timeMin / US2MS, timeMax / US2MS,
  270. timeAvg / US2MS);
  271. }
  272. return RET_OK;
  273. }
  274. STATUS Benchmark::MarkAccuracy() {
  275. MS_LOGI("MarkAccuracy");
  276. auto status = session->Run(msInputs);
  277. if (status != RET_OK) {
  278. MS_LOGE("Inference error %d", status);
  279. return status;
  280. }
  281. msOutputs = session->GetAllOutput();
  282. ReadCalibData();
  283. status = CompareOutput(msOutputs);
  284. if (cleanData) {
  285. for (auto &msOutput : msOutputs) {
  286. for (auto &outputTensor : msOutput.second) {
  287. delete outputTensor;
  288. }
  289. }
  290. msOutputs.clear();
  291. }
  292. return status;
  293. }
  294. STATUS Benchmark::CleanData() {
  295. if (cleanData) {
  296. for (auto &msInput : msInputs) {
  297. delete msInput;
  298. }
  299. msInputs.clear();
  300. for (auto &data : calibData) {
  301. data.second->shape.clear();
  302. data.second->data.clear();
  303. delete data.second;
  304. }
  305. calibData.clear();
  306. }
  307. return RET_OK;
  308. }
  309. STATUS Benchmark::RunBenchmark() {
  310. // Load graph
  311. std::string comment = modelName;
  312. MS_LOGI("start reading model file");
  313. size_t size = 0;
  314. char *graphBuf = ReadFile(_flags->modelPath.c_str(), &size);
  315. if (graphBuf == nullptr) {
  316. MS_LOGE("Load graph failed while running %s", comment.c_str());
  317. return RET_ERROR;
  318. }
  319. uint64_t startPrepareTime = GetTimeUs();
  320. session = CreateSession(graphBuf, size, ctx);
  321. if (session == nullptr) {
  322. delete graphBuf;
  323. MS_LOGE("new session failed while running %s", comment.c_str());
  324. return RET_ERROR;
  325. }
  326. uint64_t endPrepareTime = GetTimeUs();
  327. MS_LOGI("PrepareTime = %f ms, ", (endPrepareTime - startPrepareTime) / US2MS);
  328. // Load input
  329. MS_LOGI("start generate input data");
  330. auto status = LoadInput();
  331. if (status != RET_OK) {
  332. delete graphBuf;
  333. MS_LOGE("Generate input data error");
  334. return status;
  335. }
  336. if (!_flags->calibDataPath.empty()) {
  337. status = MarkAccuracy();
  338. if (status != RET_OK) {
  339. delete graphBuf;
  340. MS_LOGE("Run MarkAccuracy error: %d", status);
  341. return status;
  342. }
  343. } else {
  344. status = MarkPerformance();
  345. if (status != RET_OK) {
  346. delete graphBuf;
  347. MS_LOGE("Run MarkPerformance error: %d", status);
  348. return status;
  349. }
  350. }
  351. CleanData();
  352. delete graphBuf;
  353. return RET_OK;
  354. }
  355. STATUS Benchmark::Init() {
  356. if (this->_flags == nullptr) {
  357. return RET_ERROR;
  358. }
  359. MS_LOGI("ModelPath = %s", this->_flags->modelPath.c_str());
  360. MS_LOGI("InDataPath = %s", this->_flags->inDataPath.c_str());
  361. MS_LOGI("TensorDataType = %s", this->_flags->tensorDataTypeIn.c_str());
  362. MS_LOGI("LoopCount = %d", this->_flags->loopCount);
  363. MS_LOGI("WarmUpLoopCount = %d", this->_flags->warmUpLoopCount);
  364. MS_LOGI("NumThreads = %d", this->_flags->numThreads);
  365. MS_LOGI("calibDataPath = %s", this->_flags->calibDataPath.c_str());
  366. this->_flags->inDataType = this->_flags->inDataTypeIn == "img" ? kImage : kBinary;
  367. if (this->_flags->tensorDataTypeIn == "float") {
  368. this->_flags->tensorDataType = DataType_DT_FLOAT;
  369. }
  370. if (_flags->modelPath.empty()) {
  371. MS_LOGE("modelPath is required");
  372. return RET_ERROR;
  373. }
  374. modelName = _flags->modelPath.substr(_flags->modelPath.find_last_of("/") + 1);
  375. return RET_OK;
  376. }
  377. int RunBenchmark(int argc, const char **argv) {
  378. BenchmarkFlags flags;
  379. Option<std::string> err = flags.ParseFlags(argc, argv);
  380. if (err.IsSome()) {
  381. std::cerr << err.Get() << std::endl;
  382. std::cerr << flags.Usage() << std::endl;
  383. return -1;
  384. }
  385. if (flags.help) {
  386. std::cerr << flags.Usage() << std::endl;
  387. return 0;
  388. }
  389. Benchmark mBenchmark(&flags);
  390. auto status = mBenchmark.Init();
  391. if (status != RET_OK) {
  392. MS_LOGE("Benchmark init Error : %d", status);
  393. return 1;
  394. }
  395. status = mBenchmark.RunBenchmark();
  396. if (status != RET_OK) {
  397. MS_LOGE("Run Benchmark Error : %d", status);
  398. return 1;
  399. }
  400. MS_LOGI("end of benchmark");
  401. return 0;
  402. }
  403. } // namespace predict
  404. } // namespace mindspore