VTrain/base/Framework/ModelProcess/ModelProcess.cpp

600 lines
18 KiB
C++
Raw Normal View History

2024-11-27 12:47:45 +00:00
/*
* Copyright(C) 2020. Huawei Technologies Co.,Ltd. All rights reserved.
*
* 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.
*/
#ifdef USE_DCMI_INTERFACE
#include <unistd.h>
#include <termios.h>
#endif
#include "ModelProcess.h"
#include "FileManager.h"
ModelProcess::ModelProcess(const int deviceId, const std::string &modelName)
{
deviceId_ = deviceId;
modelName_ = modelName;
}
ModelProcess::ModelProcess() {}
ModelProcess::~ModelProcess()
{
if (!isDeInit_)
{
DeInit();
}
}
void ModelProcess::DestroyDataset(const aclmdlDataset *dataset) const
{
// Just release the DataBuffer object and DataSet object, remain the buffer, because it is managerd by user
if (dataset != nullptr)
{
for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(dataset); i++)
{
aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(dataset, i);
if (dataBuffer != nullptr)
{
aclDestroyDataBuffer(dataBuffer);
dataBuffer = nullptr;
}
}
aclmdlDestroyDataset(dataset);
dataset = nullptr;
}
}
aclmdlDesc *ModelProcess::GetModelDesc() const
{
return modelDesc_.get();
}
//从device侧获得推理数据
int ModelProcess::ModelInference_from_dvpp(void *buffer, uint32_t buffer_size)
{
std::vector<void *> inputBuffers{buffer};
std::vector<size_t> inputSizes{buffer_size};
//创建输入数据集
aclmdlDataset *input = nullptr;
input = CreateAndFillDataset(inputBuffers, inputSizes);
if (input == nullptr)
{
return APP_ERR_COMM_FAILURE;
}
APP_ERROR ret = 0;
//创建输出数据集
aclmdlDataset *output = nullptr;
output = CreateAndFillDataset(outputBuffers_, outputSizes_);
if (output == nullptr)
{
DestroyDataset(input);
input = nullptr;
return APP_ERR_COMM_FAILURE;
}
//对同一个modelId的模型由于与模型关联的资源例如stream、内存等唯一因此不能在多线程中并发使用否则可能导致业务异常。
//同一个id的可以加锁不同id的不用加锁
mtx_.lock();
ret = aclmdlExecute(modelId_, input, output);
mtx_.unlock();
if (ret != APP_ERR_OK)
{
LogError << "aclmdlExecute failed, ret[" << ret << "].";
//加上,防止内存泄露
DestroyDataset(input);
DestroyDataset(output);
return ret;
}
DestroyDataset(input);
DestroyDataset(output);
return APP_ERR_OK;
}
int ModelProcess::ModelInference(const std::vector<void *> &inputBufs, const std::vector<size_t> &inputSizes,
const std::vector<void *> &ouputBufs, const std::vector<size_t> &outputSizes, size_t dynamicBatchSize)
{
LogDebug << "ModelProcess:Begin to inference.";
aclmdlDataset *input = nullptr;
input = CreateAndFillDataset(inputBufs, inputSizes);
if (input == nullptr)
{
return APP_ERR_COMM_FAILURE;
}
APP_ERROR ret = 0;
if (dynamicBatchSize != 0)
{
size_t index;
ret = aclmdlGetInputIndexByName(modelDesc_.get(), ACL_DYNAMIC_TENSOR_NAME, &index);
if (ret != ACL_ERROR_NONE)
{
LogError << "aclmdlGetInputIndexByName failed, maybe static model";
return APP_ERR_COMM_CONNECTION_FAILURE;
}
ret = aclmdlSetDynamicBatchSize(modelId_, input, index, dynamicBatchSize);
if (ret != ACL_ERROR_NONE)
{
LogError << "dynamic batch set failed, modelId_=" << modelId_ << ", input=" << input << ", index=" << index
<< ", dynamicBatchSize=" << dynamicBatchSize;
return APP_ERR_COMM_CONNECTION_FAILURE;
}
LogDebug << "set dynamicBatchSize success, dynamicBatchSize=" << dynamicBatchSize;
}
aclmdlDataset *output = nullptr;
output = CreateAndFillDataset(ouputBufs, outputSizes);
if (output == nullptr)
{
DestroyDataset(input);
input = nullptr;
return APP_ERR_COMM_FAILURE;
}
mtx_.lock();
ret = aclmdlExecute(modelId_, input, output);
mtx_.unlock();
if (ret != APP_ERR_OK)
{
LogError << "aclmdlExecute failed, ret[" << ret << "].";
return ret;
}
DestroyDataset(input);
DestroyDataset(output);
return APP_ERR_OK;
}
int ModelProcess::ModelInferDynamicHW(const std::vector<void *> &inputBufs, const std::vector<size_t> &inputSizes,
const std::vector<void *> &ouputBufs, const std::vector<size_t> &outputSizes)
{
LogDebug << "ModelProcess:Begin to inference with dynamic width and height.";
aclmdlDataset *input = nullptr;
input = CreateAndFillDataset(inputBufs, inputSizes);
if (input == nullptr)
{
return APP_ERR_COMM_FAILURE;
}
size_t index;
APP_ERROR ret = aclmdlGetInputIndexByName(modelDesc_.get(), ACL_DYNAMIC_TENSOR_NAME, &index);
if (ret != ACL_ERROR_NONE)
{
LogError << "Failed to execute aclmdlGetInputIndexByName, maybe static model.";
return APP_ERR_COMM_CONNECTION_FAILURE;
}
ret = aclmdlSetDynamicHWSize(modelId_, input, index, modelHeight_, modelWidth_);
if (ret != ACL_ERROR_NONE)
{
LogError << "Failed to set dynamic HW, modelId_=" << modelId_ << ", input=" << input << ", index="
<< index << ", dynamicW=" << modelWidth_ << ", dynamicH=" << modelHeight_;
return APP_ERR_COMM_CONNECTION_FAILURE;
}
LogDebug << "Set dynamicHWSize success, dynamicHWSize=" << modelWidth_ << ", " << modelHeight_;
aclmdlDataset *output = nullptr;
output = CreateAndFillDataset(ouputBufs, outputSizes);
if (output == nullptr)
{
DestroyDataset(input);
input = nullptr;
return APP_ERR_COMM_FAILURE;
}
mtx_.lock();
ret = aclmdlExecute(modelId_, input, output);
mtx_.unlock();
if (ret != APP_ERR_OK)
{
LogError << "aclmdlExecute failed, ret[" << ret << "].";
return ret;
}
DestroyDataset(input);
DestroyDataset(output);
return APP_ERR_OK;
}
int ModelProcess::DeInit()
{
LogInfo << "Model[" << modelName_ << "][" << deviceId_ << "] deinit begin";
isDeInit_ = true;
//卸载模型
APP_ERROR ret = aclmdlUnload(modelId_);
if (ret != APP_ERR_OK)
{
LogError << "aclmdlUnload failed, ret[" << ret << "].";
return ret;
}
//释放工作内存
if (modelDevPtr_ != nullptr)
{
ret = aclrtFree(modelDevPtr_);
if (ret != APP_ERR_OK)
{
LogError << "aclrtFree failed, ret[" << ret << "].";
return ret;
}
modelDevPtr_ = nullptr;
}
//释放权值内存
if (weightDevPtr_ != nullptr)
{
ret = aclrtFree(weightDevPtr_);
if (ret != APP_ERR_OK)
{
LogError << "aclrtFree failed, ret[" << ret << "].";
return ret;
}
weightDevPtr_ = nullptr;
}
//释放输入内存
for (size_t i = 0; i < inputBuffers_.size(); i++)
{
if (inputBuffers_[i] != nullptr)
{
aclrtFree(inputBuffers_[i]);
inputBuffers_[i] = nullptr;
}
}
inputBuffers_.clear();
inputSizes_.clear();
//释放输出内存
for (size_t i = 0; i < outputBuffers_.size(); i++)
{
if (outputBuffers_[i] != nullptr)
{
aclrtFree(outputBuffers_[i]);
outputBuffers_[i] = nullptr;
}
}
outputBuffers_.clear();
outputSizes_.clear();
LogInfo << "Model[" << modelName_ << "][" << deviceId_ << "] deinit success";
return APP_ERR_OK;
}
APP_ERROR ModelProcess::LoadModel(const std::shared_ptr<uint8_t> &modelData, int modelSize)
{
APP_ERROR ret = aclmdlQuerySizeFromMem(modelData.get(), modelSize, &modelDevPtrSize_, &weightDevPtrSize_);
if (ret != APP_ERR_OK)
{
LogError << "aclmdlQuerySizeFromMem failed, ret[" << ret << "].";
return ret;
}
LogDebug << "modelDevPtrSize_[" << modelDevPtrSize_ << "], weightDevPtrSize_[" << weightDevPtrSize_ << "].";
//申请Device上的内存
ret = aclrtMalloc(&modelDevPtr_, modelDevPtrSize_, ACL_MEM_MALLOC_HUGE_FIRST);
if (ret != APP_ERR_OK)
{
LogError << "aclrtMalloc dev_ptr failed, ret[" << ret << "].";
return ret;
}
//申请Device上的内存
ret = aclrtMalloc(&weightDevPtr_, weightDevPtrSize_, ACL_MEM_MALLOC_HUGE_FIRST);
if (ret != APP_ERR_OK)
{
LogError << "aclrtMalloc weight_ptr failed, ret[" << ret << "] (" << GetAppErrCodeInfo(ret) << ").";
return ret;
}
//从内存加载离线模型数据,由用户自行管理模型运行的内存
//统完成模型加载后返回的模型ID作为后续操作时用于识别模型的标志。
ret = aclmdlLoadFromMemWithMem(modelData.get(), modelSize, &modelId_, modelDevPtr_, modelDevPtrSize_, weightDevPtr_, weightDevPtrSize_);
if (ret != APP_ERR_OK)
{
LogError << "aclmdlLoadFromMemWithMem failed, ret[" << ret << "].";
return ret;
}
//对同一个modelId的模型由于与模型关联的资源例如stream、内存等唯一因此不能在多线程中并发使用否则可能导致业务异常。
//每次加载id都递增
// printf("modelid:%d\n", modelId_);
ret = aclrtGetCurrentContext(&contextModel_);
if (ret != APP_ERR_OK)
{
LogError << "aclrtMalloc weight_ptr failed, ret[" << ret << "].";
return ret;
}
// get input and output size
aclmdlDesc *modelDesc = aclmdlCreateDesc();
if (modelDesc == nullptr)
{
LogError << "aclmdlCreateDesc failed.";
return APP_ERR_ACL_FAILURE;
}
ret = aclmdlGetDesc(modelDesc, modelId_);
if (ret != APP_ERR_OK)
{
LogError << "aclmdlGetDesc ret fail, ret:" << ret << ".";
return ret;
}
modelDesc_.reset(modelDesc, aclmdlDestroyDesc);
//输入内存不用分配用dvpp的输出内存
//分配输出内存
OutputBufferWithSizeMalloc(ACL_MEM_MALLOC_NORMAL_ONLY);
return APP_ERR_OK;
}
APP_ERROR ModelProcess::Init(std::string modelPath)
{
LogInfo << "ModelProcess:Begin to init instance.";
int modelSize = 0;
std::shared_ptr<uint8_t> modelData = nullptr;
// modelPath should point to an encrypted model when isEncrypted is true
APP_ERROR ret = ReadBinaryFile(modelPath, modelData, modelSize);
if (ret != APP_ERR_OK)
{
LogError << "read model file failed, ret[" << ret << "].";
return ret;
}
return LoadModel(modelData, modelSize);
}
#ifdef USE_DCMI_INTERFACE
void ModelProcess::SetConsoleDispMode(int fd, int option)
{
struct termios term;
if (tcgetattr(fd, &term) == -1)
{
LogWarn << "Failed to get the attribution of the terminal, errno=" << errno << ".";
return;
}
const tcflag_t echoFlags = (ECHO | ECHOE | ECHOK | ECHONL);
if (option)
{
term.c_lflag |= echoFlags;
}
else
{
term.c_lflag &= ~echoFlags;
}
int err = tcsetattr(fd, TCSAFLUSH, &term);
if ((err == -1) || (err == EINTR))
{
LogWarn << "Failed to set the attribution of the terminal, errno=" << errno << ".";
return;
}
return;
}
/*
* @description: Get id and password of secret key encrypted information
* @return: APP_ERR_OK success
* @return: Other values failure
* @attention: This function needs to be implemented by users.
*/
APP_ERROR ModelProcess::GetKeyIdPassword(unsigned int &id, unsigned char password[], unsigned int &passwordLen) const
{
LogInfo << "This function should be implemented by users.";
LogInfo << "Please input secret key encryped index:";
while (1)
{
std::cin >> id;
if (std::cin.rdstate() == std::ios::goodbit)
{
// Clear newline character
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
break;
}
// Clear the cin state and buffer to receive the next input
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
LogInfo << "Input error, please input secret key encryped index again:";
}
LogInfo << "Please input secret key encryped password:";
// Disable the terminal display when entering the password
SetConsoleDispMode(STDIN_FILENO, 0);
std::cin.get(reinterpret_cast<char *>(password), MAX_ENCODE_LEN);
// Enable the terminal display when entering the password
SetConsoleDispMode(STDIN_FILENO, 1);
passwordLen = strlen(reinterpret_cast<char *>(password));
return APP_ERR_OK;
}
APP_ERROR ModelProcess::Init(const std::string &modelPath, bool isEncrypted, int cardId, int deviceId)
{
if (!isEncrypted)
{
return Init(modelPath);
}
LogInfo << "ModelProcess:Begin to init instance.";
int modelSize = 0;
std::shared_ptr<uint8_t> modelData = nullptr;
// modelPath should point to an encrypted model when isEncrypted is true
APP_ERROR ret = ReadBinaryFile(modelPath, modelData, modelSize);
if (ret != APP_ERR_OK)
{
LogError << "Failed to read model file, ret[" << ret << "].";
return ret;
}
// Users need to implement this function as required
ret = GetKeyIdPassword(encryptModelData_.id, encryptModelData_.password, encryptModelData_.password_len);
if (ret != APP_ERR_OK)
{
return ret;
}
ret = dcmi_init();
if (ret != APP_ERR_OK)
{
LogError << "Failed to initialize dcmi, ret = " << ret << ".";
return ret;
}
// Read secret key from dcmi
ret = dcmi_get_ai_model_info(cardId, deviceId, &encryptModelData_);
if (ret != APP_ERR_OK)
{
LogError << "Failed to get model info from dcmi, ret[" << ret << "].";
return ret;
}
// Clear password immediately after use
aclrtMemset(encryptModelData_.password, sizeof(encryptModelData_.password), 0, sizeof(encryptModelData_.password));
LogInfo << "Users need to decrypt model before the next operation.";
// User should modify the decryptedModelData and encryptedModelSize according to the actual situation
std::shared_ptr<uint8_t> decryptedModelData = modelData;
int encryptedModelSize = modelSize;
// Load decrypted model
return LoadModel(decryptedModelData, encryptedModelSize);
}
#endif
aclmdlDataset *ModelProcess::CreateAndFillDataset(const std::vector<void *> &bufs, const std::vector<size_t> &sizes)
const
{
APP_ERROR ret = APP_ERR_OK;
aclmdlDataset *dataset = aclmdlCreateDataset();
if (dataset == nullptr)
{
LogError << "ACL_ModelInputCreate failed.";
return nullptr;
}
for (size_t i = 0; i < bufs.size(); ++i)
{
aclDataBuffer *data = aclCreateDataBuffer(bufs[i], sizes[i]);
if (data == nullptr)
{
DestroyDataset(dataset);
LogError << "aclCreateDataBuffer failed.";
return nullptr;
}
ret = aclmdlAddDatasetBuffer(dataset, data);
if (ret != APP_ERR_OK)
{
DestroyDataset(dataset);
LogError << "ACL_ModelInputDataAdd failed, ret[" << ret << "].";
return nullptr;
}
}
return dataset;
}
size_t ModelProcess::GetModelNumInputs() const
{
return aclmdlGetNumInputs(modelDesc_.get());
}
size_t ModelProcess::GetModelNumOutputs() const
{
return aclmdlGetNumOutputs(modelDesc_.get());
}
size_t ModelProcess::GetModelInputSizeByIndex(const size_t &i) const
{
return aclmdlGetInputSizeByIndex(modelDesc_.get(), i);
}
size_t ModelProcess::GetModelOutputSizeByIndex(const size_t &i) const
{
return aclmdlGetOutputSizeByIndex(modelDesc_.get(), i);
}
APP_ERROR ModelProcess::InputBufferWithSizeMalloc(aclrtMemMallocPolicy policy)
{
size_t inputNum = aclmdlGetNumInputs(modelDesc_.get());
LogDebug << modelName_ << "model inputNum is : " << inputNum << ".";
for (size_t i = 0; i < inputNum; ++i)
{
void *buffer = nullptr;
// modify size
size_t size = aclmdlGetInputSizeByIndex(modelDesc_.get(), i);
APP_ERROR ret = aclrtMalloc(&buffer, size, policy);
if (ret != APP_ERR_OK)
{
LogFatal << modelName_ << "model input aclrtMalloc fail(ret=" << ret
<< "), buffer=" << buffer << ", size=" << size << ".";
// Free the buffer malloced successfully before return error
ReleaseModelBuffer(inputBuffers_);
return ret;
}
inputBuffers_.push_back(buffer);
inputSizes_.push_back(size);
LogDebug << modelName_ << "model inputBuffer i=" << i << ", size=" << size << ".";
}
return APP_ERR_OK;
}
APP_ERROR ModelProcess::OutputBufferWithSizeMalloc(aclrtMemMallocPolicy policy)
{
size_t outputNum = aclmdlGetNumOutputs(modelDesc_.get());
LogDebug << modelName_ << "model outputNum is : " << outputNum << ".";
for (size_t i = 0; i < outputNum; ++i)
{
void *buffer = nullptr;
// modify size
size_t size = aclmdlGetOutputSizeByIndex(modelDesc_.get(), i);
APP_ERROR ret = aclrtMalloc(&buffer, size, policy);
if (ret != APP_ERR_OK)
{
LogFatal << modelName_ << "model output aclrtMalloc fail(ret=" << ret
<< "), buffer=" << buffer << ", size=" << size << ".";
// Free the buffer malloced successfully before return error
ReleaseModelBuffer(outputBuffers_);
return ret;
}
outputBuffers_.push_back(buffer);
outputSizes_.push_back(size);
LogDebug << modelName_ << "," << aclmdlGetOutputNameByIndex(modelDesc_.get(), i) << ",model outputBuffer i=" << i << ", size=" << size << ".";
}
return APP_ERR_OK;
}
void ModelProcess::ReleaseModelBuffer(std::vector<void *> &modelBuffers) const
{
for (size_t i = 0; i < modelBuffers.size(); i++)
{
if (modelBuffers[i] != nullptr)
{
aclrtFree(modelBuffers[i]);
modelBuffers[i] = nullptr;
}
}
}
void ModelProcess::SetModelWH(uint32_t width, uint32_t height)
{
modelWidth_ = width;
modelHeight_ = height;
}