From 7f89fa1047a756034c49ba6d4b560398d2d9dac5 Mon Sep 17 00:00:00 2001 From: jianghui58 Date: Wed, 28 Apr 2021 09:38:11 +0800 Subject: [PATCH] benchmark add dump data feature --- mindspore/lite/CMakeLists.txt | 2 +- mindspore/lite/src/common/file_utils.cc | 69 ++++++ mindspore/lite/src/common/file_utils.h | 2 + mindspore/lite/tools/benchmark/CMakeLists.txt | 5 +- mindspore/lite/tools/benchmark/benchmark.cc | 217 +++++++++++++++--- mindspore/lite/tools/benchmark/benchmark.h | 16 +- 6 files changed, 280 insertions(+), 31 deletions(-) diff --git a/mindspore/lite/CMakeLists.txt b/mindspore/lite/CMakeLists.txt index e5406d4f0a..76d2674eb8 100644 --- a/mindspore/lite/CMakeLists.txt +++ b/mindspore/lite/CMakeLists.txt @@ -132,7 +132,7 @@ if(SUPPORT_GPU STREQUAL vulkan) include(${TOP_DIR}/cmake/external_libs/vulkan.cmake) endif() -if(ENABLE_CONVERTER OR BUILD_MINDDATA STREQUAL "full" OR BUILD_MINDDATA STREQUAL "wrapper") +if(ENABLE_CONVERTER OR BUILD_MINDDATA STREQUAL "full" OR BUILD_MINDDATA STREQUAL "wrapper" OR PLATFORM_ARM) include(${TOP_DIR}/cmake/external_libs/json.cmake) endif() diff --git a/mindspore/lite/src/common/file_utils.cc b/mindspore/lite/src/common/file_utils.cc index 8daf048350..31348c26dd 100644 --- a/mindspore/lite/src/common/file_utils.cc +++ b/mindspore/lite/src/common/file_utils.cc @@ -15,13 +15,33 @@ */ #include "src/common/file_utils.h" +#ifdef _WIN32 +#include +#include +#else #include +#include +#include +#include +#endif + #include #include #include "securec/include/securec.h" +#ifdef _WIN32 +#define ACCESS(file_path, access_mode) _access(file_path, access_mode) +#define MKDIR(file_path) _mkdir(file_path) +#else +#define ACCESS(file_path, accessMode) access(file_path, accessMode) +#define MKDIR(file_path) mkdir(file_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) +#endif + namespace mindspore { namespace lite { +namespace { +const int MAXIMUM_NUMBERS_OF_FOLDER = 1000; +} char *ReadFile(const char *file, size_t *size) { if (file == nullptr) { MS_LOG(ERROR) << "file is nullptr"; @@ -82,5 +102,54 @@ std::string RealPath(const char *path) { std::string res = resolved_path.get(); return res; } + +int CreateOutputDir(std::string *file_path) { + if (file_path->empty()) { + MS_LOG(ERROR) << "input file path is empty."; + return RET_ERROR; + } else if (file_path->size() > PATH_MAX) { + MS_LOG(ERROR) << "input file path is too long"; + return RET_ERROR; + } + + for (size_t i = 0; i < file_path->size(); i++) { + if ((*file_path).at(i) == '\\' || (*file_path).at(i) == '/') { + if (ACCESS(file_path->substr(0, i + 1).c_str(), F_OK) != 0) { + int ret = MKDIR(file_path->substr(0, i + 1).c_str()); + if (ret != RET_OK) { + MS_LOG(ERROR) << "mkdir failed. " << file_path->substr(0, i + 1); + return RET_ERROR; + } + } + } + } + + if (file_path->back() != '\\' && file_path->back() != '/') { + if (ACCESS(file_path->c_str(), F_OK) != 0) { + int ret = MKDIR(file_path->c_str()); + if (ret != RET_OK) { + MS_LOG(ERROR) << "mkdir failed. " << file_path; + return RET_ERROR; + } + } + } + + int count = 1; + while (ACCESS((*file_path + "/" + std::to_string(count)).c_str(), F_OK) == 0) { + MS_LOG(DEBUG) << "current file_path has existed, file_path cnt plus 1."; // such as: /xxx/1 ==> /xxx/2 + count++; + if (count > MAXIMUM_NUMBERS_OF_FOLDER) { + MS_LOG(ERROR) << "the number of file folders exceeds the upper limit."; + return RET_ERROR; + } + } + *file_path += "/" + std::to_string(count); + int ret = MKDIR(file_path->c_str()); + if (ret != RET_OK) { + MS_LOG(ERROR) << "mkdir failed. " << file_path->c_str(); + return RET_ERROR; + } + return RET_OK; +} } // namespace lite } // namespace mindspore diff --git a/mindspore/lite/src/common/file_utils.h b/mindspore/lite/src/common/file_utils.h index fbd7668e2e..e610b1b4ee 100644 --- a/mindspore/lite/src/common/file_utils.h +++ b/mindspore/lite/src/common/file_utils.h @@ -33,6 +33,8 @@ char *ReadFile(const char *file, size_t *size); std::string RealPath(const char *path); +int CreateOutputDir(std::string *dir); + template void WriteToTxt(const std::string &file_path, void *data, size_t element_size) { std::ofstream out_file; diff --git a/mindspore/lite/tools/benchmark/CMakeLists.txt b/mindspore/lite/tools/benchmark/CMakeLists.txt index 425490c891..f8b5855278 100644 --- a/mindspore/lite/tools/benchmark/CMakeLists.txt +++ b/mindspore/lite/tools/benchmark/CMakeLists.txt @@ -4,6 +4,7 @@ set(COMMON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/file_utils.cc ${CMAKE_CURRENT_SOURCE_DIR}/../../src/common/utils.cc ) + add_executable(benchmark ${CMAKE_CURRENT_SOURCE_DIR}/main.cc ${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cc @@ -12,7 +13,7 @@ add_executable(benchmark add_dependencies(benchmark fbs_src) if(PLATFORM_ARM32 OR PLATFORM_ARM64) - target_link_libraries(benchmark mindspore-lite) + target_link_libraries(benchmark mindspore-lite mindspore::json) else() - target_link_libraries(benchmark mindspore-lite pthread) + target_link_libraries(benchmark mindspore-lite mindspore::json pthread) endif() diff --git a/mindspore/lite/tools/benchmark/benchmark.cc b/mindspore/lite/tools/benchmark/benchmark.cc index b5b187f007..214bdba9d7 100644 --- a/mindspore/lite/tools/benchmark/benchmark.cc +++ b/mindspore/lite/tools/benchmark/benchmark.cc @@ -38,6 +38,28 @@ namespace lite { static const char *DELIM_COLON = ":"; static const char *DELIM_COMMA = ","; static const char *DELIM_SLASH = "/"; +static const std::unordered_map TYPE_ID_MAP{ + {kNumberTypeFloat16, "Float16"}, {kNumberTypeFloat, "Float32"}, {kNumberTypeFloat32, "Float32"}, + {kNumberTypeInt8, "Int8"}, {kNumberTypeInt16, "Int16"}, {kNumberTypeInt, "Int32"}, + {kNumberTypeInt32, "Int32"}, {kNumberTypeUInt8, "UInt8"}, {kNumberTypeUInt16, "UInt16"}, + {kNumberTypeUInt, "UInt32"}, {kNumberTypeUInt32, "UInt32"}, {kObjectTypeString, "String"}, + {kNumberTypeBool, "Bool"}, {kObjectTypeTensorType, "Tensor"}}; +static const std::unordered_map TENSOR_FORMAT_MAP{ + {schema::Format_NCHW, "NCHW"}, {schema::Format_NHWC, "NHWC"}, {schema::Format_NHWC4, "NHWC4"}, + {schema::Format_HWKC, "HWKC"}, {schema::Format_HWCK, "HWCK"}, {schema::Format_KCHW, "KCHW"}, + {schema::Format_CKHW, "CKHW"}, {schema::Format_KHWC, "KHWC"}, {schema::Format_CHWK, "CHWK"}, + {schema::Format_HW, "HW"}, {schema::Format_HW4, "HW4"}, {schema::Format_NC, "NC"}, + {schema::Format_NC4, "NC4"}, {schema::Format_NC4HW4, "NC4HW4"}, {schema::Format_NCDHW, "NCDHW"}}; + +namespace dump { +constexpr auto kConfigPath = "MINDSPORE_DUMP_CONFIG"; +constexpr auto kSettings = "common_dump_settings"; +constexpr auto kMode = "dump_mode"; +constexpr auto kPath = "path"; +constexpr auto kNetName = "net_name"; +constexpr auto kInputOutput = "input_output"; +constexpr auto kKernels = "kernels"; +} // namespace dump int Benchmark::GenerateRandomData(size_t size, void *data, TypeId data_type) { MS_ASSERT(data != nullptr); @@ -245,6 +267,30 @@ int Benchmark::ReadTensorData(std::ifstream &in_file_stream, const std::string & return RET_OK; } +void Benchmark::InitContext(const std::shared_ptr &context) { + auto &cpu_device_ctx = context->device_list_[0]; + if (flags_->cpu_bind_mode_ == MID_CPU || flags_->cpu_bind_mode_ == HIGHER_CPU) { + cpu_device_ctx.device_info_.cpu_device_info_.cpu_bind_mode_ = CpuBindMode(flags_->cpu_bind_mode_); + } else { + cpu_device_ctx.device_info_.cpu_device_info_.cpu_bind_mode_ = NO_BIND; + } + cpu_device_ctx.device_info_.cpu_device_info_.enable_float16_ = flags_->enable_fp16_; + + if (flags_->device_ == "GPU") { + DeviceContext gpu_device_ctx{DT_GPU, {false}}; + gpu_device_ctx.device_info_.gpu_device_info_.enable_float16_ = flags_->enable_fp16_; + context->device_list_.push_back(gpu_device_ctx); + } + + if (flags_->device_ == "NPU") { + DeviceContext npu_device_ctx{DT_NPU}; + npu_device_ctx.device_info_.npu_device_info_.frequency_ = 3; + context->device_list_.push_back(npu_device_ctx); + } + + context->thread_num_ = flags_->num_threads_; +} + int Benchmark::CompareOutput() { std::cout << "================ Comparing Output data ================" << std::endl; float total_bias = 0; @@ -575,27 +621,7 @@ int Benchmark::RunBenchmark() { return RET_ERROR; } - auto &cpu_device_ctx = context->device_list_[0]; - if (flags_->cpu_bind_mode_ == MID_CPU || flags_->cpu_bind_mode_ == HIGHER_CPU) { - cpu_device_ctx.device_info_.cpu_device_info_.cpu_bind_mode_ = CpuBindMode(flags_->cpu_bind_mode_); - } else { - cpu_device_ctx.device_info_.cpu_device_info_.cpu_bind_mode_ = NO_BIND; - } - cpu_device_ctx.device_info_.cpu_device_info_.enable_float16_ = flags_->enable_fp16_; - - if (flags_->device_ == "GPU") { - DeviceContext gpu_device_ctx{DT_GPU, {false}}; - gpu_device_ctx.device_info_.gpu_device_info_.enable_float16_ = flags_->enable_fp16_; - context->device_list_.push_back(gpu_device_ctx); - } - - if (flags_->device_ == "NPU") { - DeviceContext npu_device_ctx{DT_NPU}; - npu_device_ctx.device_info_.npu_device_info_.frequency_ = 3; - context->device_list_.push_back(npu_device_ctx); - } - - context->thread_num_ = flags_->num_threads_; + (void)InitContext(context); session_ = session::LiteSession::CreateSession(context.get()); if (session_ == nullptr) { @@ -617,7 +643,7 @@ int Benchmark::RunBenchmark() { return ret; } } - if (model != nullptr) { + if (model != nullptr && !flags_->dump_tensor_data_) { model->Free(); } @@ -655,6 +681,9 @@ int Benchmark::RunBenchmark() { return status; } } + if (flags_->dump_tensor_data_) { + std::cout << "Dumped file is saved to : " + dump_file_output_dir_ << std::endl; + } return RET_OK; } @@ -860,7 +889,7 @@ std::string DumpMSTensor(tensor::MSTensor *tensor) { for (auto &dim : tensor->shape()) { oss << " " << dim; } - oss << std::endl << "Data:"; + oss << std::endl << " Data:"; switch (tensor->data_type()) { case kNumberTypeFloat32: { oss << DataToString(tensor->data(), tensor->ElementsNum()); @@ -883,9 +912,31 @@ std::string DumpMSTensor(tensor::MSTensor *tensor) { } return oss.str(); } + +std::string GenerateOutputFileName(tensor::MSTensor *tensor, const std::string &op_name, const std::string &file_type, + const size_t &idx) { + std::string file_name = op_name; + auto pos = file_name.find_first_of('/'); + while (pos != std::string::npos) { + file_name.replace(pos, 1, "."); + pos = file_name.find_first_of('/'); + } + file_name += "_" + file_type + "_" + std::to_string(idx) + "_shape_"; + for (const auto &dim : tensor->shape()) { + file_name += std::to_string(dim) + "_"; + } + if (TYPE_ID_MAP.find(tensor->data_type()) != TYPE_ID_MAP.end()) { + file_name += TYPE_ID_MAP.at(tensor->data_type()); + } + auto tensor_format = static_cast(tensor)->format(); + if (TENSOR_FORMAT_MAP.find(tensor_format) != TENSOR_FORMAT_MAP.end()) { + file_name += "_" + TENSOR_FORMAT_MAP.at(tensor_format) + ".bin"; + } + return file_name; +} } // namespace -int Benchmark::InitDumpProfilingCallbackParameter() { +int Benchmark::InitPrintTensorDataCallbackParameter() { // before callback before_call_back_ = [&](const std::vector &before_inputs, const std::vector &before_outputs, @@ -910,6 +961,105 @@ int Benchmark::InitDumpProfilingCallbackParameter() { }; return RET_OK; } +int Benchmark::InitDumpTensorDataCallbackParameter() { + // before callback + before_call_back_ = [&](const std::vector &before_inputs, + const std::vector &before_outputs, + const CallBackParam &call_param) { return true; }; + + // after callback + after_call_back_ = [&](const std::vector &after_inputs, + const std::vector &after_outputs, + const CallBackParam &call_param) { + auto dump_mode = dump_cfg_json_[dump::kSettings][dump::kMode].get(); + auto input_output_mode = dump_cfg_json_[dump::kSettings][dump::kInputOutput].get(); + auto kernels = dump_cfg_json_[dump::kSettings][dump::kKernels].get>(); + + if (dump_mode == 0 || std::find(kernels.begin(), kernels.end(), call_param.node_name) != kernels.end()) { + if (input_output_mode == 0 || input_output_mode == 1) { + for (size_t i = 0; i < after_inputs.size(); i++) { + auto ms_tensor = after_inputs.at(i); + auto file_name = GenerateOutputFileName(ms_tensor, call_param.node_name, "input", i); + auto abs_file_path = dump_file_output_dir_ + "/" + file_name; + if (WriteToBin(abs_file_path, ms_tensor->data(), ms_tensor->Size()) != RET_OK) { // save to file + MS_LOG(ERROR) << "write tensor data to file failed."; + return false; + } + } + } + } + if (dump_mode == 0 || std::find(kernels.begin(), kernels.end(), call_param.node_name) != kernels.end()) { + if (input_output_mode == 0 || input_output_mode == 2) { + for (size_t i = 0; i < after_outputs.size(); i++) { + auto ms_tensor = after_outputs.at(i); + auto file_name = GenerateOutputFileName(ms_tensor, call_param.node_name, "output", i); + auto abs_file_path = dump_file_output_dir_ + "/" + file_name; + if (WriteToBin(abs_file_path, ms_tensor->data(), ms_tensor->Size()) != RET_OK) { // save to file + MS_LOG(ERROR) << "write tensor data to file failed."; + return false; + } + } + } + } + return true; + }; + return RET_OK; +} + +int Benchmark::InitDumpConfigFromJson(char *path) { + auto real_path = RealPath(path); + std::ifstream ifs(real_path); + if (!ifs.good()) { + MS_LOG(ERROR) << "file: " << real_path << " is not exist"; + return RET_ERROR; + } + if (!ifs.is_open()) { + MS_LOG(ERROR) << "file: " << real_path << " open failed"; + return RET_ERROR; + } + + try { + dump_cfg_json_ = nlohmann::json::parse(ifs); + } catch (const nlohmann::json::parse_error &error) { + MS_LOG(ERROR) << "parse json file failed, please check your file."; + return RET_ERROR; + } + if (dump_cfg_json_[dump::kSettings] == nullptr) { + MS_LOG(ERROR) << "\"common_dump_settings\" is required."; + return RET_ERROR; + } + if (dump_cfg_json_[dump::kSettings][dump::kMode] == nullptr) { + MS_LOG(ERROR) << "\"dump_mode\" is required."; + return RET_ERROR; + } + if (dump_cfg_json_[dump::kSettings][dump::kPath] == nullptr) { + MS_LOG(ERROR) << "\"path\" is required."; + return RET_ERROR; + } + if (dump_cfg_json_[dump::kSettings][dump::kNetName] == nullptr) { + dump_cfg_json_[dump::kSettings][dump::kNetName] = "Default"; + } + if (dump_cfg_json_[dump::kSettings][dump::kInputOutput] == nullptr) { + dump_cfg_json_[dump::kSettings][dump::kInputOutput] = 0; + } + if (dump_cfg_json_[dump::kSettings][dump::kKernels] != nullptr && + !dump_cfg_json_[dump::kSettings][dump::kKernels].empty()) { + if (dump_cfg_json_[dump::kSettings][dump::kMode] == 0) { + MS_LOG(ERROR) << R"("dump_mode" should be 1 when "kernels" isn't empty.)"; + return RET_ERROR; + } + } + + dump_file_output_dir_ = dump_cfg_json_[dump::kSettings][dump::kPath].get() + "/" + + dump_cfg_json_[dump::kSettings][dump::kNetName].get(); + auto status = CreateOutputDir(&dump_file_output_dir_); + if (status != RET_OK) { + MS_LOG(ERROR) << "create data output directory failed."; + return RET_ERROR; + } + + return RET_OK; +} int Benchmark::InitCallbackParameter() { int ret = RET_OK; @@ -917,8 +1067,10 @@ int Benchmark::InitCallbackParameter() { ret = InitTimeProfilingCallbackParameter(); } else if (flags_->perf_profiling_) { ret = InitPerfProfilingCallbackParameter(); - } else if (flags_->dump_profiling_) { - ret = InitDumpProfilingCallbackParameter(); + } else if (flags_->print_tensor_data_) { + ret = InitPrintTensorDataCallbackParameter(); + } else if (flags_->dump_tensor_data_) { + ret = InitDumpTensorDataCallbackParameter(); } return ret; } @@ -1002,6 +1154,19 @@ int Benchmark::Init() { if (flags_->time_profiling_ && flags_->perf_profiling_) { MS_LOG(INFO) << "time_profiling is enabled, will not run perf_profiling."; } + + // get dump data output path + auto dump_cfg_path = std::getenv(dump::kConfigPath); + if (dump_cfg_path != nullptr) { + flags_->dump_tensor_data_ = true; + if (InitDumpConfigFromJson(dump_cfg_path) != RET_OK) { + MS_LOG(ERROR) << "parse dump config file failed."; + return RET_ERROR; + } + } else { + MS_LOG(INFO) << "No MINDSPORE_DUMP_CONFIG in env, don't need to dump data"; + } + auto status = InitCallbackParameter(); if (status != RET_OK) { MS_LOG(ERROR) << "Init callback Parameter failed."; diff --git a/mindspore/lite/tools/benchmark/benchmark.h b/mindspore/lite/tools/benchmark/benchmark.h index a33a367f5e..7428e65069 100644 --- a/mindspore/lite/tools/benchmark/benchmark.h +++ b/mindspore/lite/tools/benchmark/benchmark.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "include/model.h" #include "tools/common/flag_parser.h" #include "src/common/file_utils.h" @@ -125,7 +126,8 @@ class MS_API BenchmarkFlags : public virtual FlagParser { bool time_profiling_ = false; bool perf_profiling_ = false; std::string perf_event_ = "CYCLE"; - bool dump_profiling_ = false; + bool dump_tensor_data_ = false; + bool print_tensor_data_ = false; }; class MS_API Benchmark { @@ -152,6 +154,8 @@ class MS_API Benchmark { int ReadTensorData(std::ifstream &in_file_stream, const std::string &tensor_name, const std::vector &dims); + void InitContext(const std::shared_ptr &context); + int CompareOutput(); tensor::MSTensor *GetTensorByNameOrShape(const std::string &node_or_tensor_name, const std::vector &dims); @@ -163,13 +167,17 @@ class MS_API Benchmark { int CompareDataGetTotalBiasAndSize(const std::string &name, tensor::MSTensor *tensor, float *total_bias, int *total_size); + int InitDumpConfigFromJson(char *path); + int InitCallbackParameter(); int InitTimeProfilingCallbackParameter(); int InitPerfProfilingCallbackParameter(); - int InitDumpProfilingCallbackParameter(); + int InitDumpTensorDataCallbackParameter(); + + int InitPrintTensorDataCallbackParameter(); int PrintResult(const std::vector &title, const std::map> &result); @@ -287,6 +295,10 @@ class MS_API Benchmark { float op_cost_total_ = 0.0f; std::map> op_times_by_type_; std::map> op_times_by_name_; + + // dump data + nlohmann::json dump_cfg_json_; + std::string dump_file_output_dir_; #ifdef ENABLE_ARM64 int perf_fd = 0; int perf_fd2 = 0;