/** * Copyright 2020 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 "serving/acl/model_process.h" #include #include #include "include/infer_log.h" namespace mindspore { namespace inference { Status ModelProcess::PreInitModelResource() { model_desc_ = aclmdlCreateDesc(); aclError acl_ret = aclmdlGetDesc(model_desc_, model_id_); if (acl_ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Read model desc failed"; return FAILED; } Status ret = InitInputsBuffer(); if (ret != SUCCESS) { MSI_LOG_ERROR << "Create input buffer failed"; return FAILED; } ret = InitOutputsBuffer(); if (ret != SUCCESS) { MSI_LOG_ERROR << "Create output buffer failed"; return FAILED; } return SUCCESS; } Status ModelProcess::LoadModelFromFile(const std::string &file_name, uint32_t &model_id) { aclError acl_ret = aclmdlLoadFromFile(file_name.c_str(), &model_id); if (acl_ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Read model file failed, file name is " << file_name; return FAILED; } MSI_LOG_INFO << "Load model success " << file_name; model_id_ = model_id; if (PreInitModelResource() != SUCCESS) { aclmdlUnload(model_id_); MSI_LOG_ERROR << "Pre init model resource failed, file name is " << file_name; return FAILED; } return SUCCESS; } Status ModelProcess::InitInputsBuffer() { aclError ret; size_t input_size = aclmdlGetNumInputs(model_desc_); for (size_t i = 0; i < input_size; ++i) { auto buffer_size = aclmdlGetInputSizeByIndex(model_desc_, i); void *data_mem_buffer = nullptr; if (!is_run_on_device_) { // need to copy input/output to/from device ret = aclrtMalloc(&data_mem_buffer, buffer_size, ACL_MEM_MALLOC_NORMAL_ONLY); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Malloc device input buffer faild , input size " << buffer_size; return FAILED; } } aclmdlIODims dims; ret = aclmdlGetInputDims(model_desc_, i, &dims); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Get input shape failed"; if (!is_run_on_device_) { aclrtFree(data_mem_buffer); } return FAILED; } aclDataType data_type = aclmdlGetInputDataType(model_desc_, i); std::vector shape(dims.dims, dims.dims + dims.dimCount); input_infos_.emplace_back(AclTensorInfo{data_mem_buffer, buffer_size, data_type, shape}); } MSI_LOG_INFO << "Create model inputs success"; return SUCCESS; } Status ModelProcess::CreateDataBuffer(void *&data_mem_buffer, size_t buffer_size, aclmdlDataset *dataset) { aclError ret; auto free_data_buffer = [this](void *dataMemBuffer) { if (!is_run_on_device_) { aclrtFree(dataMemBuffer); } else { aclrtFreeHost(dataMemBuffer); } }; if (!is_run_on_device_) { ret = aclrtMalloc(&data_mem_buffer, buffer_size, ACL_MEM_MALLOC_NORMAL_ONLY); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Malloc device buffer faild , buffer size " << buffer_size; return FAILED; } } else { ret = aclrtMallocHost(&data_mem_buffer, buffer_size); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Malloc device buffer faild , buffer size " << buffer_size; return FAILED; } } auto data_buffer = aclCreateDataBuffer(data_mem_buffer, buffer_size); if (data_buffer == nullptr) { MSI_LOG_ERROR << "Create Data Buffer failed"; free_data_buffer(data_mem_buffer); return FAILED; } ret = aclmdlAddDatasetBuffer(dataset, data_buffer); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "add data buffer failed"; free_data_buffer(data_mem_buffer); aclDestroyDataBuffer(data_buffer); return FAILED; } return SUCCESS; } Status ModelProcess::InitOutputsBuffer() { aclError ret; outputs_ = aclmdlCreateDataset(); if (outputs_ == nullptr) { MSI_LOG_ERROR << "Create input dataset failed"; return FAILED; } size_t output_size = aclmdlGetNumOutputs(model_desc_); for (size_t i = 0; i < output_size; ++i) { auto buffer_size = aclmdlGetOutputSizeByIndex(model_desc_, i); void *data_mem_buffer = nullptr; if (CreateDataBuffer(data_mem_buffer, buffer_size, outputs_) != SUCCESS) { MSI_LOG_ERROR << "add output data buffer failed, buffer size " << buffer_size; return FAILED; } aclmdlIODims dims; ret = aclmdlGetOutputDims(model_desc_, i, &dims); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Get input shape failed"; if (!is_run_on_device_) { aclrtFree(data_mem_buffer); } else { aclrtFreeHost(data_mem_buffer); } return FAILED; } aclDataType data_type = aclmdlGetOutputDataType(model_desc_, i); std::vector shape(dims.dims, dims.dims + dims.dimCount); output_infos_.emplace_back(AclTensorInfo{data_mem_buffer, buffer_size, data_type, shape}); } MSI_LOG_INFO << "Create model output success"; return SUCCESS; } void ModelProcess::DestroyInputsDataset() { if (inputs_ == nullptr) { return; } for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(inputs_); i++) { auto dataBuffer = aclmdlGetDatasetBuffer(inputs_, i); aclDestroyDataBuffer(dataBuffer); } aclmdlDestroyDataset(inputs_); inputs_ = nullptr; } void ModelProcess::DestroyInputsDataMem() { if (!is_run_on_device_) { for (const auto &item : input_infos_) { aclrtFree(item.device_data); } } input_infos_.clear(); } void ModelProcess::DestroyInputsBuffer() { DestroyInputsDataMem(); DestroyInputsDataset(); } void ModelProcess::DestroyOutputsBuffer() { for (const auto &item : output_infos_) { if (!is_run_on_device_) { aclrtFree(item.device_data); } else { aclrtFreeHost(item.device_data); } } output_infos_.clear(); if (outputs_ == nullptr) { return; } for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(outputs_); i++) { auto dataBuffer = aclmdlGetDatasetBuffer(outputs_, i); aclDestroyDataBuffer(dataBuffer); } aclmdlDestroyDataset(outputs_); outputs_ = nullptr; } void ModelProcess::UnLoad() { auto ret = aclmdlUnload(model_id_); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Unload model failed"; } if (model_desc_ != nullptr) { aclmdlDestroyDesc(model_desc_); model_desc_ = nullptr; } DestroyInputsBuffer(); DestroyOutputsBuffer(); MSI_LOG_INFO << "End unload model " << model_id_; } Status ModelProcess::CheckAndInitInput(const RequestBase &request) { aclError ret; inputs_ = aclmdlCreateDataset(); // check inputs if (request.size() != input_infos_.size()) { MSI_LOG_ERROR << "inputs count not match, required count " << input_infos_.size() << ", given count " << request.size(); return INFER_STATUS(INVALID_INPUTS) << "inputs count not match, required count " << input_infos_.size() << ", given count " << request.size(); } for (size_t i = 0; i < input_infos_.size(); i++) { if (request[i] == nullptr) { MSI_LOG_ERROR << "input " << i << " cannot be null"; return FAILED; } if (request[i]->data_size() != input_infos_[i].buffer_size) { MSI_LOG_ERROR << "input " << i << " data size not match, required size " << input_infos_[i].buffer_size << ", given count " << request[i]->data_size(); return INFER_STATUS(INVALID_INPUTS) << "input " << i << " data size not match, required size " << input_infos_[i].buffer_size << ", given count " << request[i]->data_size(); } } // copy inputs for (size_t i = 0; i < input_infos_.size(); i++) { void *input_buffer = nullptr; auto &info = input_infos_[i]; const void *data = request[i]->data(); if (!is_run_on_device_) { ret = aclrtMemcpy(info.device_data, info.buffer_size, data, request[i]->data_size(), ACL_MEMCPY_HOST_TO_DEVICE); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "memcpy input " << i << " data to device failed, buffer size " << request[i]->data_size(); return FAILED; } input_buffer = info.device_data; } else { input_buffer = const_cast(data); } auto data_buffer = aclCreateDataBuffer(input_buffer, info.buffer_size); if (data_buffer == nullptr) { MSI_LOG_ERROR << "Create Data Buffer failed"; return FAILED; } ret = aclmdlAddDatasetBuffer(inputs_, data_buffer); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "add data buffer failed"; aclDestroyDataBuffer(data_buffer); return FAILED; } } return SUCCESS; } Status ModelProcess::CheckAndInitDvppInput(const void *dvpp_outputs_buffer_dev, size_t dvpp_outputs_buffer_size, size_t input_index) { aclError ret; inputs_ = aclmdlCreateDataset(); // check inputs if (input_index >= input_infos_.size()) { MSI_LOG_ERROR << "inputs count not match, required count " << input_infos_.size() << ", given index " << input_index; return INFER_STATUS(INVALID_INPUTS) << "inputs count not match, required count " << input_infos_.size() << ", given index " << input_index; } if (dvpp_outputs_buffer_dev == nullptr) { MSI_LOG_ERROR << "input " << 0 << " cannot be null"; return FAILED; } if (dvpp_outputs_buffer_size != input_infos_[input_index].buffer_size) { MSI_LOG_ERROR << "input " << 0 << " data size not match, required size " << input_infos_[input_index].buffer_size << ", given count " << dvpp_outputs_buffer_size; return INFER_STATUS(INVALID_INPUTS) << "input " << 0 << " data size not match, required size " << input_infos_[input_index].buffer_size << ", given count " << dvpp_outputs_buffer_size; } // copy inputs auto &info = input_infos_[input_index]; auto data_buffer = aclCreateDataBuffer(const_cast(dvpp_outputs_buffer_dev), info.buffer_size); if (data_buffer == nullptr) { MSI_LOG_ERROR << "Create Data Buffer failed"; return FAILED; } ret = aclmdlAddDatasetBuffer(inputs_, data_buffer); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "add data buffer failed"; aclDestroyDataBuffer(data_buffer); return FAILED; } return SUCCESS; } Status ModelProcess::BuildOutputs(ReplyBase &reply) { aclError ret; // copy outputs reply.clear(); std::unordered_map data_type_map = { {ACL_FLOAT16, inference::kMSI_Float16}, {ACL_FLOAT, inference::kMSI_Float32}, {ACL_DOUBLE, inference::kMSI_Float64}, {ACL_INT8, inference::kMSI_Int8}, {ACL_INT16, inference::kMSI_Int16}, {ACL_INT32, inference::kMSI_Int32}, {ACL_INT64, inference::kMSI_Int64}, {ACL_UINT8, inference::kMSI_Uint8}, {ACL_UINT16, inference::kMSI_Uint16}, {ACL_UINT32, inference::kMSI_Uint32}, {ACL_UINT64, inference::kMSI_Uint64}, {ACL_BOOL, inference::kMSI_Bool}, }; auto trans_to_serving_type = [&data_type_map](aclDataType data_type) { auto it = data_type_map.find(data_type); if (it == data_type_map.end()) { return inference::kMSI_Unknown; } else { return it->second; } }; for (size_t i = 0; i < output_infos_.size(); i++) { auto &info = output_infos_[i]; auto output = reply.add(); if (output == nullptr) { MSI_LOG_ERROR << "add new output failed"; return FAILED; } output->set_data_type(trans_to_serving_type(info.data_type)); output->set_shape(info.dims); if (!output->resize_data(info.buffer_size)) { MSI_LOG_ERROR << "new output data buffer failed, data size " << info.buffer_size; return FAILED; } if (!is_run_on_device_) { ret = aclrtMemcpy(output->mutable_data(), output->data_size(), info.device_data, info.buffer_size, ACL_MEMCPY_DEVICE_TO_HOST); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Memcpy output " << i << " to host failed, memory size " << info.buffer_size; return FAILED; } } else { ret = aclrtMemcpy(output->mutable_data(), output->data_size(), info.device_data, info.buffer_size, ACL_MEMCPY_HOST_TO_HOST); if (ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Memcpy output " << i << " to host failed, memory size " << info.buffer_size; return FAILED; } } } return SUCCESS; } Status ModelProcess::Execute(const RequestBase &request, ReplyBase &reply) { aclError acl_ret; Status ret = CheckAndInitInput(request); if (ret != SUCCESS) { MSI_LOG_ERROR << "check or init input failed"; DestroyInputsDataset(); return ret; // forward status error } acl_ret = aclmdlExecute(model_id_, inputs_, outputs_); DestroyInputsDataset(); if (acl_ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Execute Model Failed"; return FAILED; } ret = BuildOutputs(reply); if (ret != SUCCESS) { MSI_LOG_ERROR << "Build outputs faield"; return FAILED; } MSI_LOG_INFO << "excute model success"; return SUCCESS; } Status ModelProcess::Execute(const void *dvpp_outputs_buffer_dev, size_t dvpp_outputs_buffer_size, ReplyBase &reply) { aclError acl_ret; if (input_infos_.size() != 1) { MSI_LOG_ERROR << "can only support input size 1, now model inputs size is " << input_infos_.size(); return INFER_STATUS(INVALID_INPUTS) << "can only support input size 1, now model inputs size is " << input_infos_.size(); } Status ret = CheckAndInitDvppInput(dvpp_outputs_buffer_dev, dvpp_outputs_buffer_size, 0); if (ret != SUCCESS) { MSI_LOG_ERROR << "check or init input failed"; DestroyInputsDataset(); return ret; // forward status msg } acl_ret = aclmdlExecute(model_id_, inputs_, outputs_); DestroyInputsDataset(); if (acl_ret != ACL_ERROR_NONE) { MSI_LOG_ERROR << "Execute Model Failed"; return INFER_STATUS(FAILED) << "Execute Model Failed"; } ret = BuildOutputs(reply); if (ret != SUCCESS) { MSI_LOG_ERROR << "Build outputs faield"; return FAILED; } MSI_LOG_INFO << "excute model success"; return SUCCESS; } size_t ModelProcess::GetBatchSize() const { if (input_infos_.empty()) { MSI_LOG_ERROR << "Model is not loaded"; return 0; } if (input_infos_[0].dims.empty()) { return 1; } return static_cast(input_infos_[0].dims[0]); } } // namespace inference } // namespace mindspore