blob: edfb5a194782c229f71b3c681419f670c63ce513 [file] [log] [blame]
/*
* Copyright (c) Qualcomm Innovation Center, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <executorch/backends/qualcomm/runtime/QnnManager.h>
#include <executorch/backends/qualcomm/runtime/Utils.h>
#include <executorch/backends/qualcomm/runtime/backends/QnnImplementation.h>
#include <cstdlib>
#include <cstring>
#include <fstream>
namespace torch {
namespace executor {
namespace qnn {
QnnManager::~QnnManager() {
backend_params_ptr_.reset(new BackendConfigParameters());
logger_.reset();
qnn_loaded_backend_.TerminateAllBackends();
}
QnnManager::QnnManager(
const QnnExecuTorchOptions* options,
const QnnExecuTorchContextBinary& qnn_executorch_context_binary)
: backend_type_(options->backend_type()),
library_path_(options->library_path()->c_str()),
skel_library_dir_(options->skel_library_dir()->c_str()),
tensor_dump_output_path_(options->tensor_dump_output_path()->c_str()),
graph_name_(options->graph_name()->c_str()),
soc_info_(options->soc_info()),
htp_options_(options->htp_options()),
log_level_(options->log_level()),
qnn_context_blob_(qnn_executorch_context_binary),
qnn_loaded_backend_(library_path_),
online_prepare_(options->online_prepare()) {
if (log_level_ >= QnnExecuTorchLogLevel::kLogLevelInfo) {
QNN_EXECUTORCH_LOG_INFO(
"backend_type: %s",
EnumNameQnnExecuTorchBackendType(options->backend_type()));
QNN_EXECUTORCH_LOG_INFO("graph_name: %s", options->graph_name()->c_str());
QNN_EXECUTORCH_LOG_INFO(
"library_path: %s", options->library_path()->c_str());
QNN_EXECUTORCH_LOG_INFO(
"skel_library_dir: %s", options->skel_library_dir()->c_str());
QNN_EXECUTORCH_LOG_INFO(
"tensor_dump_output_path: %s",
options->tensor_dump_output_path()->c_str());
QNN_EXECUTORCH_LOG_INFO(
"log_level: %s", EnumNameQnnExecuTorchLogLevel(options->log_level()));
QNN_EXECUTORCH_LOG_INFO(
"soc_model in soc_info: %s",
EnumNameQcomChipset(options->soc_info()->soc_model()));
QNN_EXECUTORCH_LOG_INFO(
"htp_arch in htp_info: %s",
EnumNameHtpArch(options->soc_info()->htp_info()->htp_arch()));
QNN_EXECUTORCH_LOG_INFO(
"vtcm_size_in_mb in htp_info: %d",
options->soc_info()->htp_info()->vtcm_size_in_mb());
QNN_EXECUTORCH_LOG_INFO(
"the size of qnn context binary: %d",
qnn_executorch_context_binary.nbytes);
QNN_EXECUTORCH_LOG_INFO(
"Is on-device graph construction: %d", options->online_prepare());
}
if (!skel_library_dir_.empty()) {
setenv("ADSP_LIBRARY_PATH", skel_library_dir_.c_str(), /*overwrite=*/1);
}
if (library_path_.empty()) {
switch (backend_type_) {
case QnnExecuTorchBackendType::kHtpBackend:
library_path_ = htp_library_name_;
break;
case QnnExecuTorchBackendType::kDspBackend:
library_path_ = dsp_library_name_;
break;
case QnnExecuTorchBackendType::kGpuBackend:
library_path_ = gpu_library_name_;
break;
default:
QNN_EXECUTORCH_LOG_ERROR("Unknown backend type: %s", backend_type_);
break;
}
}
qnn_loaded_backend_ = QnnImplementation(library_path_);
backend_params_ptr_ = std::make_unique<BackendConfigParameters>();
}
Error QnnManager::LoadQnnLibrary() {
Error ret = qnn_loaded_backend_.Load(nullptr);
return ret;
}
Error QnnManager::Init() {
ET_CHECK_OR_RETURN_ERROR(
LoadQnnLibrary() == Error::Ok, Internal, "Fail to load Qnn library");
logger_ = std::make_unique<QnnLogger>(
qnn_loaded_backend_, LoggingCallback, log_level_);
if (backend_params_ptr_->backend_init_state_ ==
BackendInitializeState::UNINITIALIZED) {
QNN_EXECUTORCH_LOG_INFO(
"Initialize Qnn backend "
"parameters for Qnn executorch backend type %d",
backend_type_);
backend_params_ptr_ = QnnBackendFactory().Create(
qnn_loaded_backend_,
logger_.get(),
log_level_,
qnn_context_blob_,
backend_type_,
graph_name_,
soc_info_,
htp_options_);
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_backend_ptr_->Configure() == Error::Ok,
Internal,
"Fail to configure Qnn backend");
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_device_ptr_->Configure() == Error::Ok,
Internal,
"Fail to configure Qnn device");
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_context_ptr_->Configure() == Error::Ok,
Internal,
"Fail to configure Qnn context");
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_graph_ptr_->Configure() == Error::Ok,
Internal,
"Fail to configure Qnn graph");
backend_params_ptr_->backend_init_state_ =
BackendInitializeState::INITIALIZED;
}
return Error::Ok;
}
Error QnnManager::AllocateTensor() {
std::vector<Qnn_Tensor_t> input_tensors =
backend_params_ptr_->qnn_context_ptr_->GetGraphInputs();
std::vector<Qnn_Tensor_t> output_tensors =
backend_params_ptr_->qnn_context_ptr_->GetGraphOutputs();
for (auto& tensor : input_tensors) {
std::shared_ptr<TensorWrapper> tensor_wrapper = CreateTensorWrapper(tensor);
tensor_wrapper->UpdateQnnTensorMeta(tensor);
input_tensors_.emplace_back(std::move(tensor_wrapper));
}
for (auto& tensor : output_tensors) {
std::shared_ptr<TensorWrapper> tensor_wrapper = CreateTensorWrapper(tensor);
tensor_wrapper->UpdateQnnTensorMeta(tensor);
if (!tensor_dump_output_path_.empty()) {
tensor_wrapper->AllocateDataBuffer();
}
output_tensors_.emplace_back(std::move(tensor_wrapper));
}
return Error::Ok;
}
Error QnnManager::AllocateTensor(
std::vector<std::shared_ptr<TensorWrapper>>& inputs,
std::vector<std::shared_ptr<TensorWrapper>>& outputs) {
input_tensors_ = std::move(inputs);
for (auto& output_tensor : outputs) {
if (!tensor_dump_output_path_.empty()) {
output_tensor->AllocateDataBuffer();
}
}
output_tensors_ = std::move(outputs);
return Error::Ok;
}
Error QnnManager::Execute(
const std::vector<Qnn_Tensor_t>& input_tensor_structs,
std::vector<Qnn_Tensor_t>& output_tensor_structs) {
Qnn_ErrorHandle_t error = QNN_SUCCESS;
error = backend_params_ptr_->qnn_graph_ptr_->GraphExecute(
input_tensor_structs, output_tensor_structs);
if (error != QNN_SUCCESS) {
QNN_EXECUTORCH_LOG_ERROR(
"qnn_graph_execute failed. Error %d", QNN_GET_ERROR_CODE(error));
return Error::Internal;
}
if (!tensor_dump_output_path_.empty()) {
// TODO: Need to handle the graph which is partitioned.
// Maybe we could use graph name.
std::string dir = tensor_dump_output_path_ + "/Result/";
CreateDirectory(dir);
QNN_EXECUTORCH_LOG_INFO("Dump tensor to the path: %s", dir.c_str());
for (std::size_t out_idx = 0; out_idx < output_tensor_structs.size();
++out_idx) {
const Qnn_Tensor_t& output_tensor = output_tensor_structs[out_idx];
std::string output_path =
dir + QNN_VER_PTR(output_tensor)->name + "_tensor.raw";
std::ofstream fout(output_path, std::ios::binary);
if (fout.fail()) {
QNN_EXECUTORCH_LOG_ERROR(
"Dump tensor name: %s Failed.", QNN_VER_PTR(output_tensor)->name);
return Error::Internal;
}
fout.write(
static_cast<const char*>(QNN_VER_PTR(output_tensor)->clientBuf.data),
QNN_VER_PTR(output_tensor)->clientBuf.dataSize);
}
}
return Error::Ok;
}
void QnnManager::Destroy() {
QNN_EXECUTORCH_LOG_INFO("Destroy Qnn backend parameters");
backend_params_ptr_.reset(new BackendConfigParameters());
logger_.reset();
qnn_loaded_backend_.TerminateAllBackends();
}
bool QnnManager::IsAvailable() {
return true;
}
bool QnnManager::IsOnlinePrepare() {
return online_prepare_;
}
bool QnnManager::IsNodeSupportedByBackend(
std::vector<std::shared_ptr<OpWrapper>>& op_wrappers) {
Qnn_ErrorHandle_t error = QNN_SUCCESS;
for (std::shared_ptr<OpWrapper>& op_wrapper : op_wrappers) {
for (const auto& param : op_wrapper->GetParams()) {
// unused?
// auto* p_tensor_param = dynamic_cast<TensorParamWrapper*>(param.get());
if (param->PopulateQnnParam() != Error::Ok) {
QNN_EXECUTORCH_LOG_WARN(
"Qnn Backend op validation failed "
"with PopulateQnnParam: %d",
QNN_GET_ERROR_CODE(error));
return false;
}
}
error = backend_params_ptr_->qnn_backend_ptr_->BackendValidateOpConfig(
op_wrapper->GetOpConfig());
if (error != QNN_SUCCESS) {
QNN_EXECUTORCH_LOG_WARN(
"Qnn Backend op validation failed with error: %d",
QNN_GET_ERROR_CODE(error));
return false;
}
}
return true;
}
Error QnnManager::Compile(
std::vector<std::shared_ptr<OpWrapper>>& op_wrappers,
QnnExecuTorchContextBinary& qnn_executorch_context_binary) {
Qnn_ErrorHandle_t error = QNN_SUCCESS;
for (std::shared_ptr<OpWrapper>& op_wrapper : op_wrappers) {
for (const auto& tensor_wrapper : op_wrapper->GetInputTensors()) {
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_graph_ptr_->EnsureTensorInQnnGraph(
tensor_wrapper) == Error::Ok,
Internal,
"Tensor name %s isn't added to Qnn Graph",
tensor_wrapper->GetName().c_str());
}
for (const auto& tensor_wrapper : op_wrapper->GetOutputTensors()) {
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_graph_ptr_->EnsureTensorInQnnGraph(
tensor_wrapper) == Error::Ok,
Internal,
"Tensor name %s isn't added to Qnn Graph",
tensor_wrapper->GetName().c_str());
}
for (const auto& param : op_wrapper->GetParams()) {
auto* p_tensor_param = dynamic_cast<TensorParamWrapper*>(param.get());
if (p_tensor_param != nullptr) {
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_graph_ptr_->EnsureTensorInQnnGraph(
p_tensor_param->GetTensorWrapper()) == Error::Ok,
Internal,
"Param tensor name %s isn't added to Qnn Graph",
p_tensor_param->GetName().c_str());
}
ET_CHECK_OR_RETURN_ERROR(
param->PopulateQnnParam() == Error::Ok,
Internal,
"Fail to configure Qnn backend");
}
error = backend_params_ptr_->qnn_graph_ptr_->GraphAddNode(
op_wrapper->GetOpConfig());
if (error != QNN_SUCCESS) {
QNN_EXECUTORCH_LOG_ERROR(
"Failed to add node to Qnn Graph with error: %d",
QNN_GET_ERROR_CODE(error));
return Error::Internal;
}
}
error = backend_params_ptr_->qnn_graph_ptr_->GraphFinalize();
if (error != QNN_SUCCESS) {
QNN_EXECUTORCH_LOG_ERROR(
"Failed to finalize Qnn Graph with error: %d",
QNN_GET_ERROR_CODE(error));
return Error::Internal;
}
// no need to generate extra context binary in online prepare scenario
if (!IsOnlinePrepare()) {
ET_CHECK_OR_RETURN_ERROR(
backend_params_ptr_->qnn_context_ptr_->GetContextBinary(
qnn_executorch_context_binary) == Error::Ok,
Internal,
"Fail to get context binary.");
}
return Error::Ok;
};
} // namespace qnn
} // namespace executor
} // namespace torch