[QNN EP] Expose device-level session options (#19212)

### Description
- Adds the following session options to configure the device:
- `soc_model`: The SoC model number. Refer to the QNN SDK documentation
for valid values. Defaults to "0" (unknown).
- `htp_arch`: The minimum HTP architecture the driver will use to select
compatible QNN operators.
- `device_id`: The ID of the device to use when setting 'htp_arch'.
Defaults to "0" (for single device).

### Motivation and Context
Allow more configuration.
This commit is contained in:
Adrian Lizarraga 2024-01-22 12:47:42 -08:00 committed by GitHub
parent 373ebac167
commit 8d9d751179
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 292 additions and 118 deletions

View file

@ -3608,6 +3608,14 @@ struct OrtApi {
* - "1": Faster preparation time, less optimal graph.
* - "2": Longer preparation time, more optimal graph.
* - "3": Longest preparation time, most likely even more optimal graph. See QNN SDK documentation for specific details.
* "soc_model": The SoC model number. Refer to the QNN SDK documentation for valid values. Defaults to "0" (unknown).
* "htp_arch": The minimum HTP architecture the driver will use to select compatible QNN operators. Available options:
* - "0": Default (none).
* - "68"
* - "69"
* - "73"
* - "75"
* "device_id": The ID of the device to use when setting 'htp_arch'. Defaults to "0" (for single device).
*
* SNPE supported keys:
* "runtime": SNPE runtime engine, options: "CPU", "CPU_FLOAT32", "GPU", "GPU_FLOAT32_16_HYBRID", "GPU_FLOAT16",

View file

@ -17,6 +17,7 @@
#include "core/framework/endian_utils.h"
#include "core/common/logging/capture.h"
#include "core/providers/qnn/builder/onnx_ctx_model_helper.h"
#include "core/providers/qnn/builder/qnn_configs_helper.h"
#ifdef _WIN32
#include <winmeta.h>
@ -329,9 +330,37 @@ Status QnnBackendManager::CreateDevice() {
return Status::OK();
}
qnn::QnnConfigsBuilder<QnnDevice_Config_t, QnnHtpDevice_CustomConfig_t> device_configs_builder(QNN_DEVICE_CONFIG_INIT,
{});
if (qnn_backend_type_ == QnnBackendType::HTP) {
// Set SoC Model. The *enum* Qnn_SocModel_t is deprecated and will not be updated in the future. Therefore,
// must use the latest SDK documentation to get the SoC model of the latest HW.
if (soc_model_ != QNN_SOC_MODEL_UNKNOWN) {
QnnHtpDevice_CustomConfig_t& custom_config = device_configs_builder.PushCustomConfig();
custom_config.option = QNN_HTP_DEVICE_CONFIG_OPTION_SOC;
custom_config.socModel = soc_model_;
QnnDevice_Config_t& device_config = device_configs_builder.PushConfig();
device_config.option = QNN_DEVICE_CONFIG_OPTION_CUSTOM;
device_config.customConfig = &custom_config;
}
// Set the minimum HTP architecture. The driver will use ops that are compatible with this minimum architecture.
if (htp_arch_ != QNN_HTP_DEVICE_ARCH_NONE) {
QnnHtpDevice_CustomConfig_t& custom_config = device_configs_builder.PushCustomConfig();
custom_config.option = QNN_HTP_DEVICE_CONFIG_OPTION_ARCH;
custom_config.arch.arch = htp_arch_;
custom_config.arch.deviceId = device_id_;
QnnDevice_Config_t& device_config = device_configs_builder.PushConfig();
device_config.option = QNN_DEVICE_CONFIG_OPTION_CUSTOM;
device_config.customConfig = &custom_config;
}
}
LOGS_DEFAULT(INFO) << "Create device.";
if (nullptr != qnn_interface_.deviceCreate) {
auto result = qnn_interface_.deviceCreate(log_handle_, nullptr, &device_handle_);
auto result = qnn_interface_.deviceCreate(log_handle_, device_configs_builder.GetQnnConfigs(), &device_handle_);
if (QNN_SUCCESS != result) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Failed to create device. Error: ", result);
}

View file

@ -17,6 +17,7 @@
#include <vector>
#include "HTP/QnnHtpDevice.h"
#include "QnnLog.h"
#include "QnnTypes.h"
#include "System/QnnSystemInterface.h"
#include "core/common/status.h"
#include "core/common/logging/logging.h"
@ -35,13 +36,19 @@ class QnnBackendManager {
uint32_t rpc_control_latency,
HtpPerformanceMode htp_performance_mode,
ContextPriority context_priority,
std::string&& qnn_saver_path)
std::string&& qnn_saver_path,
uint32_t device_id,
QnnHtpDevice_Arch_t htp_arch,
uint32_t soc_model)
: backend_path_(backend_path),
profiling_level_(profiling_level),
rpc_control_latency_(rpc_control_latency),
htp_performance_mode_(htp_performance_mode),
context_priority_(context_priority),
qnn_saver_path_(qnn_saver_path) {
qnn_saver_path_(qnn_saver_path),
device_id_(device_id),
htp_arch_(htp_arch),
soc_model_(soc_model) {
}
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(QnnBackendManager);
@ -233,6 +240,9 @@ class QnnBackendManager {
#endif
const std::string qnn_saver_path_;
uint32_t htp_power_config_client_id_ = 0;
uint32_t device_id_ = 0;
QnnHtpDevice_Arch_t htp_arch_ = QNN_HTP_DEVICE_ARCH_NONE;
uint32_t soc_model_ = QNN_SOC_MODEL_UNKNOWN;
};
} // namespace qnn

View file

@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include <core/common/inlined_containers_fwd.h>
namespace onnxruntime {
namespace qnn {
/**
* Helper class for building a null-terminated list of QNN configurations.
* A QNN configuration consists of multiple objects with references to each other. This
* class ensures that all configuration objects have the same lifetime, so that they remain valid
* across calls to qnn_interface.xxxCreate().
*/
template <typename BaseConfigType, typename CustomConfigType>
class QnnConfigsBuilder {
public:
/**
* Initializes the config build. Provide the initial/default value for each config struct type.
* \param base_config_init The initial/default value for objects of type BaseConfigType.
* \param custom_config_init The initial/default value for objects of type CustomConfigType.
*/
QnnConfigsBuilder(BaseConfigType base_config_init, CustomConfigType custom_config_init)
: base_config_init_(std::move(base_config_init)), custom_config_init_(std::move(custom_config_init)) {}
/**
* Returns a pointer to the beginning of a null-terminated array of QNN base configurations.
* This result is typically passed to QNN's xxxCreate() APIs.
*
* \return Pointer to null-terminated BaseConfigType* array.
*/
const BaseConfigType** GetQnnConfigs() {
if (config_ptrs_.empty()) {
return nullptr;
}
if (!IsNullTerminated()) {
config_ptrs_.push_back(nullptr);
}
return config_ptrs_.data();
}
/**
* Creates and returns a reference to a new custom QNN configuration object. The object is initialized to
* the QNN recommended default value. The caller is meant to override fields in this object.
*
* \return A reference to a default CustomConfigType object.
*/
CustomConfigType& PushCustomConfig() {
custom_configs_.push_back(custom_config_init_);
return custom_configs_.back();
}
/**
* Creates and returns a reference to a new QNN configuration object. The object is initialized to
* the QNN recommended default value. The caller is meant to override fields in this object.
*
* \return A reference to a default BaseConfigType object.
*/
BaseConfigType& PushConfig() {
configs_.push_back(base_config_init_);
BaseConfigType& config = configs_.back();
// Add pointer to this new config to the list of config pointers.
if (IsNullTerminated()) {
config_ptrs_.back() = &config; // Replace last nullptr entry.
} else {
config_ptrs_.push_back(&config);
}
return config;
}
private:
bool IsNullTerminated() const {
return !config_ptrs_.empty() && config_ptrs_.back() == nullptr;
}
BaseConfigType base_config_init_;
CustomConfigType custom_config_init_;
InlinedVector<CustomConfigType> custom_configs_;
InlinedVector<BaseConfigType> configs_;
InlinedVector<const BaseConfigType*> config_ptrs_;
};
} // namespace qnn
} // namespace onnxruntime

View file

@ -1,43 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "core/providers/qnn/builder/qnn_graph_configs_helper.h"
#include "HTP/QnnHtpGraph.h"
namespace onnxruntime {
namespace qnn {
const QnnGraph_Config_t** QnnGraphConfigsBuilder::GetQnnGraphConfigs() {
if (graph_config_ptrs_.empty()) {
return nullptr;
}
if (!IsNullTerminated()) {
graph_config_ptrs_.push_back(nullptr);
}
return graph_config_ptrs_.data();
}
QnnHtpGraph_CustomConfig_t& QnnGraphConfigsBuilder::PushHtpGraphCustomConfig() {
htp_custom_graph_configs_.push_back(QNN_HTP_GRAPH_CUSTOM_CONFIG_INIT);
return htp_custom_graph_configs_.back();
}
QnnGraph_Config_t& QnnGraphConfigsBuilder::PushGraphConfig() {
graph_configs_.push_back(QNN_GRAPH_CONFIG_INIT);
QnnGraph_Config_t& config = graph_configs_.back();
// Add pointer to this new graph config to the list of graph config pointers.
if (IsNullTerminated()) {
graph_config_ptrs_.back() = &config; // Replace last nullptr entry.
} else {
graph_config_ptrs_.push_back(&config);
}
return config;
}
} // namespace qnn
} // namespace onnxruntime

View file

@ -1,56 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include <core/common/inlined_containers_fwd.h>
#include "HTP/QnnHtpGraph.h"
namespace onnxruntime {
namespace qnn {
/**
* Helper class for building a null-terminated list of QNN Graph configurations.
* A QNN configuration consists of multiple objects with references to each other. This
* class ensures that all configuration objects have the same lifetime, so that they remain valid
* across the call to graphCreate().
*/
class QnnGraphConfigsBuilder {
public:
/**
* Returns a pointer to the beginning of a null-terminated array of QNN Graph configurations.
* This result is passed QNN's graphCreate() API.
*
* \return Pointer to null-terminated QnnGraph_Config_t* array.
*/
const QnnGraph_Config_t** GetQnnGraphConfigs();
/**
* Creates and returns a reference to a new HTP graph configuration object. The object is initialized to
* the QNN recommended default value. The caller is meant to override fields in this object.
*
* \return A reference to a default QnnHtpGraph_CustomConfig_t object.
*/
QnnHtpGraph_CustomConfig_t& PushHtpGraphCustomConfig();
/**
* Creates and returns a reference to a new graph configuration object. The object is initialized to
* the QNN recommended default value. The caller is meant to override fields in this object.
*
* \return A reference to a default QnnGraph_Config_t object.
*/
QnnGraph_Config_t& PushGraphConfig();
private:
bool IsNullTerminated() const {
return !graph_config_ptrs_.empty() && graph_config_ptrs_.back() == nullptr;
}
InlinedVector<QnnHtpGraph_CustomConfig_t> htp_custom_graph_configs_;
InlinedVector<QnnGraph_Config_t> graph_configs_;
InlinedVector<const QnnGraph_Config_t*> graph_config_ptrs_;
};
} // namespace qnn
} // namespace onnxruntime

View file

@ -111,6 +111,22 @@ void QNNExecutionProvider::ParseHtpGraphFinalizationOptimizationMode(const std::
}
}
static void ParseHtpArchitecture(const std::string& htp_arch_string, QnnHtpDevice_Arch_t& qnn_htp_arch) {
if (htp_arch_string.empty() || htp_arch_string == "0") {
qnn_htp_arch = QNN_HTP_DEVICE_ARCH_NONE;
} else if (htp_arch_string == "68") {
qnn_htp_arch = QNN_HTP_DEVICE_ARCH_V68;
} else if (htp_arch_string == "69") {
qnn_htp_arch = QNN_HTP_DEVICE_ARCH_V69;
} else if (htp_arch_string == "73") {
qnn_htp_arch = QNN_HTP_DEVICE_ARCH_V73;
} else if (htp_arch_string == "75") {
qnn_htp_arch = QNN_HTP_DEVICE_ARCH_V75;
} else {
LOGS_DEFAULT(WARNING) << "Invalid HTP architecture: " << htp_arch_string;
}
}
QNNExecutionProvider::QNNExecutionProvider(const ProviderOptions& provider_options_map,
const SessionOptions* session_options)
: IExecutionProvider{onnxruntime::kQnnExecutionProvider, true} {
@ -223,13 +239,49 @@ QNNExecutionProvider::QNNExecutionProvider(const ProviderOptions& provider_optio
}
}
static const std::string QNN_DEVICE_ID = "device_id";
uint32_t device_id = 0;
auto dev_id_pos = provider_options_map.find(QNN_DEVICE_ID);
if (dev_id_pos != provider_options_map.end()) {
int value = std::stoi(dev_id_pos->second);
if (value < 0) {
LOGS_DEFAULT(WARNING) << "Invalid device ID '" << value
<< "', only >= 0 allowed. Set to " << device_id << ".";
} else {
device_id = static_cast<uint32_t>(value);
}
}
static const std::string QNN_HTP_ARCH = "htp_arch";
QnnHtpDevice_Arch_t htp_arch = QNN_HTP_DEVICE_ARCH_NONE;
auto htp_arch_pos = provider_options_map.find(QNN_HTP_ARCH);
if (htp_arch_pos != provider_options_map.end()) {
ParseHtpArchitecture(htp_arch_pos->second, htp_arch);
}
static const std::string QNN_SOC_MODEL = "soc_model";
uint32_t soc_model = QNN_SOC_MODEL_UNKNOWN;
auto soc_model_pos = provider_options_map.find(QNN_SOC_MODEL);
if (soc_model_pos != provider_options_map.end()) {
int value = std::stoi(soc_model_pos->second);
if (value < 0) {
LOGS_DEFAULT(WARNING) << "Invalid SoC Model '" << value
<< "', only >= 0 allowed. Set to " << soc_model << ".";
} else {
soc_model = static_cast<uint32_t>(value);
}
}
qnn_backend_manager_ = std::make_unique<qnn::QnnBackendManager>(
std::move(backend_path),
profiling_level,
rpc_control_latency,
htp_performance_mode,
context_priority,
std::move(qnn_saver_path));
std::move(qnn_saver_path),
device_id,
htp_arch,
soc_model);
}
bool QNNExecutionProvider::IsNodeSupported(qnn::QnnModelWrapper& qnn_model_wrapper, const NodeUnit& node_unit,
@ -512,25 +564,25 @@ Status QNNExecutionProvider::CreateComputeFunc(std::vector<NodeComputeInfo>& nod
return Status::OK();
}
void QNNExecutionProvider::InitQnnGraphConfigs(qnn::QnnGraphConfigsBuilder& configs_builder) const {
void QNNExecutionProvider::InitQnnGraphConfigs(qnn::QnnConfigsBuilder<QnnGraph_Config_t, QnnHtpGraph_CustomConfig_t>& configs_builder) const {
if (qnn_backend_manager_->GetQnnBackendType() == qnn::QnnBackendType::HTP) {
if (htp_graph_finalization_opt_mode_ != qnn::HtpGraphFinalizationOptimizationMode::kDefault) {
QnnHtpGraph_CustomConfig_t& htp_graph_opt_config = configs_builder.PushHtpGraphCustomConfig();
QnnHtpGraph_CustomConfig_t& htp_graph_opt_config = configs_builder.PushCustomConfig();
htp_graph_opt_config.option = QNN_HTP_GRAPH_CONFIG_OPTION_OPTIMIZATION;
htp_graph_opt_config.optimizationOption.type = QNN_HTP_GRAPH_OPTIMIZATION_TYPE_FINALIZE_OPTIMIZATION_FLAG;
htp_graph_opt_config.optimizationOption.floatValue = static_cast<float>(htp_graph_finalization_opt_mode_);
QnnGraph_Config_t& graph_opt_config = configs_builder.PushGraphConfig();
QnnGraph_Config_t& graph_opt_config = configs_builder.PushConfig();
graph_opt_config.option = QNN_GRAPH_CONFIG_OPTION_CUSTOM;
graph_opt_config.customConfig = &htp_graph_opt_config;
}
if (vtcm_size_in_mb_ > 0) {
QnnHtpGraph_CustomConfig_t& htp_graph_opt_config_vtcm = configs_builder.PushHtpGraphCustomConfig();
QnnHtpGraph_CustomConfig_t& htp_graph_opt_config_vtcm = configs_builder.PushCustomConfig();
htp_graph_opt_config_vtcm.option = QNN_HTP_GRAPH_CONFIG_OPTION_VTCM_SIZE;
htp_graph_opt_config_vtcm.vtcmSizeInMB = static_cast<uint32_t>(vtcm_size_in_mb_);
QnnGraph_Config_t& graph_opt_config_vtcm = configs_builder.PushGraphConfig();
QnnGraph_Config_t& graph_opt_config_vtcm = configs_builder.PushConfig();
graph_opt_config_vtcm.option = QNN_GRAPH_CONFIG_OPTION_CUSTOM;
graph_opt_config_vtcm.customConfig = &htp_graph_opt_config_vtcm;
}
@ -547,10 +599,11 @@ Status QNNExecutionProvider::CompileFromOrtGraph(const std::vector<FusedNodeAndG
std::unique_ptr<qnn::QnnModel> qnn_model = std::make_unique<qnn::QnnModel>(logger,
qnn_backend_manager_.get());
qnn::QnnGraphConfigsBuilder graph_configs_builder;
qnn::QnnConfigsBuilder<QnnGraph_Config_t, QnnHtpGraph_CustomConfig_t> graph_configs_builder(QNN_GRAPH_CONFIG_INIT,
QNN_HTP_GRAPH_CUSTOM_CONFIG_INIT);
InitQnnGraphConfigs(graph_configs_builder);
ORT_RETURN_IF_ERROR(qnn_model->ComposeGraph(graph_viewer, fused_node, graph_configs_builder.GetQnnGraphConfigs()));
ORT_RETURN_IF_ERROR(qnn_model->ComposeGraph(graph_viewer, fused_node, graph_configs_builder.GetQnnConfigs()));
ORT_RETURN_IF_ERROR(qnn_model->FinalizeGraphs());
ORT_RETURN_IF_ERROR(qnn_model->SetupQnnInputOutput());

View file

@ -5,11 +5,12 @@
#include "core/framework/execution_provider.h"
#include "core/framework/session_options.h"
#include "core/graph/model.h"
#include <string>
#include "core/providers/qnn/builder/qnn_backend_manager.h"
#include "core/providers/qnn/builder/qnn_model.h"
#include "core/providers/qnn/builder/qnn_graph_configs_helper.h"
#include "core/graph/model.h"
#include "core/providers/qnn/builder/qnn_configs_helper.h"
#include "HTP/QnnHtpGraph.h"
namespace onnxruntime {
@ -58,7 +59,7 @@ class QNNExecutionProvider : public IExecutionProvider {
void ParseHtpGraphFinalizationOptimizationMode(const std::string& htp_graph_finalization_opt_mode_string);
void InitQnnGraphConfigs(qnn::QnnGraphConfigsBuilder& configs_holder) const;
void InitQnnGraphConfigs(qnn::QnnConfigsBuilder<QnnGraph_Config_t, QnnHtpGraph_CustomConfig_t>& configs_builder) const;
private:
qnn::HtpGraphFinalizationOptimizationMode htp_graph_finalization_opt_mode_ = qnn::HtpGraphFinalizationOptimizationMode::kDefault;

View file

@ -60,6 +60,10 @@ void usage() {
"\t [QNN only] [qnn_saver_path]: QNN Saver backend path. e.g '/folderpath/libQnnSaver.so'.\n"
"\t [QNN only] [htp_graph_finalization_optimization_mode]: QNN graph finalization optimization mode, options: \n"
"\t '0', '1', '2', '3', default is '0'.\n"
"\t [QNN only] [soc_model]: The SoC Model number. Refer to QNN SDK documentation for specific values. Defaults to '0' (unknown). \n"
"\t [QNN only] [htp_arch]: The minimum HTP architecture. The driver will use ops compatible with this architecture. \n"
"\t Options are '0', '68', '69', '73', '75'. Defaults to '0' (none). \n"
"\t [QNN only] [device_id]: The ID of the device to use when setting 'htp_arch'. Defaults to '0' (for single device). \n"
"\t [Usage]: -e <provider_name> -i '<key1>|<value1> <key2>|<value2>' \n\n"
"\t [Example] [For QNN EP] -e qnn -i \"profiling_level|detailed backend_path|/folderpath/libQnnCpu.so\" \n\n"
"\t [SNPE only] [runtime]: SNPE runtime, options: 'CPU', 'GPU', 'GPU_FLOAT16', 'DSP', 'AIP_FIXED_TF'. \n"
@ -483,7 +487,7 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
if (supported_profiling_level.find(value) == supported_profiling_level.end()) {
ORT_THROW("Supported profiling_level: off, basic, detailed");
}
} else if (key == "rpc_control_latency" || key == "vtcm_mb") {
} else if (key == "rpc_control_latency" || key == "vtcm_mb" || key == "soc_model" || key == "device_id") {
// no validation
} else if (key == "htp_performance_mode") {
std::set<std::string> supported_htp_perf_mode = {"burst", "balanced", "default", "high_performance",
@ -512,10 +516,20 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
std::string str = str_stream.str();
ORT_THROW("Wrong value for htp_graph_finalization_optimization_mode. select from: " + str);
}
} else if (key == "htp_arch") {
std::unordered_set<std::string> supported_htp_archs = {"0", "68", "69", "73", "75"};
if (supported_htp_archs.find(value) == supported_htp_archs.end()) {
std::ostringstream str_stream;
std::copy(supported_htp_archs.begin(), supported_htp_archs.end(),
std::ostream_iterator<std::string>(str_stream, ","));
std::string str = str_stream.str();
ORT_THROW("Wrong value for htp_arch. select from: " + str);
}
} else {
ORT_THROW(R"(Wrong key type entered. Choose from options: ['backend_path',
'profiling_level', 'rpc_control_latency', 'vtcm_mb', 'htp_performance_mode',
'qnn_saver_path', 'htp_graph_finalization_optimization_mode', 'qnn_context_priority'])");
'qnn_saver_path', 'htp_graph_finalization_optimization_mode', 'qnn_context_priority',
'soc_model', 'htp_arch', 'device_id'])");
}
qnn_options[key] = value;

View file

@ -78,6 +78,10 @@ namespace perftest {
"\t [QNN only] [qnn_saver_path]: QNN Saver backend path. e.g '/folderpath/libQnnSaver.so'.\n"
"\t [QNN only] [htp_graph_finalization_optimization_mode]: QNN graph finalization optimization mode, options: \n"
"\t '0', '1', '2', '3', default is '0'.\n"
"\t [QNN only] [soc_model]: The SoC Model number. Refer to QNN SDK documentation for specific values. Defaults to '0' (unknown). \n"
"\t [QNN only] [htp_arch]: The minimum HTP architecture. The driver will use ops compatible with this architecture. \n"
"\t Options are '0', '68', '69', '73', '75'. Defaults to '0' (none). \n"
"\t [QNN only] [device_id]: The ID of the device to use when setting 'htp_arch'. Defaults to '0' (for single device). \n"
"\t [Usage]: -e <provider_name> -i '<key1>|<value1> <key2>|<value2>'\n\n"
"\t [Example] [For OpenVINO EP] -e openvino -i \"device_type|CPU_FP32 enable_npu_fast_compile|true num_of_threads|5 enable_opencl_throttling|true cache_dir|\"<path>\"\"\n"
"\t [Example] [For QNN EP] -e qnn -i \"backend_path|/folderpath/libQnnCpu.so\" \n\n"

View file

@ -343,7 +343,7 @@ OnnxRuntimeTestSession::OnnxRuntimeTestSession(Ort::Env& env, std::random_device
if (supported_profiling_level.find(value) == supported_profiling_level.end()) {
ORT_THROW("Supported profiling_level: off, basic, detailed");
}
} else if (key == "rpc_control_latency" || key == "vtcm_mb") {
} else if (key == "rpc_control_latency" || key == "vtcm_mb" || key == "soc_model" || key == "device_id") {
// no validation
} else if (key == "htp_performance_mode") {
std::set<std::string> supported_htp_perf_mode = {"burst", "balanced", "default", "high_performance",
@ -372,10 +372,20 @@ OnnxRuntimeTestSession::OnnxRuntimeTestSession(Ort::Env& env, std::random_device
if (supported_qnn_context_priority.find(value) == supported_qnn_context_priority.end()) {
ORT_THROW("Supported qnn_context_priority: low, normal, normal_high, high");
}
} else if (key == "htp_arch") {
std::unordered_set<std::string> supported_htp_archs = {"0", "68", "69", "73", "75"};
if (supported_htp_archs.find(value) == supported_htp_archs.end()) {
std::ostringstream str_stream;
std::copy(supported_htp_archs.begin(), supported_htp_archs.end(),
std::ostream_iterator<std::string>(str_stream, ","));
std::string str = str_stream.str();
ORT_THROW("Wrong value for htp_arch. select from: " + str);
}
} else {
ORT_THROW(R"(Wrong key type entered. Choose from options: ['backend_path',
'profiling_level', 'rpc_control_latency', 'vtcm_mb', 'htp_performance_mode',
'qnn_saver_path', 'htp_graph_finalization_optimization_mode', 'qnn_context_priority'])");
'qnn_saver_path', 'htp_graph_finalization_optimization_mode', 'qnn_context_priority', 'soc_model',
'htp_arch', 'device_id'])");
}
qnn_options[key] = value;

View file

@ -176,7 +176,10 @@ TEST(QnnEP, TestDisableCPUFallback_ConflictingConfig) {
// types and shapes.
static void RunNHWCResizeModel(const ORTCHAR_T* ort_model_path, bool use_htp, bool enable_qnn_saver = false,
std::string htp_graph_finalization_opt_mode = "",
std::string qnn_context_priority = "") {
std::string qnn_context_priority = "",
std::string soc_model = "",
std::string htp_arch = "",
std::string device_id = "") {
Ort::SessionOptions so;
// Ensure all type/shape inference warnings result in errors!
@ -205,6 +208,18 @@ static void RunNHWCResizeModel(const ORTCHAR_T* ort_model_path, bool use_htp, bo
options["qnn_context_priority"] = std::move(qnn_context_priority);
}
if (!soc_model.empty()) {
options["soc_model"] = std::move(soc_model);
}
if (!htp_arch.empty()) {
options["htp_arch"] = std::move(htp_arch);
}
if (!device_id.empty()) {
options["device_id"] = std::move(device_id);
}
so.AppendExecutionProvider("QNN", options);
Ort::Session session(*ort_env, ort_model_path, so);
@ -519,6 +534,45 @@ TEST_F(QnnHTPBackendTests, HTPGraphFinalizationOptimizationModes) {
}
}
// Test that models run with various SoC model values
TEST_F(QnnHTPBackendTests, HTPSocModels) {
constexpr std::array<const char*, 3> soc_models = { "", // No explicit SoC model specified
"0", // "Unknown"
#if defined(_M_ARM64)
"37" }; // SC8280X
#elif defined(__linux__)
"30" }; // SM8350
#else
"" };
#endif
for (auto soc_model : soc_models) {
RunNHWCResizeModel(ORT_MODEL_FOLDER "nhwc_resize_sizes_opset18.quant.onnx",
true, // use_htp
false, // enable_qnn_saver
"", // htp_graph_finalization_opt_mode
"", // qnn_context_priority
soc_model);
}
}
// Test that models run with various HTP architecture values (and set device_id)
TEST_F(QnnHTPBackendTests, HTPArchValues) {
constexpr std::array<const char*, 3> htp_archs = {"", // No explicit arch specified
"0", // "None"
"68"}; // v68
for (auto htp_arch : htp_archs) {
RunNHWCResizeModel(ORT_MODEL_FOLDER "nhwc_resize_sizes_opset18.quant.onnx",
true, // use_htp
false, // enable_qnn_saver
"", // htp_graph_finalization_opt_mode
"", // qnn_context_priority
"", // soc_model
htp_arch, // htp_arch
"0"); // device_id
}
}
// Test that models run with high QNN context priority.
TEST_F(QnnHTPBackendTests, QnnContextPriorityHigh) {
RunNHWCResizeModel(ORT_MODEL_FOLDER "nhwc_resize_sizes_opset18.quant.onnx",