Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
return CheckHtpDataTypes(input_qnn_dtypes, output_qnn_dtypes);
} else if (IsGpuBackend(qnn_model_wrapper.GetQnnBackendType())) {
return CheckGpuDataTypes(input_qnn_dtypes, output_qnn_dtypes);
} else if (IsIrBackend(qnn_model_wrapper.GetQnnBackendType())) {
// TODO: CheckIrDataTypes

Check warning on line 80 in onnxruntime/core/providers/qnn/builder/opbuilder/base_op_builder.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Missing username in TODO; it should look like "// TODO(my_username): Stuff." [readability/todo] [2] Raw Output: onnxruntime/core/providers/qnn/builder/opbuilder/base_op_builder.cc:80: Missing username in TODO; it should look like "// TODO(my_username): Stuff." [readability/todo] [2]
return Status::OK();
}
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Only support backend: CPU, HTP and GPU");
}
Expand Down
4 changes: 4 additions & 0 deletions onnxruntime/core/providers/qnn/builder/qnn_def.cc
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,10 @@ bool QnnOpConfigWrapper::CreateQnnGraphOp(const QNN_INTERFACE_VER_TYPE& qnn_inte
return true;
}

bool IsIrBackend(QnnBackendType backend_type) {
return backend_type == QnnBackendType::SERIALIZER;
}

bool IsNpuBackend(QnnBackendType backend_type) {
return backend_type == QnnBackendType::HTP || backend_type == QnnBackendType::DSP;
}
Expand Down
2 changes: 2 additions & 0 deletions onnxruntime/core/providers/qnn/builder/qnn_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ enum class QnnBackendType : uint8_t {
SERIALIZER,
};

bool IsIrBackend(QnnBackendType backend_type);

bool IsCpuBackend(QnnBackendType backend_type);

bool IsNpuBackend(QnnBackendType backend_type);
Expand Down
70 changes: 70 additions & 0 deletions onnxruntime/test/providers/qnn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# ONNX Runtime QNN Execution Provider Tests
## Overview
1. The `onnxruntime/test/providers/qnn` directory contains integration tests for the Qualcomm Neural Network (QNN) execution provider.
2. Most testcases run an ONNX model through the QNN-EP, then verifies the inference result against the one on CPU-EP

## Building the Tests
The tests are built as part of the regular ONNX Runtime build. After a successful build you will have an executable named
- onnxruntime_provider_test.exe (Windows)
- onnxruntime_provider_test (Linux/macOS)

## Running the Tests
1. QNN supports several backends. You can use the standard Google‑Test syntax for filtering:
- `onnxruntime_provider_test.exe --gtest_filter=QnnCPUBackendTests.*`
- `onnxruntime_provider_test.exe --gtest_filter=QnnHTPBackendTests.*`
- `onnxruntime_provider_test.exe --gtest_filter=QnnGPUBackendTests.*`
- `onnxruntime_provider_test.exe --gtest_filter=QnnIRBackendTests.*`
2. Saving Test Artifacts
- For debugging it is often helpful to keep the intermediate files that the tests generate. The following environment
variables are recognized by the test binary:
- `QNN_DUMP_ONNX`: Saves the input ONNX model used for the test
- `QNN_DUMP_JSON`: Save json qnn graph with provider_option `dump_json_qnn_graph`
- `QNN_DUMP_DLC`: Saves the compiled QNN DLC file by specifying the provider_option `backend_path` to `QnnIr.dll`
- The artifacts will be saved to a directory named with `<TestSuite>_<TestName>`
```
.
├── QnnCPUBackendTests_BatchNorm2D_fp32 # RunQnnModelTest
│ ├── dumped_f32_model.onnx # float32 ONNX model
│ ├── QNNExecutionProvider_QNN_XXXX_X_X.dlc
│ └── QNNExecutionProvider_QNN_XXXX_X_X.json
├── QnnHTPBackendTests_BatchNorm_FP16 # TestFp16ModelAccuracy
│ ├── dumped_f16_model.onnx # float16 ONNX model
│ ├── dumped_f32_model.onnx # float32 ONNX model
│ ├── QNNExecutionProvider_QNN_XXXX_X_X.dlc
│ └── QNNExecutionProvider_QNN_XXXX_X_X.json
└── QnnHTPBackendTests_BatchNorm2D_U8U8S32 # TestQDQModelAccuracy
├── dumped_f32_model.onnx # float32 ONNX model
├── dumped_qdq_model.onnx # QDQ ONNX model
├── QNNExecutionProvider_QNN_XXXX_X_X.dlc
└── QNNExecutionProvider_QNN_XXXX_X_X.json

# All artifact files are placed under the current working directory from which the test binary is invoked.
```
3. Verbose
- `QNN_VERBOSE`: Sets the ONNX Runtime log level to `ORT_LOGGING_LEVEL_VERBOSE`

4. You can enable any combination of these environment variables, for example:
- On Linux/macOS
```bash
export QNN_DUMP_ONNX=1
export QNN_DUMP_JSON=1
export QNN_DUMP_DLC=1
export QNN_VERBOSE=1
```
- On Windows
```cmd
set QNN_DUMP_ONNX=1
set QNN_DUMP_JSON=1
set QNN_DUMP_DLC=1
set QNN_VERBOSE=1
```
```ps1
$Env:QNN_DUMP_ONNX = "1"
$Env:QNN_DUMP_JSON = "1"
$Env:QNN_DUMP_DLC = "1"
$Env:QNN_VERBOSE = "1"
```

# Note
- An issue on QNN backends can prevent the test artifacts from being successfully saved.
- The `onnxruntime_provider_test.exe` does not automatically delete the artifact directories, so you may want to prune them after a debugging session.
60 changes: 60 additions & 0 deletions onnxruntime/test/providers/qnn/qnn_test_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ void RunQnnModelTest(const GetTestModelFn& build_test_case, ProviderOptions prov
int opset_version, ExpectedEPNodeAssignment expected_ep_assignment,
float fp32_abs_err, logging::Severity log_severity, bool verify_outputs,
std::function<void(const Graph&)>* ep_graph_checker) {
std::filesystem::path output_dir;
if (QNNTestEnvironment::GetInstance().dump_onnx() ||
QNNTestEnvironment::GetInstance().dump_json() ||
QNNTestEnvironment::GetInstance().dump_dlc()) {
output_dir = QNNTestEnvironment::GetInstance().CreateTestcaseDirs();
}
EPVerificationParams verification_params;
verification_params.ep_node_assignment = expected_ep_assignment;
verification_params.fp32_abs_err = fp32_abs_err;
Expand All @@ -110,6 +116,10 @@ void RunQnnModelTest(const GetTestModelFn& build_test_case, ProviderOptions prov

auto& logging_manager = DefaultLoggingManager();
logging_manager.SetDefaultLoggerSeverity(log_severity);
if (QNNTestEnvironment::GetInstance().verbose()) {
logging_manager.RemoveSink(logging::SinkType::EtwSink);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity, why is the ETW log sink removed?

logging_manager.SetDefaultLoggerSeverity(logging::Severity::kVERBOSE);
}

onnxruntime::Model model("QNN_EP_TestModel", false, ModelMetaData(), PathString(),
IOnnxRuntimeOpSchemaRegistryList(), domain_to_version, {},
Expand All @@ -123,7 +133,27 @@ void RunQnnModelTest(const GetTestModelFn& build_test_case, ProviderOptions prov
// Serialize the model to a string.
std::string model_data;
model.ToProto().SerializeToString(&model_data);

if (QNNTestEnvironment::GetInstance().dump_onnx()) {
auto dump_path = output_dir / ToPathString("dumped_f32_model.onnx");
LOGS(logging_manager.DefaultLogger(), VERBOSE) << "Save onnx model at: " << dump_path;
ASSERT_STATUS_OK(onnxruntime::Model::Save(model, dump_path));
}

TryEnableQNNSaver(provider_options);
if (QNNTestEnvironment::GetInstance().dump_dlc()) {
provider_options["dump_qnn_ir_dlc"] = "1";
provider_options["dump_qnn_ir_dlc_dir"] = output_dir.string();
#if defined(_WIN32)
provider_options["qnn_ir_backend_path"] = "QnnIr.dll";
#else
provider_options["qnn_ir_backend_path"] = "libQnnIr.so";
#endif // defined(_WIN32)
}
if (QNNTestEnvironment::GetInstance().dump_json()) {
provider_options["dump_json_qnn_graph"] = "1";
provider_options["json_qnn_graph_dir"] = output_dir.string();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: some of the test setup code is duplicated a few times. would it be worth refactoring it into helper functions?

RunAndVerifyOutputsWithEP(AsByteSpan(model_data.data(), model_data.size()), "QNN_EP_TestLogID",
QnnExecutionProviderWithOptions(provider_options),
helper.feeds_, verification_params,
Expand All @@ -134,11 +164,21 @@ void RunQnnModelTestHTPNoVerify(const GetTestModelFn& build_test_case, ProviderO
int opset_version, ExpectedEPNodeAssignment expected_ep_assignment,
logging::Severity log_severity,
std::function<void(const Graph&)>* ep_graph_checker) {
std::filesystem::path output_dir;
if (QNNTestEnvironment::GetInstance().dump_onnx() ||
QNNTestEnvironment::GetInstance().dump_dlc() ||
QNNTestEnvironment::GetInstance().dump_json()) {
output_dir = QNNTestEnvironment::GetInstance().CreateTestcaseDirs();
}
// Add kMSDomain to cover contrib op like Gelu
const std::unordered_map<std::string, int> domain_to_version = {{"", opset_version}, {kMSDomain, 1}};

auto& logging_manager = DefaultLoggingManager();
logging_manager.SetDefaultLoggerSeverity(log_severity);
if (QNNTestEnvironment::GetInstance().verbose()) {
logging_manager.RemoveSink(logging::SinkType::EtwSink);
logging_manager.SetDefaultLoggerSeverity(logging::Severity::kVERBOSE);
}

onnxruntime::Model model("QNN_EP_TestModel", false, ModelMetaData(), PathString(),
IOnnxRuntimeOpSchemaRegistryList(), domain_to_version, {},
Expand All @@ -152,7 +192,27 @@ void RunQnnModelTestHTPNoVerify(const GetTestModelFn& build_test_case, ProviderO
// Serialize the model to a string.
std::string model_data;
model.ToProto().SerializeToString(&model_data);

if (QNNTestEnvironment::GetInstance().dump_onnx()) {
auto dump_path = output_dir / ToPathString("dumped_f32_model.onnx");
LOGS(logging_manager.DefaultLogger(), VERBOSE) << "Save onnx model at: " << dump_path;
ASSERT_STATUS_OK(onnxruntime::Model::Save(model, dump_path));
}

TryEnableQNNSaver(provider_options);
if (QNNTestEnvironment::GetInstance().dump_dlc()) {
provider_options["dump_qnn_ir_dlc"] = "1";
provider_options["dump_qnn_ir_dlc_dir"] = output_dir.string();
#if defined(_WIN32)
provider_options["qnn_ir_backend_path"] = "QnnIr.dll";
#else
provider_options["qnn_ir_backend_path"] = "libQnnIr.so";
#endif // defined(_WIN32)
}
if (QNNTestEnvironment::GetInstance().dump_json()) {
provider_options["dump_json_qnn_graph"] = "1";
provider_options["json_qnn_graph_dir"] = output_dir.string();
}

SessionOptions so;
so.session_logid = "QNN_EP_TestLogID";
Expand Down
Loading
Loading