From 9b67a85c01382977f441bd7a6c8a68859cb4ea70 Mon Sep 17 00:00:00 2001 From: monumeen Date: Fri, 5 Jun 2026 11:32:30 +0530 Subject: [PATCH 01/36] rebased pr --- .../qnn/builder/qnn_backend_manager.cc | 245 ++++++++++++++++-- .../qnn/builder/qnn_backend_manager.h | 63 ++++- .../core/providers/qnn/builder/qnn_def.h | 16 +- .../builder/qnn_htp_power_config_manager.cc | 78 ++++++ .../builder/qnn_htp_power_config_manager.h | 15 ++ .../core/providers/qnn/builder/timer.cc | 89 +++++++ .../core/providers/qnn/builder/timer.h | 73 ++++++ .../providers/qnn/qnn_execution_provider.cc | 93 ++++--- .../providers/qnn/qnn_execution_provider.h | 2 + .../test/providers/qnn/qnn_basic_test.cc | 49 ++++ 10 files changed, 663 insertions(+), 60 deletions(-) create mode 100644 onnxruntime/core/providers/qnn/builder/timer.cc create mode 100644 onnxruntime/core/providers/qnn/builder/timer.h diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 0804cb3448..cbcb2b1fe7 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -158,6 +158,208 @@ Ort::Status ReadBinaryFromFile(const std::string& file_path, uint8_t* buffer, si return Ort::Status(); } +bool QnnBackendManager::IsTimerThreadRunning() { + std::chrono::microseconds remainUs = std::chrono::microseconds::zero(); + uint64_t remaining_duration = 0; + if (timer_ && timer_->TimerInUse() && timer_->RemainingDuration(remainUs)) { + remaining_duration = static_cast(remainUs.count()); + return remaining_duration > 0 && remaining_duration < timer_resource_.sustained_timer_duration_; + } + return false; +} + +Ort::Status QnnBackendManager::SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, + QnnHtpPerfInfrastructure_PowerConfig_t power_config, + uint32_t rpc_polling_time, + uint32_t rpc_control_latency) { + RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete."); + RETURN_IF_ERROR(htp_power_config_manager_.AddRpcPollingTime(rpc_polling_time, *logger_ptr_)); + RETURN_IF_ERROR(htp_power_config_manager_.AddRpcControlLatency(rpc_control_latency, *logger_ptr_)); + RETURN_IF_ERROR(htp_power_config_manager_.AddHtpPerformanceConfig(power_config)); + RETURN_IF_ERROR(htp_power_config_manager_.SetPowerConfig(htp_power_config_client_id, GetQnnInterface(), *logger_ptr_)); + + return Ort::Status(); +} + +Ort::Status QnnBackendManager::SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { + std::lock_guard lk(perf_mutex_); + Ort::Status status = Ort::Status(); + + std::chrono::microseconds sustainedDurationUs(timer_resource_.sustained_timer_duration_); + + switch (graph_state_) { + case GraphState::RUN_DONE: + if (IsTimerThreadRunning()) { + timer_->AbortTimer(); + } + RETURN_IF_NOT(timer_->Launch(sustainedDurationUs), "Not able to launch timer thread."); + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + break; + case GraphState::RUN_START: + if (IsTimerThreadRunning()) { + timer_->AbortTimer(); + } else { + status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + } + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = true; + break; + case GraphState::INIT_DONE: { + QnnHtpPerfInfrastructure_PowerConfig_t init_done_htp_performance_cfg{}; + htp_power_config_manager_.SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, init_done_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + break; + } + case GraphState::INIT_START: + if (IsTimerThreadRunning()) { + timer_->AbortTimer(); + } else { + status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + } + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = true; + break; + case GraphState::TIMEOUT: { + if (!timer_resource_.caller_busy_) { + QnnHtpPerfInfrastructure_PowerConfig_t timeout_htp_performance_cfg{}; + htp_power_config_manager_.SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, timeout_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + graph_state_ = GraphState::NONE; + } + break; + } + default: + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + break; + } + return status; +} + +Ort::Status QnnBackendManager::SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { + std::lock_guard lk(perf_mutex_); + Ort::Status status = Ort::Status(); + switch (graph_state_) { + case GraphState::RUN_DONE: + case GraphState::INIT_DONE: + switch (performance_mode) { + case qnn::HtpPerformanceMode::kHtpLowBalanced: + case qnn::HtpPerformanceMode::kHtpBalanced: + case qnn::HtpPerformanceMode::kHtpHighPerformance: { + QnnHtpPerfInfrastructure_PowerConfig_t relaxed_htp_performance_cfg{}; + htp_power_config_manager_.SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, relaxed_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + break; + } + case qnn::HtpPerformanceMode::kHtpExtremePowerSaver: { + QnnHtpPerfInfrastructure_PowerConfig_t extreme_power_saver_htp_performance_cfg{}; + htp_power_config_manager_.SetExtremeLowPerfPowerConfig(extreme_power_saver_htp_performance_cfg, htp_power_config_client_id); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, extreme_power_saver_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + break; + } + case qnn::HtpPerformanceMode::kHtpLowPowerSaver: + case qnn::HtpPerformanceMode::kHtpHighPowerSaver: + case qnn::HtpPerformanceMode::kHtpPowerSaver: { + QnnHtpPerfInfrastructure_PowerConfig_t released_htp_performance_cfg{}; + htp_power_config_manager_.SetReleasedPerfPowerConfig(released_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, released_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + break; + } + default: + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); + break; + } + graph_state_ = GraphState::NONE; + break; + case GraphState::RUN_START: + case GraphState::INIT_START: + status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + graph_state_ = GraphState::NONE; + break; + default: + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + break; + } + return status; +} + +Ort::Status QnnBackendManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { + std::lock_guard lk(state_mutex_); + RETURN_IF(timer_resource_.timer_active_ = false, "Timer is not active. Cannot set state."); + if (state != graph_state_) { + graph_state_ = state; + if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { + RETURN_IF(timer_ == nullptr, "timer is not started"); + return SetSustainedPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + } else if (perfMode == qnn::HtpPerformanceMode::kHtpDefault) { + if (timer_ && timer_->TimerInUse()) { + timer_->AbortTimer(); + } + return Ort::Status(); + } else { + if (timer_ && timer_->TimerInUse()) { + timer_->AbortTimer(); + } + return SetPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + } + } + return Ort::Status(); +} + +void QnnBackendManager::TimerCallback(void* user_data) { + TimerCallbackArg* args = static_cast(user_data); + QnnBackendManager* instance = args->instance_; + if (instance->timer_resource_.timer_active_) { + auto rt = instance->SetState(GraphState::TIMEOUT, args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0); + if (!rt.IsOK()) { + ORT_CXX_LOG_PTR(instance->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); + } + } +} + +void QnnBackendManager::CreateTimerThread(uint32_t htp_power_config_client_id) { + std::lock_guard lk(state_mutex_); + if (timer_ == nullptr) { + std::unique_ptr temp(new Timer()); + if (temp != nullptr) { + timer_ = std::move(temp); + timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this); + if (timer_callback_arg_ == nullptr) { + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create Timer argument"); + timer_.reset(); + return; + } + if (!timer_->Initialize(TimerCallback, timer_callback_arg_.get())) { + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); + timer_callback_arg_.reset(); + timer_.reset(); + } else { + timer_resource_.timer_active_ = false; + } + } else { + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); + } + } else { + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); + } +} + +void QnnBackendManager::ReleaseTimerThread() { + std::lock_guard lk(state_mutex_); + if (timer_ != nullptr) { + { + timer_resource_.timer_active_ = false; + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + } + timer_->DeInitialize(); + timer_callback_arg_.reset(); + timer_.reset(); + } +} + Ort::Status QnnBackendManager::ParseLoraConfig(std::string lora_config_path) { ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_INFO, ("Acquiring the QnnInterface " + lora_config_path).c_str()); @@ -2001,9 +2203,18 @@ Ort::Status QnnBackendManager::SetupBackend( return status; } -Ort::Status QnnBackendManager::CreateHtpPowerCfgId(uint32_t device_id, - uint32_t core_id, - uint32_t& htp_power_config_id) { +Ort::Status QnnBackendManager::InitializePowerCfgId(uint32_t device_id, uint32_t core_id, uint32_t& htp_power_config_id) { + RETURN_IF_ERROR(CreateHtpPowerCfgId(device_id, core_id, htp_power_config_id)); + CreateTimerThread(htp_power_config_id); + return Ort::Status(); +} + +Ort::Status QnnBackendManager::DeInitializePowerCfgId(uint32_t htp_power_config_id) { + DestroyHTPPowerConfigID(htp_power_config_id); + return Ort::Status(); +} + +Ort::Status QnnBackendManager::CreateHtpPowerCfgId(uint32_t device_id, uint32_t core_id, uint32_t& htp_power_config_id) { // This function is called in QNN EP's OnRunStart() even if QNN backend setup failed and the model is assigned // to a different EP. Therefore, we have to check that backend setup actually completed before trying to // create an HTP power config ID. Otherwise, this causes a segfault because the QNN backend lib is unloaded. @@ -2051,29 +2262,19 @@ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id auto htp_power_config_id = htp_power_configs.power_config_id; if (pre_run) { + // add in htp_power_configs the default power config id also so to run when we execute if (htp_power_configs.pre_run_perf_mode.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.AddHtpPerformanceMode(*htp_power_configs.pre_run_perf_mode, - htp_power_config_id, - *logger_ptr_)); - } - - if (htp_power_configs.rpc_control_latency.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.AddRpcControlLatency(*htp_power_configs.rpc_control_latency, - *logger_ptr_)); + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + } else if (htp_power_configs.default_perf_mode.has_value()) { + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } - - if (htp_power_configs.rpc_polling_time.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.AddRpcPollingTime(*htp_power_configs.rpc_polling_time, - *logger_ptr_)); + } else { + if (htp_power_configs.post_run_perf_mode.has_value()) { + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + } else if (htp_power_configs.default_perf_mode.has_value()) { + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } - } else if (htp_power_configs.post_run_perf_mode.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.AddHtpPerformanceMode(*htp_power_configs.post_run_perf_mode, - htp_power_config_id, - *logger_ptr_)); } - - RETURN_IF_ERROR(htp_power_config_manager_.SetPowerConfig(htp_power_config_id, GetQnnInterface(), *logger_ptr_)); - return Ort::Status(); } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index d0dc3f7bae..bec15e64b9 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -37,6 +37,8 @@ #include "core/providers/qnn/builder/qnn_node_group/qnn_node_group.h" #include "core/providers/qnn/builder/qnn_profile_serializer.h" #include "core/providers/qnn/ort_api.h" +#include "core/providers/qnn/builder/timer.h" +#include #ifdef QNN_FILE_MAPPED_WEIGHTS_AVAILABLE #include "core/providers/qnn/builder/qnn_file_mapping_interface.h" @@ -124,6 +126,16 @@ struct QnnBackendManagerConfig { bool skip_qnn_version_check; }; +// Graph states to tune the power/performance configurations +enum class GraphState { + INIT_START, + INIT_DONE, + RUN_START, + RUN_DONE, + TIMEOUT, + NONE +}; + class QnnBackendManager : public std::enable_shared_from_this { private: // private tag to pass to constructor to ensure that constructor cannot be directly called externally @@ -185,12 +197,11 @@ class QnnBackendManager : public std::enable_shared_from_this bool enable_htp_extended_udma_mode = false, bool enable_htp_prepare_only = false); - Ort::Status CreateHtpPowerCfgId(uint32_t deviceId, uint32_t coreId, uint32_t& htp_power_config_id); + Ort::Status InitializePowerCfgId(uint32_t deviceId, uint32_t coreId, uint32_t& htp_power_config_id); - Ort::Status SetHtpPowerConfigs(uint32_t htp_power_config_client_id, - HtpPerformanceMode htp_performance_mode, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency); + Ort::Status DeInitializePowerCfgId(uint32_t htp_power_config_id); + + void ReleaseTimerThread(); Ort::Status SetPerThreadHtpPowerConfigs(const std::thread::id& thread_id, bool pre_run); @@ -264,8 +275,6 @@ class QnnBackendManager : public std::enable_shared_from_this : backend_path.parent_path().string(); } - Ort::Status DestroyHTPPowerConfigID(uint32_t htp_power_config_id); - Ort::Status GetMaxSpillFillBufferSize(unsigned char* buffer, uint64_t buffer_length, uint64_t& max_spill_fill_buffer_size); @@ -313,6 +322,7 @@ class QnnBackendManager : public std::enable_shared_from_this // Releases all QNN resources. Called in the destructor. // NOTE: This function indirectly locks the internal `logger_recursive_mutex_` via nested function calls. void ReleaseResources(); + Ort::Status SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); #ifdef QNN_FILE_MAPPED_WEIGHTS_AVAILABLE typedef struct FileMappingCallbackInfo { @@ -439,6 +449,27 @@ class QnnBackendManager : public std::enable_shared_from_this void* LibFunction(void* handle, const char* symbol, std::string& error_msg); + bool IsTimerThreadRunning(); + + Ort::Status SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + + Ort::Status SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + + static void TimerCallback(void* user_data); + + Ort::Status CreateHtpPowerCfgId(uint32_t deviceId, uint32_t coreId, uint32_t& htp_power_config_id); + + void CreateTimerThread(uint32_t htp_power_config_client_id); + + Ort::Status DestroyHTPPowerConfigID(uint32_t htp_power_config_id); + + Ort::Status SetHtpPowerConfigs(uint32_t htp_power_config_client_id, + HtpPerformanceMode htp_performance_mode, + uint32_t rpc_polling_time, + uint32_t rpc_control_latency); + + Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + template inline T ResolveSymbol(void* lib_handle, const char* sym, const Ort::Logger& logger) { std::string error_msg = ""; @@ -689,6 +720,24 @@ class QnnBackendManager : public std::enable_shared_from_this const Ort::Logger* logger_ptr_; std::shared_ptr rpcmem_library_ = nullptr; + std::mutex perf_mutex_; + std::mutex state_mutex_; + std::unique_ptr timer_; + struct TimerResource { + static constexpr uint64_t sustained_timer_duration_ = kDefaultTimerTimeoutUs; // in microseconds + std::atomic caller_busy_ = false; + std::atomic timer_active_ = false; + }; + TimerResource timer_resource_; + std::atomic graph_state_ = GraphState::NONE; + struct TimerCallbackArg { + uint32_t power_config_id_; + QnnBackendManager* instance_; + + TimerCallbackArg(uint32_t id, QnnBackendManager* manager) + : power_config_id_(id), instance_(manager) {} + }; + std::unique_ptr timer_callback_arg_; }; } // namespace qnn diff --git a/onnxruntime/core/providers/qnn/builder/qnn_def.h b/onnxruntime/core/providers/qnn/builder/qnn_def.h index d3c917ee7e..12b9c9e3de 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_def.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_def.h @@ -84,11 +84,19 @@ enum class HtpPerformanceMode : uint8_t { kHtpExtremePowerSaver, }; +enum class DcvsState { + DCVS_DEFAULT = 0, + DCVS_DISABLE = 1, + DCVS_ENABLE = 2, + DCVS_NUM_STATES +}; + typedef struct PerThreadHtpPowerConfigs { std::optional pre_run_perf_mode; std::optional post_run_perf_mode; std::optional rpc_control_latency; std::optional rpc_polling_time; + std::optional default_perf_mode; uint32_t power_config_id = 0; } PerThreadHtpPowerConfigs_t; @@ -130,17 +138,23 @@ bool IsQpuBackend(QnnBackendType backend_type); std::string QnnBackendTypeToString(QnnBackendType backend_type); -// constexpr config values +// latency values are in microseconds constexpr const int kSleepMinLatency = 40; constexpr const int kSleepLowLatency = 100; constexpr const int kSleepMediumLatency = 1000; constexpr const int kSleepHighLatency = 2000; +constexpr const int kSleepHigherLatency = 65535; + +// constexpr config values constexpr const int kDcvsDisable = 0; constexpr const int kDcvsEnable = 1; constexpr const uint32_t kDisableRpcPolling = 0; constexpr const uint32_t kDisableRpcControlLatency = 0; constexpr const uint32_t kMaxRpcPolling = 9999; +// performance timer timeout value is in microseconds +static constexpr uint64_t kDefaultTimerTimeoutUs = 300000; + struct OnnxTensorInfo { ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(OnnxTensorInfo); OnnxTensorInfo(size_t index, int32_t data_type, std::vector&& shape) : index_(index), data_type_(data_type), shape_(std::move(shape)) {} diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index 152a5fc575..523296e418 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -94,6 +94,13 @@ static std::string_view PerformanceModeToString(HtpPerformanceMode htp_performan return "UNKNOWN"; } +Ort::Status HtpPowerConfigManager::AddHtpPerformanceConfig(QnnHtpPerfInfrastructure_PowerConfig_t htp_performance_cfg) { + power_configs_.emplace_back(std::move(htp_performance_cfg)); + last_set_htp_performance_mode_ = HtpPerformanceMode::kHtpDefault; + htp_performance_mode_set_ = true; + return Ort::Status(); +} + Ort::Status HtpPowerConfigManager::AddHtpPerformanceMode(HtpPerformanceMode htp_performance_mode, uint32_t htp_power_config_client_id, const Ort::Logger& logger) { @@ -289,6 +296,77 @@ Ort::Status HtpPowerConfigManager::SetHtpPerformancePowerConfig(QnnHtpPerfInfras return Ort::Status(); } +void HtpPowerConfigManager::SetRelaxedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, DcvsState dcvsState) { + power_config.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_DCVS_V3; + QnnHtpPerfInfrastructure_DcvsV3_t& dcvs_v3 = power_config.dcvsV3Config; + dcvs_v3.contextId = htp_power_config_client_id; + dcvs_v3.dcvsEnable = 1; + dcvs_v3.setDcvsEnable = 1; + dcvs_v3.sleepLatency = kSleepHighLatency; + dcvs_v3.setSleepLatency = 1; + dcvs_v3.sleepDisable = 0; + dcvs_v3.setSleepDisable = 0; + if (dcvsState == DcvsState::DCVS_ENABLE) { + dcvs_v3.powerMode = QNN_HTP_PERF_INFRASTRUCTURE_POWERMODE_ADJUST_UP_DOWN; + } else { + dcvs_v3.powerMode = QNN_HTP_PERF_INFRASTRUCTURE_POWERMODE_POWER_SAVER_MODE; + } + dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS2; + dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS; + dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS; + dcvs_v3.setBusParams = 1; + dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS2; + dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS; + dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS; + dcvs_v3.setCoreParams = 1; +} + +void HtpPowerConfigManager::SetExtremeLowPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id) { + power_config.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_DCVS_V3; + QnnHtpPerfInfrastructure_DcvsV3_t& dcvs_v3 = power_config.dcvsV3Config; + dcvs_v3.contextId = htp_power_config_client_id; + dcvs_v3.dcvsEnable = 1; + dcvs_v3.setDcvsEnable = 1; + dcvs_v3.sleepLatency = kSleepHigherLatency; + dcvs_v3.setSleepLatency = 1; + dcvs_v3.sleepDisable = 0; + dcvs_v3.setSleepDisable = 0; + dcvs_v3.powerMode = QNN_HTP_PERF_INFRASTRUCTURE_POWERMODE_POWER_SAVER_MODE; + dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_CORNER_DISABLE; + dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_CORNER_DISABLE; + dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_CORNER_DISABLE; + dcvs_v3.setBusParams = 1; + dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_CORNER_DISABLE; + dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_CORNER_DISABLE; + dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_CORNER_DISABLE; + dcvs_v3.setCoreParams = 1; +} + +void HtpPowerConfigManager::SetReleasedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, DcvsState dcvsState) { + power_config.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_DCVS_V3; + QnnHtpPerfInfrastructure_DcvsV3_t& dcvs_v3 = power_config.dcvsV3Config; + dcvs_v3.contextId = htp_power_config_client_id; + dcvs_v3.dcvsEnable = 1; + dcvs_v3.setDcvsEnable = 1; + dcvs_v3.sleepLatency = kSleepHigherLatency; + dcvs_v3.setSleepLatency = 1; + dcvs_v3.sleepDisable = 0; + dcvs_v3.setSleepDisable = 0; + if (dcvsState == DcvsState::DCVS_ENABLE) { + dcvs_v3.powerMode = QNN_HTP_PERF_INFRASTRUCTURE_POWERMODE_ADJUST_UP_DOWN; + } else { + dcvs_v3.powerMode = QNN_HTP_PERF_INFRASTRUCTURE_POWERMODE_POWER_SAVER_MODE; + } + dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_MIN_VOLTAGE_CORNER; + dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_MIN_VOLTAGE_CORNER; + dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_MIN_VOLTAGE_CORNER; + dcvs_v3.setBusParams = 1; + dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_MIN_VOLTAGE_CORNER; + dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_MIN_VOLTAGE_CORNER; + dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_MIN_VOLTAGE_CORNER; + dcvs_v3.setCoreParams = 1; +} + } // namespace power } // namespace qnn } // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index eed403bb40..177bf4fd9e 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -40,6 +40,10 @@ class HtpPowerConfigManager { uint32_t htp_power_config_client_id, const Ort::Logger& logger); + // Stages a new HTP power configuration for next power config update + // performance mode is set to default after setting the power config + Ort::Status AddHtpPerformanceConfig(QnnHtpPerfInfrastructure_PowerConfig_t); + // Takes all configs staged for update and attempts to update // the HTP power configurations. If there is nothing staged, // then no attempt will be made. @@ -47,6 +51,17 @@ class HtpPowerConfigManager { const QNN_INTERFACE_VER_TYPE& qnn_interface, const Ort::Logger& logger); + // Sets power config for relaxed performance mode based on DCVS state + void SetRelaxedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, + uint32_t htp_power_config_client_id, + DcvsState dcvsState); + + // Sets power config for released performance mode based on DCVS state + void SetReleasedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, DcvsState dcvsState); + + // Sets power config for extreme low performance mode + void SetExtremeLowPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id); + private: // Sets voltage corner votes for HTP based on the given performance mode Ort::Status SetHtpPerformancePowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, diff --git a/onnxruntime/core/providers/qnn/builder/timer.cc b/onnxruntime/core/providers/qnn/builder/timer.cc new file mode 100644 index 0000000000..981a4fcf14 --- /dev/null +++ b/onnxruntime/core/providers/qnn/builder/timer.cc @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License + +#include "timer.h" + +void Timer::DeInitialize() { + std::unique_lock lk(mtx_); + is_timer_deinit_ = true; + cv_.notify_all(); + lk.unlock(); + if (bkg_thread_.joinable()) { + bkg_thread_.join(); + } +} + +Timer::~Timer() { this->DeInitialize(); } + +void Timer::BkgTimer() { + { + std::unique_lock lk(mtx_); + try { + thread_status_ = threadState::IDLE; + cv_.notify_all(); + } catch (const std::system_error& e) { + ORT_UNUSED_PARAMETER(e); + thread_status_ = threadState::FAILED; + cv_.notify_all(); + } + } + while (true) { + std::unique_lock lk(mtx_); + + if (thread_status_ == threadState::IDLE) { + cv_.wait(lk, [&]() { + return is_timer_launched_ || is_timer_stopped_ || is_timer_deinit_; + }); + } + + if (is_timer_deinit_) { + thread_status_ = threadState::DEINIT; + is_timer_deinit_ = false; + return; + } + + if (is_timer_stopped_) { + thread_status_ = threadState::IDLE; + is_timer_stopped_ = false; + cv_.notify_all(); + } + + if (thread_status_ == threadState::LAUNCH) { + bool isElapsed = !cv_.wait_until(lk, end_time_, [&]() { + return is_timer_stopped_ || is_timer_deinit_; + }); + if (isElapsed) { + thread_status_ = threadState::CALLING; + lk.unlock(); + timeout_fn_(timeout_arg_); + lk.lock(); + thread_status_ = threadState::IDLE; + } + is_timer_launched_ = false; + } + } +} + +bool Timer::Initialize(std::function callbackFn, void* callbackArg) { + std::unique_lock lk(mtx_); + timeout_arg_ = callbackArg; + timeout_fn_ = callbackFn; + bkg_thread_ = std::thread(&Timer::BkgTimer, this); + cv_.wait(lk, [&] { return thread_status_ == threadState::IDLE || thread_status_ == threadState::FAILED; }); + if (thread_status_ == threadState::FAILED) { + return false; + } + return true; +} + +void Timer::AbortTimer() { + std::unique_lock lk(mtx_); + is_timer_stopped_ = true; + cv_.notify_all(); + cv_.wait(lk, [&] { return thread_status_ == threadState::IDLE; }); +} + +bool Timer::TimerInUse() { + std::unique_lock lk(mtx_); + return thread_status_ == threadState::LAUNCH; +} diff --git a/onnxruntime/core/providers/qnn/builder/timer.h b/onnxruntime/core/providers/qnn/builder/timer.h new file mode 100644 index 0000000000..300fad7914 --- /dev/null +++ b/onnxruntime/core/providers/qnn/builder/timer.h @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License + +#pragma once + +#include +#include +#include +#include +#include +#include "core/providers/qnn/ort_api.h" + +class Timer { + public: + enum class threadState { + IDLE, // Timer is created + LAUNCH, // Timer starts counting down + CALLING, // Callback function is called + DEINIT, // Timer is deinit + FAILED // Timer thread failed to start + }; + // constructor + Timer() = default; + // destructor + ~Timer(); + + template + bool RemainingDuration(std::chrono::duration& duration) { + std::unique_lock lk(mtx_); + if (thread_status_ == threadState::LAUNCH) { + duration = std::chrono::duration_cast>(end_time_ - std::chrono::steady_clock::now()); + return true; + } else if (thread_status_ == threadState::CALLING || thread_status_ == threadState::IDLE) { + duration = std::chrono::duration::zero(); + return true; + } else { + duration = std::chrono::duration::zero(); + return false; + } + } + + template + bool Launch(const std::chrono::duration& timeoutVal) { + std::unique_lock lk(mtx_); + if (thread_status_ != threadState::IDLE) { + return false; + } + end_time_ = std::chrono::steady_clock::now() + timeoutVal; + thread_status_ = threadState::LAUNCH; + is_timer_launched_ = true; + cv_.notify_all(); + return true; + } + + bool Initialize(std::function callbackFn, void* callbackArg); + void DeInitialize(); + void AbortTimer(); + + bool TimerInUse(); + + private: + std::thread bkg_thread_; + void BkgTimer(); + std::mutex mtx_; + std::condition_variable cv_; + std::function timeout_fn_; + void* timeout_arg_{nullptr}; + std::atomic thread_status_{threadState::DEINIT}; + std::chrono::time_point end_time_; + std::atomic is_timer_stopped_ = false; + std::atomic is_timer_deinit_ = false; + std::atomic is_timer_launched_ = false; +}; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 731339cc5a..e3b3f15682 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -1103,10 +1103,10 @@ QnnEp::~QnnEp() { if (qnn_backend_manager_) { auto thread_id = std::this_thread::get_id(); qnn_backend_manager_->RemovePerThreadHtpPowerConfigMapping(thread_id); - + qnn_backend_manager_->ReleaseTimerThread(); std::lock_guard lock(config_id_mutex_); if (htp_power_config_id_.has_value()) { - qnn_backend_manager_->DestroyHTPPowerConfigID(*htp_power_config_id_); + qnn_backend_manager_->DeInitializePowerCfgId(*htp_power_config_id_); } } @@ -2068,9 +2068,25 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, QnnEp* ep = static_cast(this_ptr); if (qnn::IsOrtGraphHasCtxNode(graphs, count, ep->ort_api)) { - return ep->CompileContextModel(graphs, fused_nodes, count, node_compute_infos); + uint32_t htp_power_config_id = 0; + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } + auto status = ep->CompileContextModel(graphs, fused_nodes, count, node_compute_infos); + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } + return status; } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { - return ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); + uint32_t htp_power_config_id = 0; + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } + auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } + return status; } #if defined(_WIN32) && (defined(__aarch64__) || defined(_M_ARM64)) @@ -2182,7 +2198,17 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, } else { finalize_start = std::chrono::steady_clock::now(); #endif - RETURN_IF_NOT_OK(qnn_model->FinalizeGraphs(ep->logger_)); + uint32_t htp_power_config_id = 0; + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } + auto finalize_status = qnn_model->FinalizeGraphs(ep->logger_); + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } + if (!finalize_status.IsOK()) { + return finalize_status.release(); + } #if defined(_WIN32) && (defined(__aarch64__) || defined(_M_ARM64)) end = std::chrono::steady_clock::now(); total_finalize_time += std::chrono::duration_cast(end - finalize_start); @@ -2207,6 +2233,10 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, if (use_multithreaded_prepare) { qnn::thread::QnnJobThreadPool tp(ep->num_graph_prepare_threads_); tp.Start(); + uint32_t htp_power_config_id = 0; + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } finalize_start = std::chrono::steady_clock::now(); for (auto& model_info : model_infos) { tp.SubmitJob([qnn_model = model_info.model.get(), &logger = ep->logger_, res = &model_info.result] { @@ -2216,7 +2246,9 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, tp.WaitForQueuedJobsToFinish(); end = std::chrono::steady_clock::now(); total_finalize_time = std::chrono::duration_cast(end - finalize_start); - + if (ep->GetHtpPowerConfigId(htp_power_config_id)) { + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + } for (auto& model_info : model_infos) { RETURN_IF_NOT_OK(std::move(model_info.result)); @@ -2356,17 +2388,31 @@ bool QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thr configs_set = true; ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, (std::string("rpc_control_latency: ") + rpc_latency).c_str()); + } else { + per_thread_htp_power_configs.rpc_control_latency = default_rpc_control_latency_; + configs_set = true; } - uint32_t rpc_polling_time = 0; if (qnn::HtpPerformanceMode::kHtpBurst == pre_run_htp_performance_mode) { - rpc_polling_time = 9999; + per_thread_htp_power_configs.rpc_polling_time = 9999; + configs_set = true; + } else { + per_thread_htp_power_configs.rpc_polling_time = default_rpc_polling_time_; + configs_set = true; + } + + if (qnn::HtpPerformanceMode::kHtpDefault != dynamic_htp_performance_mode_) { + // reset perf mode, rpc control latency and rpc polling time to dynamic perf mode values + per_thread_htp_power_configs.default_perf_mode = dynamic_htp_performance_mode_; + configs_set = true; + } else if (qnn::HtpPerformanceMode::kHtpDefault != default_htp_performance_mode_) { + per_thread_htp_power_configs.default_perf_mode = default_htp_performance_mode_; + configs_set = true; } if (qnn::HtpPerformanceMode::kHtpDefault != pre_run_htp_performance_mode) { per_thread_htp_power_configs.pre_run_perf_mode = pre_run_htp_performance_mode; // rpc polling time will only be updated with perf mode changes - per_thread_htp_power_configs.rpc_polling_time = rpc_polling_time; configs_set = true; } @@ -2493,17 +2539,13 @@ OrtStatus* ORT_API_CALL QnnEp::SetDynamicOptionsImpl(_In_ OrtEp* this_ptr, qnn::HtpPerformanceMode htp_performance_mode = qnn::HtpPerformanceMode::kHtpDefault; ParseHtpPerformanceMode(value, htp_performance_mode, ep->logger_); - uint32_t rpc_polling_time = 0; - if (htp_performance_mode == qnn::HtpPerformanceMode::kHtpBurst) { - rpc_polling_time = 9999; - } - - uint32_t htp_power_config_id = 0; - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetHtpPowerConfigs(htp_power_config_id, - htp_performance_mode, - rpc_polling_time, - ep->default_rpc_control_latency_)); + if (htp_performance_mode != qnn::HtpPerformanceMode::kHtpDefault) { + ep->dynamic_htp_performance_mode_ = htp_performance_mode; + if (htp_performance_mode == qnn::HtpPerformanceMode::kHtpBurst) { + ep->default_rpc_polling_time_ = 9999; + } else { + ep->default_rpc_polling_time_ = 0; + } } } else { ORT_CXX_LOG(ep->logger_, @@ -2677,19 +2719,10 @@ void QnnEp::CreateHtpPowerConfigId() const { constexpr uint32_t core_id = 0; uint32_t htp_power_config_id; - Ort::Status rt = qnn_backend_manager_->CreateHtpPowerCfgId(device_id_, core_id, htp_power_config_id); + Ort::Status rt = qnn_backend_manager_->InitializePowerCfgId(device_id_, core_id, htp_power_config_id); if (rt.IsOK()) { htp_power_config_id_ = htp_power_config_id; - - rt = qnn_backend_manager_->SetHtpPowerConfigs(htp_power_config_id, - default_htp_performance_mode_, - default_rpc_polling_time_, - default_rpc_control_latency_); - - if (!rt.IsOK()) { - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_ERROR, "Unable to set HTP power configurations."); - } } else { ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_ERROR, "Failed to create HTP power config id."); } diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.h b/onnxruntime/core/providers/qnn/qnn_execution_provider.h index c90b7844f7..6bd38071da 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.h +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.h @@ -183,6 +183,7 @@ class QnnEp : public OrtEp, public ApiPtrs { bool disable_cpu_ep_fallback_ = false; // True if CPU EP fallback has been disabled for this session. bool qnn_context_embed_mode_ = true; + bool stop_share_ep_contexts_ = false; bool prepare_only_ = false; bool enable_spill_fill_buffer_ = false; @@ -203,6 +204,7 @@ class QnnEp : public OrtEp, public ApiPtrs { // Configurations for HTP backend. uint32_t device_id_{0}; qnn::HtpPerformanceMode default_htp_performance_mode_{qnn::HtpPerformanceMode::kHtpDefault}; + qnn::HtpPerformanceMode dynamic_htp_performance_mode_{qnn::HtpPerformanceMode::kHtpDefault}; uint32_t default_rpc_control_latency_ = 0; uint32_t default_rpc_polling_time_ = 0; qnn::ModelSettings model_settings_ = {}; diff --git a/onnxruntime/test/providers/qnn/qnn_basic_test.cc b/onnxruntime/test/providers/qnn/qnn_basic_test.cc index 65664c47ba..c49fe99f26 100644 --- a/onnxruntime/test/providers/qnn/qnn_basic_test.cc +++ b/onnxruntime/test/providers/qnn/qnn_basic_test.cc @@ -983,6 +983,55 @@ TEST_F(QnnHTPBackendTests, MultithreadDefaultHtpPowerCfgFromEpOption) { } } +// Tests running a single session in multiple threads on the HTP backend with EP option to set default power config to sustained high performance +TEST_F(QnnHTPBackendTests, MultithreadSustainedHighPowerCfgFromEpOption) { + std::unique_ptr model; + std::vector input_data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + std::vector shape = {1, 3, 2}; + std::vector> output_shapes = {shape}; + std::vector> output_values = {{3.0f, 6.0f, 9.0f, 12.0f, 15.0f, 18.0f}}; + + CreateModelInMemory(model, + QDQBuildAdd3Tensors(TestInputDef(shape, false, input_data), + TestInputDef(shape, false, input_data), + TestInputDef(shape, false, input_data)), + "add3.qdq"); + + onnxruntime::ProviderOptions options; + +#if defined(_WIN32) + options["backend_path"] = "QnnHtp.dll"; +#else + options["backend_path"] = "libQnnHtp.so"; +#endif + options["offload_graph_io_quantization"] = "0"; + options["htp_performance_mode"] = "sustained_high_performance"; + + Ort::RunOptions run_opts; + run_opts.SetRunTag("logger0"); + + Ort::SessionOptions session_opts; + session_opts.SetLogId("logger0"); + + RegisteredEpDeviceUniquePtr registered_ep_device; + RegisterQnnEpLibrary(registered_ep_device, session_opts, onnxruntime::kQnnExecutionProvider, options); + + Ort::Session session(*ort_env, model->model_data.data(), model->model_data.size(), session_opts); + + std::vector threads; + constexpr int num_threads = 5; + constexpr int loop_count = 10; + + for (int i = 0; i < num_threads; i++) { + threads.push_back(std::thread(RunSessionAndVerify, std::ref(session), std::move(run_opts), + std::ref(model->builder.feeds_), output_shapes, output_values, loop_count)); + } + + for (auto& th : threads) { + th.join(); + } +} + // Tests running a single session in multiple threads on the HTP backend with // EP option to set default power config + run option to set power config for each run TEST_F(QnnHTPBackendTests, MultithreadHtpPowerCfgDefaultAndRunOption) { From 78d9f1eeb6c9de425ff603bda12034b4fe809f33 Mon Sep 17 00:00:00 2001 From: monumeen Date: Tue, 14 Apr 2026 23:09:10 +0530 Subject: [PATCH 02/36] Added the review changes --- onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index cbcb2b1fe7..0ff1a05ead 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -287,7 +287,7 @@ Ort::Status QnnBackendManager::SetPerformance(uint32_t htp_power_config_client_i Ort::Status QnnBackendManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { std::lock_guard lk(state_mutex_); - RETURN_IF(timer_resource_.timer_active_ = false, "Timer is not active. Cannot set state."); + RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); if (state != graph_state_) { graph_state_ = state; if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { From 8c60d7f746f17db6ea90dc2bf8dce632efb07574 Mon Sep 17 00:00:00 2001 From: monumeen Date: Tue, 14 Apr 2026 23:24:32 +0530 Subject: [PATCH 03/36] Added the review changes --- onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 0ff1a05ead..8a84134b34 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -287,10 +287,10 @@ Ort::Status QnnBackendManager::SetPerformance(uint32_t htp_power_config_client_i Ort::Status QnnBackendManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { std::lock_guard lk(state_mutex_); - RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); if (state != graph_state_) { graph_state_ = state; if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { + RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); RETURN_IF(timer_ == nullptr, "timer is not started"); return SetSustainedPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); } else if (perfMode == qnn::HtpPerformanceMode::kHtpDefault) { From 3e08f23cae73d6f5873aed9dd17f8b48595027ef Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 15 Apr 2026 09:16:53 +0530 Subject: [PATCH 04/36] fixing test failure --- onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 8a84134b34..b61e677bb6 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -336,7 +336,7 @@ void QnnBackendManager::CreateTimerThread(uint32_t htp_power_config_client_id) { timer_callback_arg_.reset(); timer_.reset(); } else { - timer_resource_.timer_active_ = false; + timer_resource_.timer_active_ = true; } } else { ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); From 7d284423be9d459d07b681abff9a4563a0701593 Mon Sep 17 00:00:00 2001 From: monumeen Date: Thu, 16 Apr 2026 15:48:30 +0530 Subject: [PATCH 05/36] addressing reviews --- .../qnn/builder/qnn_backend_manager.cc | 66 ++++++++++--------- .../core/providers/qnn/builder/timer.cc | 1 + 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index b61e677bb6..4ce5598983 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -286,26 +286,30 @@ Ort::Status QnnBackendManager::SetPerformance(uint32_t htp_power_config_client_i } Ort::Status QnnBackendManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { - std::lock_guard lk(state_mutex_); - if (state != graph_state_) { - graph_state_ = state; - if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { - RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); - RETURN_IF(timer_ == nullptr, "timer is not started"); - return SetSustainedPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); - } else if (perfMode == qnn::HtpPerformanceMode::kHtpDefault) { - if (timer_ && timer_->TimerInUse()) { - timer_->AbortTimer(); - } - return Ort::Status(); + { + std::lock_guard lk(state_mutex_); + if (state != graph_state_) { + graph_state_ = state; } else { - if (timer_ && timer_->TimerInUse()) { - timer_->AbortTimer(); - } - return SetPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); + return Ort::Status(); } } - return Ort::Status(); + if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { + RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); + RETURN_IF(timer_ == nullptr, "timer is not started"); + return SetSustainedPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + } else if (perfMode == qnn::HtpPerformanceMode::kHtpDefault) { + if (timer_ && timer_->TimerInUse()) { + timer_->AbortTimer(); + } + return Ort::Status(); + } else { + if (timer_ && timer_->TimerInUse()) { + timer_->AbortTimer(); + } + return SetPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + } } void QnnBackendManager::TimerCallback(void* user_data) { @@ -347,17 +351,19 @@ void QnnBackendManager::CreateTimerThread(uint32_t htp_power_config_client_id) { } void QnnBackendManager::ReleaseTimerThread() { - std::lock_guard lk(state_mutex_); - if (timer_ != nullptr) { - { - timer_resource_.timer_active_ = false; - graph_state_ = GraphState::NONE; - timer_resource_.caller_busy_ = false; + { + std::lock_guard lk(state_mutex_); + if (timer_ != nullptr) { + { + timer_resource_.timer_active_ = false; + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + } } - timer_->DeInitialize(); - timer_callback_arg_.reset(); - timer_.reset(); } + timer_->DeInitialize(); + timer_callback_arg_.reset(); + timer_.reset(); } Ort::Status QnnBackendManager::ParseLoraConfig(std::string lora_config_path) { @@ -2264,15 +2270,15 @@ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id if (pre_run) { // add in htp_power_configs the default power config id also so to run when we execute if (htp_power_configs.pre_run_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } } else { if (htp_power_configs.post_run_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } } return Ort::Status(); diff --git a/onnxruntime/core/providers/qnn/builder/timer.cc b/onnxruntime/core/providers/qnn/builder/timer.cc index 981a4fcf14..81752ce127 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.cc +++ b/onnxruntime/core/providers/qnn/builder/timer.cc @@ -58,6 +58,7 @@ void Timer::BkgTimer() { timeout_fn_(timeout_arg_); lk.lock(); thread_status_ = threadState::IDLE; + cv_.notify_all(); } is_timer_launched_ = false; } From 6603640111d93538931ec332f5943d1478db3857 Mon Sep 17 00:00:00 2001 From: monumeen Date: Thu, 16 Apr 2026 16:07:22 +0530 Subject: [PATCH 06/36] addressing reviews --- .../core/providers/qnn/builder/qnn_backend_manager.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 4ce5598983..fb30f65fc8 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -2270,15 +2270,15 @@ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id if (pre_run) { // add in htp_power_configs the default power config id also so to run when we execute if (htp_power_configs.pre_run_perf_mode.has_value()) { - RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } } else { if (htp_power_configs.post_run_perf_mode.has_value()) { - RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_NOT_OK(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } } return Ort::Status(); From 303b8eac6b57fdae825e60722b349706de87dcc0 Mon Sep 17 00:00:00 2001 From: monumeen Date: Thu, 16 Apr 2026 23:23:54 +0530 Subject: [PATCH 07/36] addressing reviews --- .../core/providers/qnn/builder/timer.cc | 4 ++-- .../core/providers/qnn/builder/timer.h | 4 ++-- .../providers/qnn/qnn_execution_provider.cc | 23 ++++--------------- .../providers/qnn/qnn_execution_provider.h | 3 +-- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/timer.cc b/onnxruntime/core/providers/qnn/builder/timer.cc index 81752ce127..f2cf07bb4d 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.cc +++ b/onnxruntime/core/providers/qnn/builder/timer.cc @@ -1,5 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: MIT #include "timer.h" diff --git a/onnxruntime/core/providers/qnn/builder/timer.h b/onnxruntime/core/providers/qnn/builder/timer.h index 300fad7914..53c424c795 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.h +++ b/onnxruntime/core/providers/qnn/builder/timer.h @@ -1,5 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: MIT #pragma once diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index e3b3f15682..f399a938a9 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2360,13 +2360,11 @@ OrtStatus* ORT_API_CALL QnnEp::ShouldConvertDataLayoutForOpImpl(_In_ OrtEp* this return nullptr; } -bool QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thread_htp_power_configs, +void QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thread_htp_power_configs, const ::OrtRunOptions* run_options) { qnn::HtpPerformanceMode pre_run_htp_performance_mode = qnn::HtpPerformanceMode::kHtpDefault; qnn::HtpPerformanceMode post_run_htp_performance_mode = qnn::HtpPerformanceMode::kHtpDefault; - bool configs_set = false; - const char* htp_perf_mode = nullptr; htp_perf_mode = ort_api.GetRunConfigEntry(run_options, kOrtRunOptionsConfigQnnPerfMode); if (htp_perf_mode != nullptr) { @@ -2385,43 +2383,33 @@ bool QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thr if (rpc_latency != nullptr) { rpc_control_latency = static_cast(std::stoul(rpc_latency)); per_thread_htp_power_configs.rpc_control_latency = rpc_control_latency; - configs_set = true; ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, (std::string("rpc_control_latency: ") + rpc_latency).c_str()); } else { per_thread_htp_power_configs.rpc_control_latency = default_rpc_control_latency_; - configs_set = true; } if (qnn::HtpPerformanceMode::kHtpBurst == pre_run_htp_performance_mode) { per_thread_htp_power_configs.rpc_polling_time = 9999; - configs_set = true; } else { per_thread_htp_power_configs.rpc_polling_time = default_rpc_polling_time_; - configs_set = true; } if (qnn::HtpPerformanceMode::kHtpDefault != dynamic_htp_performance_mode_) { // reset perf mode, rpc control latency and rpc polling time to dynamic perf mode values per_thread_htp_power_configs.default_perf_mode = dynamic_htp_performance_mode_; - configs_set = true; } else if (qnn::HtpPerformanceMode::kHtpDefault != default_htp_performance_mode_) { per_thread_htp_power_configs.default_perf_mode = default_htp_performance_mode_; - configs_set = true; } if (qnn::HtpPerformanceMode::kHtpDefault != pre_run_htp_performance_mode) { per_thread_htp_power_configs.pre_run_perf_mode = pre_run_htp_performance_mode; // rpc polling time will only be updated with perf mode changes - configs_set = true; } if (qnn::HtpPerformanceMode::kHtpDefault != post_run_htp_performance_mode) { per_thread_htp_power_configs.post_run_perf_mode = post_run_htp_performance_mode; - configs_set = true; } - - return configs_set; } OrtStatus* ORT_API_CALL QnnEp::OnRunStartImpl(_In_ OrtEp* this_ptr, _In_ const ::OrtRunOptions* run_options) noexcept { @@ -2442,11 +2430,10 @@ OrtStatus* ORT_API_CALL QnnEp::OnRunStartImpl(_In_ OrtEp* this_ptr, _In_ const : if (ep->GetHtpPowerConfigId(htp_power_config_id)) { auto thread_id = std::this_thread::get_id(); qnn::PerThreadHtpPowerConfigs_t per_thread_htp_power_configs; - if (ep->GetPerThreadHtpPowerConfigs(per_thread_htp_power_configs, run_options)) { - per_thread_htp_power_configs.power_config_id = htp_power_config_id; - RETURN_IF_ERROR(ep->qnn_backend_manager_->AddPerThreadHtpPowerConfigMapping(thread_id, - per_thread_htp_power_configs)); - } + ep->GetPerThreadHtpPowerConfigs(per_thread_htp_power_configs, run_options); + per_thread_htp_power_configs.power_config_id = htp_power_config_id; + RETURN_IF_ERROR(ep->qnn_backend_manager_->AddPerThreadHtpPowerConfigMapping(thread_id, + per_thread_htp_power_configs)); } const char* lora_config = nullptr; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.h b/onnxruntime/core/providers/qnn/qnn_execution_provider.h index 6bd38071da..c8cbbe81a1 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.h +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.h @@ -158,7 +158,7 @@ class QnnEp : public OrtEp, public ApiPtrs { } GraphFinalizationInfo_t; // Will return true if any power config options need to be updated - bool GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thread_htp_power_configs, + void GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thread_htp_power_configs, const ::OrtRunOptions* run_options); void CreateHtpPowerConfigId() const; @@ -183,7 +183,6 @@ class QnnEp : public OrtEp, public ApiPtrs { bool disable_cpu_ep_fallback_ = false; // True if CPU EP fallback has been disabled for this session. bool qnn_context_embed_mode_ = true; - bool stop_share_ep_contexts_ = false; bool prepare_only_ = false; bool enable_spill_fill_buffer_ = false; From 0079f47c52e59b23c876a8f78b95c5d643dfa983 Mon Sep 17 00:00:00 2001 From: monumeen Date: Thu, 16 Apr 2026 23:33:34 +0530 Subject: [PATCH 08/36] fix test --- .../core/providers/qnn/builder/qnn_backend_manager.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index fb30f65fc8..90f6469d67 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -361,9 +361,11 @@ void QnnBackendManager::ReleaseTimerThread() { } } } - timer_->DeInitialize(); - timer_callback_arg_.reset(); - timer_.reset(); + if (timer_ != nullptr) { + timer_->DeInitialize(); + timer_callback_arg_.reset(); + timer_.reset(); + } } Ort::Status QnnBackendManager::ParseLoraConfig(std::string lora_config_path) { From f27f082334e184cedacfb89faeac6f1ed42e87ed Mon Sep 17 00:00:00 2001 From: monumeen Date: Fri, 17 Apr 2026 10:18:57 +0530 Subject: [PATCH 09/36] addressing test --- .../core/providers/qnn/qnn_execution_provider.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index f399a938a9..0a4f2f2201 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2070,21 +2070,21 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, if (qnn::IsOrtGraphHasCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } auto status = ep->CompileContextModel(graphs, fused_nodes, count, node_compute_infos); if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } return status; } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } return status; } @@ -2200,11 +2200,11 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, #endif uint32_t htp_power_config_id = 0; if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } auto finalize_status = qnn_model->FinalizeGraphs(ep->logger_); if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } if (!finalize_status.IsOK()) { return finalize_status.release(); @@ -2235,7 +2235,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, tp.Start(); uint32_t htp_power_config_id = 0; if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } finalize_start = std::chrono::steady_clock::now(); for (auto& model_info : model_infos) { @@ -2247,7 +2247,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, end = std::chrono::steady_clock::now(); total_finalize_time = std::chrono::duration_cast(end - finalize_start); if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(onnxruntime::qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); + RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); } for (auto& model_info : model_infos) { RETURN_IF_NOT_OK(std::move(model_info.result)); From 1af97b859e85f355e8d837882741f5986e4a1dde Mon Sep 17 00:00:00 2001 From: monumeen Date: Fri, 17 Apr 2026 14:09:43 +0530 Subject: [PATCH 10/36] addressing test --- .../qnn/builder/qnn_backend_manager.h | 71 +++++++++++++++++++ .../providers/qnn/qnn_execution_provider.cc | 52 +++++++------- 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index bec15e64b9..d02b3e86fd 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -740,5 +740,76 @@ class QnnBackendManager : public std::enable_shared_from_this std::unique_ptr timer_callback_arg_; }; +// RAII guard for QnnBackendManager::SetState. +// +// Calls SetState(start_state, ...) on construction and SetState(done_state, ...) +// on destruction, ensuring the done state is always reached even on early returns. +// +// Typical usage (INIT_START / INIT_DONE pair): +// +// uint32_t htp_power_config_id = 0; +// QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) +// ? ep->qnn_backend_manager_.get() : nullptr; +// ScopedGraphState state_guard(mgr, GraphState::INIT_START, GraphState::INIT_DONE, +// htp_power_config_id, perf_mode, rpc_poll, rpc_latency); +// RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); +// auto status = DoWork(...); +// RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); // optional: capture post-run perf error +// return status; +// +// Passing nullptr as manager creates a no-op guard (all calls succeed immediately). +class ScopedGraphState { + public: + ScopedGraphState(QnnBackendManager* manager, + GraphState start_state, + GraphState done_state, + uint32_t htp_power_config_client_id, + qnn::HtpPerformanceMode perf_mode, + uint32_t rpc_polling_time, + uint32_t rpc_control_latency) + : manager_(manager), + done_state_(done_state), + htp_power_config_client_id_(htp_power_config_client_id), + perf_mode_(perf_mode), + rpc_polling_time_(rpc_polling_time), + rpc_control_latency_(rpc_control_latency), + finalized_(false) { + if (manager_) { + start_status_ = manager_->SetState(start_state, htp_power_config_client_id_, + perf_mode_, rpc_polling_time_, rpc_control_latency_); + } + } + ~ScopedGraphState() { + if (!finalized_ && manager_) { + // Error cannot be propagated from a destructor; silently ignore. + manager_->SetState(done_state_, htp_power_config_client_id_, + perf_mode_, rpc_polling_time_, rpc_control_latency_); + } + } + // Returns (by move) the status of setting HTP performance before work begins. + // Should be checked immediately after construction. + Ort::Status SetPreRunHtpPerfStatus() { return std::move(start_status_); } + // Explicitly sets HTP performance after work is done and returns its status. + // After this call the destructor will not invoke SetState again. + Ort::Status SetPostRunHtpPerf() { + finalized_ = true; + if (manager_) { + return manager_->SetState(done_state_, htp_power_config_client_id_, + perf_mode_, rpc_polling_time_, rpc_control_latency_); + } + return Ort::Status(); + } + ScopedGraphState(const ScopedGraphState&) = delete; + ScopedGraphState& operator=(const ScopedGraphState&) = delete; + private: + QnnBackendManager* manager_; + GraphState done_state_; + uint32_t htp_power_config_client_id_; + qnn::HtpPerformanceMode perf_mode_; + uint32_t rpc_polling_time_; + uint32_t rpc_control_latency_; + Ort::Status start_status_; + bool finalized_; +}; } // namespace qnn } // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 0a4f2f2201..3ca3b64064 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2069,23 +2069,25 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, if (qnn::IsOrtGraphHasCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + qnn::ScopedGraphState state_guard( + ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + htp_power_config_id, ep->default_htp_performance_mode_, + ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileContextModel(graphs, fused_nodes, count, node_compute_infos); - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); return status; } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + qnn::ScopedGraphState state_guard( + ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + htp_power_config_id, ep->default_htp_performance_mode_, + ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); return status; } @@ -2199,13 +2201,14 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, finalize_start = std::chrono::steady_clock::now(); #endif uint32_t htp_power_config_id = 0; - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + qnn::ScopedGraphState state_guard( + ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + htp_power_config_id, ep->default_htp_performance_mode_, + ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); auto finalize_status = qnn_model->FinalizeGraphs(ep->logger_); - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); if (!finalize_status.IsOK()) { return finalize_status.release(); } @@ -2234,9 +2237,12 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, qnn::thread::QnnJobThreadPool tp(ep->num_graph_prepare_threads_); tp.Start(); uint32_t htp_power_config_id = 0; - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_START, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + qnn::ScopedGraphState state_guard( + ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + htp_power_config_id, ep->default_htp_performance_mode_, + ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); finalize_start = std::chrono::steady_clock::now(); for (auto& model_info : model_infos) { tp.SubmitJob([qnn_model = model_info.model.get(), &logger = ep->logger_, res = &model_info.result] { @@ -2246,9 +2252,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, tp.WaitForQueuedJobsToFinish(); end = std::chrono::steady_clock::now(); total_finalize_time = std::chrono::duration_cast(end - finalize_start); - if (ep->GetHtpPowerConfigId(htp_power_config_id)) { - RETURN_IF_NOT_OK(ep->qnn_backend_manager_->SetState(qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_)); - } + RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); for (auto& model_info : model_infos) { RETURN_IF_NOT_OK(std::move(model_info.result)); From 59b1e4244ae16aa9e097fe919a6d3ed038eb862b Mon Sep 17 00:00:00 2001 From: monumeen Date: Fri, 17 Apr 2026 16:18:21 +0530 Subject: [PATCH 11/36] addressing test --- log.txt | Bin 0 -> 841588 bytes .../providers/qnn/qnn_execution_provider.cc | 12 ++++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 log.txt diff --git a/log.txt b/log.txt new file mode 100644 index 0000000000000000000000000000000000000000..7809ee2d2fe59b0dbd76d3f18136c17d16597ad2 GIT binary patch literal 841588 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 E0(Uort_api)) { uint32_t htp_power_config_id = 0; + QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; qnn::ScopedGraphState state_guard( - ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + mgr, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); @@ -2080,8 +2081,9 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, return status; } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; + QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; qnn::ScopedGraphState state_guard( - ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + mgr, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); @@ -2201,8 +2203,9 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, finalize_start = std::chrono::steady_clock::now(); #endif uint32_t htp_power_config_id = 0; + QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; qnn::ScopedGraphState state_guard( - ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + mgr, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); @@ -2237,8 +2240,9 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, qnn::thread::QnnJobThreadPool tp(ep->num_graph_prepare_threads_); tp.Start(); uint32_t htp_power_config_id = 0; + QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; qnn::ScopedGraphState state_guard( - ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr, + mgr, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); From 5bba691a97f6f187eab4a6495a4465cad0d30cee Mon Sep 17 00:00:00 2001 From: monumeen Date: Sat, 18 Apr 2026 22:27:02 +0530 Subject: [PATCH 12/36] fixing build failure --- .../qnn/builder/qnn_backend_manager.h | 9 ++++++--- .../providers/qnn/qnn_execution_provider.cc | 20 +++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index d02b3e86fd..d437dd5809 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -761,6 +761,7 @@ class QnnBackendManager : public std::enable_shared_from_this class ScopedGraphState { public: ScopedGraphState(QnnBackendManager* manager, + bool valid_power_config_id, GraphState start_state, GraphState done_state, uint32_t htp_power_config_client_id, @@ -768,19 +769,20 @@ class ScopedGraphState { uint32_t rpc_polling_time, uint32_t rpc_control_latency) : manager_(manager), + valid_power_config_id_(valid_power_config_id), done_state_(done_state), htp_power_config_client_id_(htp_power_config_client_id), perf_mode_(perf_mode), rpc_polling_time_(rpc_polling_time), rpc_control_latency_(rpc_control_latency), finalized_(false) { - if (manager_) { + if (manager_ && valid_power_config_id_) { start_status_ = manager_->SetState(start_state, htp_power_config_client_id_, perf_mode_, rpc_polling_time_, rpc_control_latency_); } } ~ScopedGraphState() { - if (!finalized_ && manager_) { + if (!finalized_ && manager_ && valid_power_config_id_) { // Error cannot be propagated from a destructor; silently ignore. manager_->SetState(done_state_, htp_power_config_client_id_, perf_mode_, rpc_polling_time_, rpc_control_latency_); @@ -793,7 +795,7 @@ class ScopedGraphState { // After this call the destructor will not invoke SetState again. Ort::Status SetPostRunHtpPerf() { finalized_ = true; - if (manager_) { + if (manager_ && valid_power_config_id_) { return manager_->SetState(done_state_, htp_power_config_client_id_, perf_mode_, rpc_polling_time_, rpc_control_latency_); } @@ -803,6 +805,7 @@ class ScopedGraphState { ScopedGraphState& operator=(const ScopedGraphState&) = delete; private: QnnBackendManager* manager_; + bool valid_power_config_id_; GraphState done_state_; uint32_t htp_power_config_client_id_; qnn::HtpPerformanceMode perf_mode_; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 8aeddcafc8..958a68dca3 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2069,9 +2069,10 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, if (qnn::IsOrtGraphHasCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; - QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; + bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id) ? true : false; qnn::ScopedGraphState state_guard( - mgr, + ep->qnn_backend_manager_.get(), + power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); @@ -2081,9 +2082,10 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, return status; } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; - QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; + bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id) ? true : false; qnn::ScopedGraphState state_guard( - mgr, + ep->qnn_backend_manager_.get(), + power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); @@ -2203,9 +2205,10 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, finalize_start = std::chrono::steady_clock::now(); #endif uint32_t htp_power_config_id = 0; - QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; + bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::ScopedGraphState state_guard( - mgr, + ep->qnn_backend_manager_.get(), + valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); @@ -2240,9 +2243,10 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, qnn::thread::QnnJobThreadPool tp(ep->num_graph_prepare_threads_); tp.Start(); uint32_t htp_power_config_id = 0; - QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) ? ep->qnn_backend_manager_.get() : nullptr; + bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::ScopedGraphState state_guard( - mgr, + ep->qnn_backend_manager_.get(), + valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); From 5e0ed87c2bffa81b62cea0d9d2f4ba3f2ecbba10 Mon Sep 17 00:00:00 2001 From: monumeen Date: Tue, 21 Apr 2026 00:36:51 +0530 Subject: [PATCH 13/36] Refactoring perf changes --- .../qnn/builder/qnn_backend_manager.cc | 43 +-- .../qnn/builder/qnn_backend_manager.h | 88 +++---- .../core/providers/qnn/builder/qnn_def.h | 10 + .../builder/qnn_htp_power_config_manager.cc | 244 +++++++++++++++++- .../builder/qnn_htp_power_config_manager.h | 64 ++++- 5 files changed, 365 insertions(+), 84 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 90f6469d67..43cde3bc3c 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -158,7 +158,7 @@ Ort::Status ReadBinaryFromFile(const std::string& file_path, uint8_t* buffer, si return Ort::Status(); } -bool QnnBackendManager::IsTimerThreadRunning() { +/*bool QnnBackendManager::IsTimerThreadRunning() { std::chrono::microseconds remainUs = std::chrono::microseconds::zero(); uint64_t remaining_duration = 0; if (timer_ && timer_->TimerInUse() && timer_->RemainingDuration(remainUs)) { @@ -166,9 +166,9 @@ bool QnnBackendManager::IsTimerThreadRunning() { return remaining_duration > 0 && remaining_duration < timer_resource_.sustained_timer_duration_; } return false; -} +}*/ -Ort::Status QnnBackendManager::SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, +/*Ort::Status QnnBackendManager::SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { @@ -179,9 +179,9 @@ Ort::Status QnnBackendManager::SetHtpPowerCustomConfigs(uint32_t htp_power_confi RETURN_IF_ERROR(htp_power_config_manager_.SetPowerConfig(htp_power_config_client_id, GetQnnInterface(), *logger_ptr_)); return Ort::Status(); -} +}*/ -Ort::Status QnnBackendManager::SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { +/*Ort::Status QnnBackendManager::SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { std::lock_guard lk(perf_mutex_); Ort::Status status = Ort::Status(); @@ -321,9 +321,9 @@ void QnnBackendManager::TimerCallback(void* user_data) { ORT_CXX_LOG_PTR(instance->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); } } -} +}*/ -void QnnBackendManager::CreateTimerThread(uint32_t htp_power_config_client_id) { +/*void QnnBackendManager::CreateTimerThread(uint32_t htp_power_config_client_id) { std::lock_guard lk(state_mutex_); if (timer_ == nullptr) { std::unique_ptr temp(new Timer()); @@ -348,10 +348,10 @@ void QnnBackendManager::CreateTimerThread(uint32_t htp_power_config_client_id) { } else { ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); } -} +}*/ void QnnBackendManager::ReleaseTimerThread() { - { + /*{ std::lock_guard lk(state_mutex_); if (timer_ != nullptr) { { @@ -365,7 +365,14 @@ void QnnBackendManager::ReleaseTimerThread() { timer_->DeInitialize(); timer_callback_arg_.reset(); timer_.reset(); - } + }*/ + /*{ + std::lock_guard lk(state_mutex_); + if (IsTimerCreated()) { + timer_resource_.timer_active_ = false; + } + }*/ + htp_power_config_manager_.ReleaseTimerThread(); } Ort::Status QnnBackendManager::ParseLoraConfig(std::string lora_config_path) { @@ -544,6 +551,7 @@ Ort::Status QnnBackendManager::LoadBackend() { QNN_API_VERSION_PATCH}, &backend_interface_provider))); qnn_interface_ = backend_interface_provider->QNN_INTERFACE_VER_NAME; + htp_power_config_manager_.Init(qnn_interface_); backend_id_ = backend_interface_provider->backendId; backend_api_version_ = backend_interface_provider->apiVersion.backendApiVersion; SetQnnBackendType(backend_id_); @@ -604,6 +612,7 @@ Ort::Status QnnBackendManager::LoadQnnSerializerBackend() { QNN_API_VERSION_PATCH}, &serializer_interface_provider))); qnn_interface_ = serializer_interface_provider->QNN_INTERFACE_VER_NAME; // NOTE: QnnSaver/Ir will provide the interfaces + htp_power_config_manager_.Init(qnn_interface_); Qnn_Version_t backend_interface_version = GetQnnInterfaceApiVersion(backend_interface_provider); Qnn_Version_t serializer_interface_version = GetQnnInterfaceApiVersion(serializer_interface_provider); @@ -2213,7 +2222,7 @@ Ort::Status QnnBackendManager::SetupBackend( Ort::Status QnnBackendManager::InitializePowerCfgId(uint32_t device_id, uint32_t core_id, uint32_t& htp_power_config_id) { RETURN_IF_ERROR(CreateHtpPowerCfgId(device_id, core_id, htp_power_config_id)); - CreateTimerThread(htp_power_config_id); + htp_power_config_manager_.CreateTimerThread(htp_power_config_id); return Ort::Status(); } @@ -2242,7 +2251,7 @@ Ort::Status QnnBackendManager::CreateHtpPowerCfgId(uint32_t device_id, uint32_t return Ort::Status(); } -Ort::Status QnnBackendManager::SetHtpPowerConfigs(uint32_t htp_power_config_client_id, +/*Ort::Status QnnBackendManager::SetHtpPowerConfigs(uint32_t htp_power_config_client_id, HtpPerformanceMode htp_performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { @@ -2260,7 +2269,7 @@ Ort::Status QnnBackendManager::SetHtpPowerConfigs(uint32_t htp_power_config_clie *logger_ptr_)); return Ort::Status(); -} +}*/ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id& thread_id, bool pre_run) { PerThreadHtpPowerConfigs_t htp_power_configs; @@ -2272,15 +2281,15 @@ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id if (pre_run) { // add in htp_power_configs the default power config id also so to run when we execute if (htp_power_configs.pre_run_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } } else { if (htp_power_configs.post_run_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_ERROR(SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); } } return Ort::Status(); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index d437dd5809..57bdf9cd16 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -126,16 +126,6 @@ struct QnnBackendManagerConfig { bool skip_qnn_version_check; }; -// Graph states to tune the power/performance configurations -enum class GraphState { - INIT_START, - INIT_DONE, - RUN_START, - RUN_DONE, - TIMEOUT, - NONE -}; - class QnnBackendManager : public std::enable_shared_from_this { private: // private tag to pass to constructor to ensure that constructor cannot be directly called externally @@ -165,7 +155,7 @@ class QnnBackendManager : public std::enable_shared_from_this soc_model_(config.soc_model), op_packages_(config.op_packages), skip_qnn_version_check_(config.skip_qnn_version_check), - htp_power_config_manager_(power::HtpPowerConfigManager()), + htp_power_config_manager_(power::HtpPowerConfigManager(logger)), api_ptrs_(api_ptrs), logger_ptr_(&logger) { } @@ -322,7 +312,7 @@ class QnnBackendManager : public std::enable_shared_from_this // Releases all QNN resources. Called in the destructor. // NOTE: This function indirectly locks the internal `logger_recursive_mutex_` via nested function calls. void ReleaseResources(); - Ort::Status SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + // Ort::Status SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); #ifdef QNN_FILE_MAPPED_WEIGHTS_AVAILABLE typedef struct FileMappingCallbackInfo { @@ -338,6 +328,8 @@ class QnnBackendManager : public std::enable_shared_from_this void ResetLogger(const Ort::Logger& logger) { logger_ptr_ = &logger; } + power::HtpPowerConfigManager& GetHtpPowerConfigManager() { return htp_power_config_manager_; } + private: Ort::Status LoadBackend(); @@ -449,26 +441,26 @@ class QnnBackendManager : public std::enable_shared_from_this void* LibFunction(void* handle, const char* symbol, std::string& error_msg); - bool IsTimerThreadRunning(); + /* bool IsTimerThreadRunning(); - Ort::Status SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); - Ort::Status SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); - static void TimerCallback(void* user_data); + static void TimerCallback(void* user_data);*/ Ort::Status CreateHtpPowerCfgId(uint32_t deviceId, uint32_t coreId, uint32_t& htp_power_config_id); - void CreateTimerThread(uint32_t htp_power_config_client_id); + // void CreateTimerThread(uint32_t htp_power_config_client_id); Ort::Status DestroyHTPPowerConfigID(uint32_t htp_power_config_id); - Ort::Status SetHtpPowerConfigs(uint32_t htp_power_config_client_id, - HtpPerformanceMode htp_performance_mode, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency); + /* Ort::Status SetHtpPowerConfigs(uint32_t htp_power_config_client_id, + HtpPerformanceMode htp_performance_mode, + uint32_t rpc_polling_time, + uint32_t rpc_control_latency); - Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency);*/ template inline T ResolveSymbol(void* lib_handle, const char* sym, const Ort::Logger& logger) { @@ -720,24 +712,24 @@ class QnnBackendManager : public std::enable_shared_from_this const Ort::Logger* logger_ptr_; std::shared_ptr rpcmem_library_ = nullptr; - std::mutex perf_mutex_; - std::mutex state_mutex_; - std::unique_ptr timer_; - struct TimerResource { - static constexpr uint64_t sustained_timer_duration_ = kDefaultTimerTimeoutUs; // in microseconds - std::atomic caller_busy_ = false; - std::atomic timer_active_ = false; - }; - TimerResource timer_resource_; - std::atomic graph_state_ = GraphState::NONE; - struct TimerCallbackArg { - uint32_t power_config_id_; - QnnBackendManager* instance_; - - TimerCallbackArg(uint32_t id, QnnBackendManager* manager) - : power_config_id_(id), instance_(manager) {} - }; - std::unique_ptr timer_callback_arg_; + /* std::mutex perf_mutex_; + std::mutex state_mutex_; + std::unique_ptr timer_; + struct TimerResource { + static constexpr uint64_t sustained_timer_duration_ = kDefaultTimerTimeoutUs; // in microseconds + std::atomic caller_busy_ = false; + std::atomic timer_active_ = false; + }; + TimerResource timer_resource_; + std::atomic graph_state_ = GraphState::NONE; + struct TimerCallbackArg { + uint32_t power_config_id_; + QnnBackendManager* instance_; + + TimerCallbackArg(uint32_t id, QnnBackendManager* manager) + : power_config_id_(id), instance_(manager) {} + }; + std::unique_ptr timer_callback_arg_;*/ }; // RAII guard for QnnBackendManager::SetState. @@ -777,15 +769,17 @@ class ScopedGraphState { rpc_control_latency_(rpc_control_latency), finalized_(false) { if (manager_ && valid_power_config_id_) { - start_status_ = manager_->SetState(start_state, htp_power_config_client_id_, - perf_mode_, rpc_polling_time_, rpc_control_latency_); + power::HtpPowerConfigManager& htp_power_config_manager = manager_->GetHtpPowerConfigManager(); + start_status_ = htp_power_config_manager.SetState(start_state, htp_power_config_client_id_, + perf_mode_, rpc_polling_time_, rpc_control_latency_); } } ~ScopedGraphState() { if (!finalized_ && manager_ && valid_power_config_id_) { // Error cannot be propagated from a destructor; silently ignore. - manager_->SetState(done_state_, htp_power_config_client_id_, - perf_mode_, rpc_polling_time_, rpc_control_latency_); + power::HtpPowerConfigManager& htp_power_config_manager = manager_->GetHtpPowerConfigManager(); + htp_power_config_manager.SetState(done_state_, htp_power_config_client_id_, + perf_mode_, rpc_polling_time_, rpc_control_latency_); } } // Returns (by move) the status of setting HTP performance before work begins. @@ -796,13 +790,15 @@ class ScopedGraphState { Ort::Status SetPostRunHtpPerf() { finalized_ = true; if (manager_ && valid_power_config_id_) { - return manager_->SetState(done_state_, htp_power_config_client_id_, - perf_mode_, rpc_polling_time_, rpc_control_latency_); + power::HtpPowerConfigManager& htp_power_config_manager = manager_->GetHtpPowerConfigManager(); + return htp_power_config_manager.SetState(done_state_, htp_power_config_client_id_, + perf_mode_, rpc_polling_time_, rpc_control_latency_); } return Ort::Status(); } ScopedGraphState(const ScopedGraphState&) = delete; ScopedGraphState& operator=(const ScopedGraphState&) = delete; + private: QnnBackendManager* manager_; bool valid_power_config_id_; diff --git a/onnxruntime/core/providers/qnn/builder/qnn_def.h b/onnxruntime/core/providers/qnn/builder/qnn_def.h index 12b9c9e3de..8e7abe62ae 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_def.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_def.h @@ -91,6 +91,16 @@ enum class DcvsState { DCVS_NUM_STATES }; +// Graph states to tune the power/performance configurations +enum class GraphState { + INIT_START, + INIT_DONE, + RUN_START, + RUN_DONE, + TIMEOUT, + NONE +}; + typedef struct PerThreadHtpPowerConfigs { std::optional pre_run_perf_mode; std::optional post_run_perf_mode; diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index 523296e418..03c660109a 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -14,14 +14,14 @@ namespace onnxruntime { namespace qnn { namespace power { -HtpPowerConfigManager::HtpPowerConfigManager() { +HtpPowerConfigManager::HtpPowerConfigManager(const Ort::Logger logger) : logger_(logger) { constexpr int kMaxNumConfigs = 3; power_configs_.reserve(kMaxNumConfigs); } HtpPowerConfigManager::~HtpPowerConfigManager() {} -Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time, const Ort::Logger& logger) { +Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time) { RETURN_IF(rpc_polling_time > kMaxRpcPolling, ("Cannot set RPC polling time to " + std::to_string(rpc_polling_time) + ". Max allowable RPC polling time is: " + std::to_string(kMaxRpcPolling)) @@ -30,11 +30,11 @@ Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time, RETURN_IF(rpc_polling_time_set_, "There is already a pending RPC polling time config"); if (rpc_polling_time == last_set_rpc_polling_time_) { - ORT_CXX_LOG(logger, + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, ("Requested rpc polling time is the same as last set (" + std::to_string(last_set_rpc_polling_time_) + "). Ignoring request").c_str()); } else { - ORT_CXX_LOG(logger, + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, ("Updating rpc polling time to: " + std::to_string(rpc_polling_time) + "us.").c_str()); auto& rpc_polling_time_cfg = power_configs_.emplace_back(); @@ -47,16 +47,16 @@ Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time, return Ort::Status(); } -Ort::Status HtpPowerConfigManager::AddRpcControlLatency(uint32_t rpc_control_latency, const Ort::Logger& logger) { +Ort::Status HtpPowerConfigManager::AddRpcControlLatency(uint32_t rpc_control_latency) { RETURN_IF(rpc_control_latency_set_, "There is already a pending RPC control latency config"); if (rpc_control_latency == last_set_rpc_control_latency_) { - ORT_CXX_LOG(logger, + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, ("Requested rpc control latency is the same as last set (" + std::to_string(last_set_rpc_control_latency_) + "). Ignoring request") .c_str()); } else { - ORT_CXX_LOG(logger, + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, ("Updating rpc control latency to: " + std::to_string(rpc_control_latency) + "us.").c_str()); auto& rpc_control_latency_cfg = power_configs_.emplace_back(); @@ -102,18 +102,17 @@ Ort::Status HtpPowerConfigManager::AddHtpPerformanceConfig(QnnHtpPerfInfrastruct } Ort::Status HtpPowerConfigManager::AddHtpPerformanceMode(HtpPerformanceMode htp_performance_mode, - uint32_t htp_power_config_client_id, - const Ort::Logger& logger) { + uint32_t htp_power_config_client_id) { RETURN_IF(htp_performance_mode_set_, "There is already a pending HTP performance mode config"); if (htp_performance_mode == last_set_htp_performance_mode_) { - ORT_CXX_LOG(logger, + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, ("Requested htp performance mode is the same as last set (" + std::string(PerformanceModeToString(last_set_htp_performance_mode_)) + "). Ignoring request") .c_str()); } else { - ORT_CXX_LOG(logger, + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, ("Updating htp performance mode to: " + std::string(PerformanceModeToString(htp_performance_mode)) + ".") @@ -134,8 +133,7 @@ Ort::Status HtpPowerConfigManager::AddHtpPerformanceMode(HtpPerformanceMode htp_ } Ort::Status HtpPowerConfigManager::SetPowerConfig(uint32_t htp_power_config_client_id, - const QNN_INTERFACE_VER_TYPE& qnn_interface, - const Ort::Logger& logger) { + const QNN_INTERFACE_VER_TYPE& qnn_interface) { if (!power_configs_.empty()) { QnnDevice_Infrastructure_t qnn_device_infra = nullptr; auto status = qnn_interface.deviceGetInfrastructure(&qnn_device_infra); @@ -161,7 +159,7 @@ Ort::Status HtpPowerConfigManager::SetPowerConfig(uint32_t htp_power_config_clie htp_performance_mode_set_ = false; power_configs_.clear(); } else { - ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "SetPowerConfig called but no configs to be set."); + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "SetPowerConfig called but no configs to be set."); } return Ort::Status(); @@ -367,6 +365,224 @@ void HtpPowerConfigManager::SetReleasedPerfPowerConfig(QnnHtpPerfInfrastructure_ dcvs_v3.setCoreParams = 1; } +void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_id) { + std::lock_guard lk(state_mutex_); + if (timer_ == nullptr) { + std::unique_ptr temp(new Timer()); + if (temp != nullptr) { + timer_ = std::move(temp); + timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this); + if (timer_callback_arg_ == nullptr) { + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create Timer argument"); + timer_.reset(); + return; + } + if (!timer_->Initialize(TimerCallback, timer_callback_arg_.get())) { + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); + timer_callback_arg_.reset(); + timer_.reset(); + } else { + timer_resource_.timer_active_ = true; + } + } else { + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); + } + } else { + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); + } +} + +void HtpPowerConfigManager::ReleaseTimerThread() { + if (timer_ != nullptr) { + timer_->DeInitialize(); + timer_callback_arg_.reset(); + timer_.reset(); + } +} + +Ort::Status HtpPowerConfigManager::SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { + std::lock_guard lk(perf_mutex_); + Ort::Status status = Ort::Status(); + + std::chrono::microseconds sustainedDurationUs(timer_resource_.sustained_timer_duration_); + + switch (graph_state_) { + case GraphState::RUN_DONE: + if (IsTimerThreadRunning()) { + timer_->AbortTimer(); + } + RETURN_IF_NOT(timer_->Launch(sustainedDurationUs), "Not able to launch timer thread."); + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + break; + case GraphState::RUN_START: + if (IsTimerThreadRunning()) { + timer_->AbortTimer(); + } else { + status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + } + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = true; + break; + case GraphState::INIT_DONE: { + QnnHtpPerfInfrastructure_PowerConfig_t init_done_htp_performance_cfg{}; + SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, init_done_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + break; + } + case GraphState::INIT_START: + if (IsTimerThreadRunning()) { + timer_->AbortTimer(); + } else { + status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + } + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = true; + break; + case GraphState::TIMEOUT: { + if (!timer_resource_.caller_busy_) { + QnnHtpPerfInfrastructure_PowerConfig_t timeout_htp_performance_cfg{}; + SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, timeout_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + graph_state_ = GraphState::NONE; + } + break; + } + default: + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + break; + } + return status; +} + +Ort::Status HtpPowerConfigManager::SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { + std::lock_guard lk(perf_mutex_); + Ort::Status status = Ort::Status(); + switch (graph_state_) { + case GraphState::RUN_DONE: + case GraphState::INIT_DONE: + switch (performance_mode) { + case qnn::HtpPerformanceMode::kHtpLowBalanced: + case qnn::HtpPerformanceMode::kHtpBalanced: + case qnn::HtpPerformanceMode::kHtpHighPerformance: { + QnnHtpPerfInfrastructure_PowerConfig_t relaxed_htp_performance_cfg{}; + SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, relaxed_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + break; + } + case qnn::HtpPerformanceMode::kHtpExtremePowerSaver: { + QnnHtpPerfInfrastructure_PowerConfig_t extreme_power_saver_htp_performance_cfg{}; + SetExtremeLowPerfPowerConfig(extreme_power_saver_htp_performance_cfg, htp_power_config_client_id); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, extreme_power_saver_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + break; + } + case qnn::HtpPerformanceMode::kHtpLowPowerSaver: + case qnn::HtpPerformanceMode::kHtpHighPowerSaver: + case qnn::HtpPerformanceMode::kHtpPowerSaver: { + QnnHtpPerfInfrastructure_PowerConfig_t released_htp_performance_cfg{}; + SetReleasedPerfPowerConfig(released_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(htp_power_config_client_id, released_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + break; + } + default: + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); + break; + } + graph_state_ = GraphState::NONE; + break; + case GraphState::RUN_START: + case GraphState::INIT_START: + status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + graph_state_ = GraphState::NONE; + break; + default: + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + break; + } + return status; +} + +Ort::Status HtpPowerConfigManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { + { + std::lock_guard lk(state_mutex_); + if (state != graph_state_) { + graph_state_ = state; + } else { + ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); + return Ort::Status(); + } + } + if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { + RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); + RETURN_IF(timer_ == nullptr, "timer is not started"); + return SetSustainedPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + } else if (perfMode == qnn::HtpPerformanceMode::kHtpDefault) { + if (timer_ && timer_->TimerInUse()) { + timer_->AbortTimer(); + } + return Ort::Status(); + } else { + if (timer_ && timer_->TimerInUse()) { + timer_->AbortTimer(); + } + return SetPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + } +} + +void HtpPowerConfigManager::TimerCallback(void* user_data) { + TimerCallbackArg* args = static_cast(user_data); + HtpPowerConfigManager* instance = args->instance_; + if (instance->timer_resource_.timer_active_) { + auto rt = instance->SetState(GraphState::TIMEOUT, args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0); + if (!rt.IsOK()) { + ORT_CXX_LOG(instance->logger_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); + } + } +} + +bool HtpPowerConfigManager::IsTimerThreadRunning() { + std::chrono::microseconds remainUs = std::chrono::microseconds::zero(); + uint64_t remaining_duration = 0; + if (timer_ && timer_->TimerInUse() && timer_->RemainingDuration(remainUs)) { + remaining_duration = static_cast(remainUs.count()); + return remaining_duration > 0 && remaining_duration < timer_resource_.sustained_timer_duration_; + } + return false; +} + +Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(uint32_t htp_power_config_client_id, + HtpPerformanceMode htp_performance_mode, + uint32_t rpc_polling_time, + uint32_t rpc_control_latency) { + // This function is called in QNN EP's OnRunStart() even if QNN backend setup failed and the model is assigned + // to a different EP. Therefore, we have to check that backend setup actually completed before trying to + // set an HTP power config ID. Otherwise, this causes a segfault because the QNN backend lib is unloaded. + // RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete."); + RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time)); + RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency)); + RETURN_IF_ERROR(AddHtpPerformanceMode(htp_performance_mode, + htp_power_config_client_id)); + RETURN_IF_ERROR(SetPowerConfig(htp_power_config_client_id, + *qnn_interface_)); + + return Ort::Status(); +} + +Ort::Status HtpPowerConfigManager::SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, + QnnHtpPerfInfrastructure_PowerConfig_t power_config, + uint32_t rpc_polling_time, + uint32_t rpc_control_latency) { + // RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete."); + RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time)); + RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency)); + RETURN_IF_ERROR(AddHtpPerformanceConfig(power_config)); + RETURN_IF_ERROR(SetPowerConfig(htp_power_config_client_id, *qnn_interface_)); + + return Ort::Status(); +} + } // namespace power } // namespace qnn } // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 177bf4fd9e..9e6e89b6fd 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -11,6 +11,7 @@ #include "core/providers/qnn/builder/qnn_def.h" #include "core/providers/qnn/ort_api.h" +#include "core/providers/qnn/builder/timer.h" namespace onnxruntime { namespace qnn { @@ -20,25 +21,24 @@ namespace power { // updates power configurations for the HTP backend class HtpPowerConfigManager { public: - HtpPowerConfigManager(); + HtpPowerConfigManager(const Ort::Logger logger); ~HtpPowerConfigManager(); // Stages a new rpc polling time for next power config update // If the value is the same as the last previously set, then // there will be no new rpc polling time staged - Ort::Status AddRpcPollingTime(uint32_t rpc_polling_time, const Ort::Logger& logger); + Ort::Status AddRpcPollingTime(uint32_t rpc_polling_time); // Stages a new rpc control latency for next power config update // If the value is the same as the last previously set, then // there will be no new rpc control latency staged - Ort::Status AddRpcControlLatency(uint32_t rpc_control_latency, const Ort::Logger& logger); + Ort::Status AddRpcControlLatency(uint32_t rpc_control_latency); // Stages a new performance mode for next power config update // If the value is the same as the last previously set, then // there will be no new performance mode staged Ort::Status AddHtpPerformanceMode(HtpPerformanceMode htp_performance_mode, - uint32_t htp_power_config_client_id, - const Ort::Logger& logger); + uint32_t htp_power_config_client_id); // Stages a new HTP power configuration for next power config update // performance mode is set to default after setting the power config @@ -48,8 +48,7 @@ class HtpPowerConfigManager { // the HTP power configurations. If there is nothing staged, // then no attempt will be made. Ort::Status SetPowerConfig(uint32_t htp_power_config_client_id, - const QNN_INTERFACE_VER_TYPE& qnn_interface, - const Ort::Logger& logger); + const QNN_INTERFACE_VER_TYPE& qnn_interface); // Sets power config for relaxed performance mode based on DCVS state void SetRelaxedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, @@ -62,12 +61,42 @@ class HtpPowerConfigManager { // Sets power config for extreme low performance mode void SetExtremeLowPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id); + void CreateTimerThread(uint32_t htp_power_config_client_id); + + void ReleaseTimerThread(); + + bool IsTimerCreated() { + if (timer_ != nullptr) { + return true; + } + return false; + } + + Ort::Status SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + + void Init(const QNN_INTERFACE_VER_TYPE& qnn_interface) { qnn_interface_ = &qnn_interface; } + private: // Sets voltage corner votes for HTP based on the given performance mode Ort::Status SetHtpPerformancePowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, const HtpPerformanceMode& htp_performance_mode); + Ort::Status SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + + Ort::Status SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + + static void TimerCallback(void* user_data); + + bool IsTimerThreadRunning(); + + Ort::Status SetHtpPowerConfigs(uint32_t htp_power_config_client_id, + HtpPerformanceMode htp_performance_mode, + uint32_t rpc_polling_time, + uint32_t rpc_control_latency); + + Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + uint32_t last_set_rpc_polling_time_ = kDisableRpcPolling; uint32_t last_set_rpc_control_latency_ = kDisableRpcControlLatency; HtpPerformanceMode last_set_htp_performance_mode_ = HtpPerformanceMode::kHtpDefault; @@ -77,6 +106,27 @@ class HtpPowerConfigManager { bool htp_performance_mode_set_ = false; std::vector power_configs_; + + const Ort::Logger logger_; + const QNN_INTERFACE_VER_TYPE* qnn_interface_ = nullptr; + + std::mutex perf_mutex_; + std::mutex state_mutex_; + std::unique_ptr timer_; + struct TimerResource { + static constexpr uint64_t sustained_timer_duration_ = kDefaultTimerTimeoutUs; // in microseconds + std::atomic caller_busy_ = false; + std::atomic timer_active_ = false; + }; + TimerResource timer_resource_; + std::atomic graph_state_ = GraphState::NONE; + struct TimerCallbackArg { + uint32_t power_config_id_; + HtpPowerConfigManager* instance_; + TimerCallbackArg(uint32_t id, HtpPowerConfigManager* manager) + : power_config_id_(id), instance_(manager) {} + }; + std::unique_ptr timer_callback_arg_; }; } // namespace power } // namespace qnn From 8558120a06d59c97cac17f251ff241724c5e501b Mon Sep 17 00:00:00 2001 From: monumeen Date: Tue, 21 Apr 2026 01:42:06 +0530 Subject: [PATCH 14/36] fix fail test --- .../qnn/builder/qnn_backend_manager.cc | 21 ------------------- .../builder/qnn_htp_power_config_manager.cc | 10 +++++++++ 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 43cde3bc3c..80cdf338b2 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -351,27 +351,6 @@ void QnnBackendManager::TimerCallback(void* user_data) { }*/ void QnnBackendManager::ReleaseTimerThread() { - /*{ - std::lock_guard lk(state_mutex_); - if (timer_ != nullptr) { - { - timer_resource_.timer_active_ = false; - graph_state_ = GraphState::NONE; - timer_resource_.caller_busy_ = false; - } - } - } - if (timer_ != nullptr) { - timer_->DeInitialize(); - timer_callback_arg_.reset(); - timer_.reset(); - }*/ - /*{ - std::lock_guard lk(state_mutex_); - if (IsTimerCreated()) { - timer_resource_.timer_active_ = false; - } - }*/ htp_power_config_manager_.ReleaseTimerThread(); } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index 03c660109a..2ed0c51ad7 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -393,6 +393,16 @@ void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_i } void HtpPowerConfigManager::ReleaseTimerThread() { + { + std::lock_guard lk(state_mutex_); + if (timer_ != nullptr) { + { + timer_resource_.timer_active_ = false; + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + } + } + } if (timer_ != nullptr) { timer_->DeInitialize(); timer_callback_arg_.reset(); From 345627f5ab6182605eb15dcf1ef9951d7dbd185b Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 22 Apr 2026 11:33:38 +0530 Subject: [PATCH 15/36] removing redundant code --- .../qnn/builder/qnn_backend_manager.cc | 212 ------------------ .../qnn/builder/qnn_backend_manager.h | 18 -- 2 files changed, 230 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 80cdf338b2..a1a44ed8df 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -158,198 +158,6 @@ Ort::Status ReadBinaryFromFile(const std::string& file_path, uint8_t* buffer, si return Ort::Status(); } -/*bool QnnBackendManager::IsTimerThreadRunning() { - std::chrono::microseconds remainUs = std::chrono::microseconds::zero(); - uint64_t remaining_duration = 0; - if (timer_ && timer_->TimerInUse() && timer_->RemainingDuration(remainUs)) { - remaining_duration = static_cast(remainUs.count()); - return remaining_duration > 0 && remaining_duration < timer_resource_.sustained_timer_duration_; - } - return false; -}*/ - -/*Ort::Status QnnBackendManager::SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, - QnnHtpPerfInfrastructure_PowerConfig_t power_config, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency) { - RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete."); - RETURN_IF_ERROR(htp_power_config_manager_.AddRpcPollingTime(rpc_polling_time, *logger_ptr_)); - RETURN_IF_ERROR(htp_power_config_manager_.AddRpcControlLatency(rpc_control_latency, *logger_ptr_)); - RETURN_IF_ERROR(htp_power_config_manager_.AddHtpPerformanceConfig(power_config)); - RETURN_IF_ERROR(htp_power_config_manager_.SetPowerConfig(htp_power_config_client_id, GetQnnInterface(), *logger_ptr_)); - - return Ort::Status(); -}*/ - -/*Ort::Status QnnBackendManager::SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { - std::lock_guard lk(perf_mutex_); - Ort::Status status = Ort::Status(); - - std::chrono::microseconds sustainedDurationUs(timer_resource_.sustained_timer_duration_); - - switch (graph_state_) { - case GraphState::RUN_DONE: - if (IsTimerThreadRunning()) { - timer_->AbortTimer(); - } - RETURN_IF_NOT(timer_->Launch(sustainedDurationUs), "Not able to launch timer thread."); - graph_state_ = GraphState::NONE; - timer_resource_.caller_busy_ = false; - break; - case GraphState::RUN_START: - if (IsTimerThreadRunning()) { - timer_->AbortTimer(); - } else { - status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); - } - graph_state_ = GraphState::NONE; - timer_resource_.caller_busy_ = true; - break; - case GraphState::INIT_DONE: { - QnnHtpPerfInfrastructure_PowerConfig_t init_done_htp_performance_cfg{}; - htp_power_config_manager_.SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, init_done_htp_performance_cfg, rpc_polling_time, rpc_control_latency); - graph_state_ = GraphState::NONE; - timer_resource_.caller_busy_ = false; - break; - } - case GraphState::INIT_START: - if (IsTimerThreadRunning()) { - timer_->AbortTimer(); - } else { - status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); - } - graph_state_ = GraphState::NONE; - timer_resource_.caller_busy_ = true; - break; - case GraphState::TIMEOUT: { - if (!timer_resource_.caller_busy_) { - QnnHtpPerfInfrastructure_PowerConfig_t timeout_htp_performance_cfg{}; - htp_power_config_manager_.SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, timeout_htp_performance_cfg, rpc_polling_time, rpc_control_latency); - graph_state_ = GraphState::NONE; - } - break; - } - default: - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); - break; - } - return status; -} - -Ort::Status QnnBackendManager::SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { - std::lock_guard lk(perf_mutex_); - Ort::Status status = Ort::Status(); - switch (graph_state_) { - case GraphState::RUN_DONE: - case GraphState::INIT_DONE: - switch (performance_mode) { - case qnn::HtpPerformanceMode::kHtpLowBalanced: - case qnn::HtpPerformanceMode::kHtpBalanced: - case qnn::HtpPerformanceMode::kHtpHighPerformance: { - QnnHtpPerfInfrastructure_PowerConfig_t relaxed_htp_performance_cfg{}; - htp_power_config_manager_.SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, relaxed_htp_performance_cfg, rpc_polling_time, rpc_control_latency); - break; - } - case qnn::HtpPerformanceMode::kHtpExtremePowerSaver: { - QnnHtpPerfInfrastructure_PowerConfig_t extreme_power_saver_htp_performance_cfg{}; - htp_power_config_manager_.SetExtremeLowPerfPowerConfig(extreme_power_saver_htp_performance_cfg, htp_power_config_client_id); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, extreme_power_saver_htp_performance_cfg, rpc_polling_time, rpc_control_latency); - break; - } - case qnn::HtpPerformanceMode::kHtpLowPowerSaver: - case qnn::HtpPerformanceMode::kHtpHighPowerSaver: - case qnn::HtpPerformanceMode::kHtpPowerSaver: { - QnnHtpPerfInfrastructure_PowerConfig_t released_htp_performance_cfg{}; - htp_power_config_manager_.SetReleasedPerfPowerConfig(released_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, released_htp_performance_cfg, rpc_polling_time, rpc_control_latency); - break; - } - default: - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); - break; - } - graph_state_ = GraphState::NONE; - break; - case GraphState::RUN_START: - case GraphState::INIT_START: - status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); - graph_state_ = GraphState::NONE; - break; - default: - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); - break; - } - return status; -} - -Ort::Status QnnBackendManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { - { - std::lock_guard lk(state_mutex_); - if (state != graph_state_) { - graph_state_ = state; - } else { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); - return Ort::Status(); - } - } - if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { - RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); - RETURN_IF(timer_ == nullptr, "timer is not started"); - return SetSustainedPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); - } else if (perfMode == qnn::HtpPerformanceMode::kHtpDefault) { - if (timer_ && timer_->TimerInUse()) { - timer_->AbortTimer(); - } - return Ort::Status(); - } else { - if (timer_ && timer_->TimerInUse()) { - timer_->AbortTimer(); - } - return SetPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); - } -} - -void QnnBackendManager::TimerCallback(void* user_data) { - TimerCallbackArg* args = static_cast(user_data); - QnnBackendManager* instance = args->instance_; - if (instance->timer_resource_.timer_active_) { - auto rt = instance->SetState(GraphState::TIMEOUT, args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0); - if (!rt.IsOK()) { - ORT_CXX_LOG_PTR(instance->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); - } - } -}*/ - -/*void QnnBackendManager::CreateTimerThread(uint32_t htp_power_config_client_id) { - std::lock_guard lk(state_mutex_); - if (timer_ == nullptr) { - std::unique_ptr temp(new Timer()); - if (temp != nullptr) { - timer_ = std::move(temp); - timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this); - if (timer_callback_arg_ == nullptr) { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create Timer argument"); - timer_.reset(); - return; - } - if (!timer_->Initialize(TimerCallback, timer_callback_arg_.get())) { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); - timer_callback_arg_.reset(); - timer_.reset(); - } else { - timer_resource_.timer_active_ = true; - } - } else { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); - } - } else { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); - } -}*/ - void QnnBackendManager::ReleaseTimerThread() { htp_power_config_manager_.ReleaseTimerThread(); } @@ -2230,26 +2038,6 @@ Ort::Status QnnBackendManager::CreateHtpPowerCfgId(uint32_t device_id, uint32_t return Ort::Status(); } -/*Ort::Status QnnBackendManager::SetHtpPowerConfigs(uint32_t htp_power_config_client_id, - HtpPerformanceMode htp_performance_mode, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency) { - // This function is called in QNN EP's OnRunStart() even if QNN backend setup failed and the model is assigned - // to a different EP. Therefore, we have to check that backend setup actually completed before trying to - // set an HTP power config ID. Otherwise, this causes a segfault because the QNN backend lib is unloaded. - RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete."); - RETURN_IF_ERROR(htp_power_config_manager_.AddRpcPollingTime(rpc_polling_time, *logger_ptr_)); - RETURN_IF_ERROR(htp_power_config_manager_.AddRpcControlLatency(rpc_control_latency, *logger_ptr_)); - RETURN_IF_ERROR(htp_power_config_manager_.AddHtpPerformanceMode(htp_performance_mode, - htp_power_config_client_id, - *logger_ptr_)); - RETURN_IF_ERROR(htp_power_config_manager_.SetPowerConfig(htp_power_config_client_id, - GetQnnInterface(), - *logger_ptr_)); - - return Ort::Status(); -}*/ - Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id& thread_id, bool pre_run) { PerThreadHtpPowerConfigs_t htp_power_configs; if (!GetPerThreadHtpPowerConfigMapping(thread_id, htp_power_configs)) { diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index 57bdf9cd16..bc159102a4 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -712,24 +712,6 @@ class QnnBackendManager : public std::enable_shared_from_this const Ort::Logger* logger_ptr_; std::shared_ptr rpcmem_library_ = nullptr; - /* std::mutex perf_mutex_; - std::mutex state_mutex_; - std::unique_ptr timer_; - struct TimerResource { - static constexpr uint64_t sustained_timer_duration_ = kDefaultTimerTimeoutUs; // in microseconds - std::atomic caller_busy_ = false; - std::atomic timer_active_ = false; - }; - TimerResource timer_resource_; - std::atomic graph_state_ = GraphState::NONE; - struct TimerCallbackArg { - uint32_t power_config_id_; - QnnBackendManager* instance_; - - TimerCallbackArg(uint32_t id, QnnBackendManager* manager) - : power_config_id_(id), instance_(manager) {} - }; - std::unique_ptr timer_callback_arg_;*/ }; // RAII guard for QnnBackendManager::SetState. From be289fe3c53d9cfc9f7a76fa79d5747a6dfeda3f Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 22 Apr 2026 11:59:46 +0530 Subject: [PATCH 16/36] refactoring code --- log.txt | Bin 841588 -> 0 bytes .../qnn/builder/qnn_backend_manager.cc | 9 ++----- .../qnn/builder/qnn_backend_manager.h | 23 ++---------------- .../providers/qnn/qnn_execution_provider.cc | 4 +-- 4 files changed, 6 insertions(+), 30 deletions(-) delete mode 100644 log.txt diff --git a/log.txt b/log.txt deleted file mode 100644 index 7809ee2d2fe59b0dbd76d3f18136c17d16597ad2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841588 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 E0(U Ort::Status InitializePowerCfgId(uint32_t deviceId, uint32_t coreId, uint32_t& htp_power_config_id); - Ort::Status DeInitializePowerCfgId(uint32_t htp_power_config_id); + void DeInitializePerfTimer(); - void ReleaseTimerThread(); + Ort::Status DestroyHTPPowerConfigID(uint32_t htp_power_config_id); Ort::Status SetPerThreadHtpPowerConfigs(const std::thread::id& thread_id, bool pre_run); @@ -441,27 +441,8 @@ class QnnBackendManager : public std::enable_shared_from_this void* LibFunction(void* handle, const char* symbol, std::string& error_msg); - /* bool IsTimerThreadRunning(); - - Ort::Status SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); - - Ort::Status SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); - - static void TimerCallback(void* user_data);*/ - Ort::Status CreateHtpPowerCfgId(uint32_t deviceId, uint32_t coreId, uint32_t& htp_power_config_id); - // void CreateTimerThread(uint32_t htp_power_config_client_id); - - Ort::Status DestroyHTPPowerConfigID(uint32_t htp_power_config_id); - - /* Ort::Status SetHtpPowerConfigs(uint32_t htp_power_config_client_id, - HtpPerformanceMode htp_performance_mode, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency); - - Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency);*/ - template inline T ResolveSymbol(void* lib_handle, const char* sym, const Ort::Logger& logger) { std::string error_msg = ""; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 958a68dca3..fadf7040f4 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -1103,10 +1103,10 @@ QnnEp::~QnnEp() { if (qnn_backend_manager_) { auto thread_id = std::this_thread::get_id(); qnn_backend_manager_->RemovePerThreadHtpPowerConfigMapping(thread_id); - qnn_backend_manager_->ReleaseTimerThread(); + qnn_backend_manager_->DeInitializePerfTimer(); std::lock_guard lock(config_id_mutex_); if (htp_power_config_id_.has_value()) { - qnn_backend_manager_->DeInitializePowerCfgId(*htp_power_config_id_); + qnn_backend_manager_->DestroyHTPPowerConfigID(*htp_power_config_id_); } } From 78e777b6cc0547712aa4df2f871fa2c77f4e71bc Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 22 Apr 2026 12:22:07 +0530 Subject: [PATCH 17/36] refactoring code --- onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index e1e2f3fe2f..39db081e1e 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -37,8 +37,6 @@ #include "core/providers/qnn/builder/qnn_node_group/qnn_node_group.h" #include "core/providers/qnn/builder/qnn_profile_serializer.h" #include "core/providers/qnn/ort_api.h" -#include "core/providers/qnn/builder/timer.h" -#include #ifdef QNN_FILE_MAPPED_WEIGHTS_AVAILABLE #include "core/providers/qnn/builder/qnn_file_mapping_interface.h" @@ -312,7 +310,6 @@ class QnnBackendManager : public std::enable_shared_from_this // Releases all QNN resources. Called in the destructor. // NOTE: This function indirectly locks the internal `logger_recursive_mutex_` via nested function calls. void ReleaseResources(); - // Ort::Status SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); #ifdef QNN_FILE_MAPPED_WEIGHTS_AVAILABLE typedef struct FileMappingCallbackInfo { From 6c8a70746c5736761f9477bea30e7fd1e35b77ad Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 22 Apr 2026 17:07:20 +0530 Subject: [PATCH 18/36] addressing comments --- .../qnn/builder/qnn_backend_manager.h | 5 +- .../builder/qnn_htp_power_config_manager.cc | 84 +++++++++---------- .../builder/qnn_htp_power_config_manager.h | 14 ++-- .../core/providers/qnn/builder/timer.cc | 8 ++ .../core/providers/qnn/builder/timer.h | 8 ++ .../providers/qnn/qnn_execution_provider.cc | 20 ++--- .../providers/qnn/qnn_execution_provider.h | 1 + 7 files changed, 77 insertions(+), 63 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index 39db081e1e..83f071adc6 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -323,7 +323,10 @@ class QnnBackendManager : public std::enable_shared_from_this } FileMappingCallbackInfo_t; #endif - void ResetLogger(const Ort::Logger& logger) { logger_ptr_ = &logger; } + void ResetLogger(const Ort::Logger& logger) { + logger_ptr_ = &logger; + htp_power_config_manager_.ResetLogger(logger); + } power::HtpPowerConfigManager& GetHtpPowerConfigManager() { return htp_power_config_manager_; } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index 2ed0c51ad7..aff67d5fba 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -14,7 +14,7 @@ namespace onnxruntime { namespace qnn { namespace power { -HtpPowerConfigManager::HtpPowerConfigManager(const Ort::Logger logger) : logger_(logger) { +HtpPowerConfigManager::HtpPowerConfigManager(const Ort::Logger& logger) : logger_ptr_(&logger) { constexpr int kMaxNumConfigs = 3; power_configs_.reserve(kMaxNumConfigs); } @@ -30,13 +30,13 @@ Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time) RETURN_IF(rpc_polling_time_set_, "There is already a pending RPC polling time config"); if (rpc_polling_time == last_set_rpc_polling_time_) { - ORT_CXX_LOG(logger_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Requested rpc polling time is the same as last set (" + std::to_string(last_set_rpc_polling_time_) + "). Ignoring request").c_str()); + ORT_CXX_LOG_PTR(logger_ptr_, + ORT_LOGGING_LEVEL_VERBOSE, + ("Requested rpc polling time is the same as last set (" + std::to_string(last_set_rpc_polling_time_) + "). Ignoring request").c_str()); } else { - ORT_CXX_LOG(logger_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Updating rpc polling time to: " + std::to_string(rpc_polling_time) + "us.").c_str()); + ORT_CXX_LOG_PTR(logger_ptr_, + ORT_LOGGING_LEVEL_VERBOSE, + ("Updating rpc polling time to: " + std::to_string(rpc_polling_time) + "us.").c_str()); auto& rpc_polling_time_cfg = power_configs_.emplace_back(); rpc_polling_time_cfg.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_RPC_POLLING_TIME; rpc_polling_time_cfg.rpcPollingTimeConfig = rpc_polling_time; @@ -50,15 +50,15 @@ Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time) Ort::Status HtpPowerConfigManager::AddRpcControlLatency(uint32_t rpc_control_latency) { RETURN_IF(rpc_control_latency_set_, "There is already a pending RPC control latency config"); if (rpc_control_latency == last_set_rpc_control_latency_) { - ORT_CXX_LOG(logger_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Requested rpc control latency is the same as last set (" + - std::to_string(last_set_rpc_control_latency_) + "). Ignoring request") - .c_str()); + ORT_CXX_LOG_PTR(logger_ptr_, + ORT_LOGGING_LEVEL_VERBOSE, + ("Requested rpc control latency is the same as last set (" + + std::to_string(last_set_rpc_control_latency_) + "). Ignoring request") + .c_str()); } else { - ORT_CXX_LOG(logger_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Updating rpc control latency to: " + std::to_string(rpc_control_latency) + "us.").c_str()); + ORT_CXX_LOG_PTR(logger_ptr_, + ORT_LOGGING_LEVEL_VERBOSE, + ("Updating rpc control latency to: " + std::to_string(rpc_control_latency) + "us.").c_str()); auto& rpc_control_latency_cfg = power_configs_.emplace_back(); rpc_control_latency_cfg.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_RPC_CONTROL_LATENCY; rpc_control_latency_cfg.rpcControlLatencyConfig = rpc_control_latency; @@ -105,18 +105,18 @@ Ort::Status HtpPowerConfigManager::AddHtpPerformanceMode(HtpPerformanceMode htp_ uint32_t htp_power_config_client_id) { RETURN_IF(htp_performance_mode_set_, "There is already a pending HTP performance mode config"); if (htp_performance_mode == last_set_htp_performance_mode_) { - ORT_CXX_LOG(logger_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Requested htp performance mode is the same as last set (" + - std::string(PerformanceModeToString(last_set_htp_performance_mode_)) + - "). Ignoring request") - .c_str()); + ORT_CXX_LOG_PTR(logger_ptr_, + ORT_LOGGING_LEVEL_VERBOSE, + ("Requested htp performance mode is the same as last set (" + + std::string(PerformanceModeToString(last_set_htp_performance_mode_)) + + "). Ignoring request") + .c_str()); } else { - ORT_CXX_LOG(logger_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Updating htp performance mode to: " + - std::string(PerformanceModeToString(htp_performance_mode)) + ".") - .c_str()); + ORT_CXX_LOG_PTR(logger_ptr_, + ORT_LOGGING_LEVEL_VERBOSE, + ("Updating htp performance mode to: " + + std::string(PerformanceModeToString(htp_performance_mode)) + ".") + .c_str()); QnnHtpPerfInfrastructure_PowerConfig_t htp_performance_cfg{}; RETURN_IF_ERROR(SetHtpPerformancePowerConfig(htp_performance_cfg, @@ -159,7 +159,7 @@ Ort::Status HtpPowerConfigManager::SetPowerConfig(uint32_t htp_power_config_clie htp_performance_mode_set_ = false; power_configs_.clear(); } else { - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "SetPowerConfig called but no configs to be set."); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "SetPowerConfig called but no configs to be set."); } return Ort::Status(); @@ -373,22 +373,22 @@ void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_i timer_ = std::move(temp); timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this); if (timer_callback_arg_ == nullptr) { - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create Timer argument"); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create Timer argument"); timer_.reset(); return; } if (!timer_->Initialize(TimerCallback, timer_callback_arg_.get())) { - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); timer_callback_arg_.reset(); timer_.reset(); } else { timer_resource_.timer_active_ = true; } } else { - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); } } else { - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); } } @@ -461,7 +461,7 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(uint32_t htp_power_co break; } default: - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); break; } return status; @@ -497,7 +497,7 @@ Ort::Status HtpPowerConfigManager::SetPerformance(uint32_t htp_power_config_clie break; } default: - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); break; } graph_state_ = GraphState::NONE; @@ -508,21 +508,19 @@ Ort::Status HtpPowerConfigManager::SetPerformance(uint32_t htp_power_config_clie graph_state_ = GraphState::NONE; break; default: - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); break; } return status; } Ort::Status HtpPowerConfigManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { - { - std::lock_guard lk(state_mutex_); - if (state != graph_state_) { - graph_state_ = state; - } else { - ORT_CXX_LOG(logger_, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); - return Ort::Status(); - } + std::lock_guard lk(state_mutex_); + if (state != graph_state_) { + graph_state_ = state; + } else { + ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); + return Ort::Status(); } if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); @@ -547,7 +545,7 @@ void HtpPowerConfigManager::TimerCallback(void* user_data) { if (instance->timer_resource_.timer_active_) { auto rt = instance->SetState(GraphState::TIMEOUT, args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0); if (!rt.IsOK()) { - ORT_CXX_LOG(instance->logger_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); + ORT_CXX_LOG_PTR(instance->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); } } } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 9e6e89b6fd..0ad4590d3f 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -21,7 +21,7 @@ namespace power { // updates power configurations for the HTP backend class HtpPowerConfigManager { public: - HtpPowerConfigManager(const Ort::Logger logger); + HtpPowerConfigManager(const Ort::Logger& logger); ~HtpPowerConfigManager(); // Stages a new rpc polling time for next power config update @@ -65,17 +65,12 @@ class HtpPowerConfigManager { void ReleaseTimerThread(); - bool IsTimerCreated() { - if (timer_ != nullptr) { - return true; - } - return false; - } - Ort::Status SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); void Init(const QNN_INTERFACE_VER_TYPE& qnn_interface) { qnn_interface_ = &qnn_interface; } + void ResetLogger(const Ort::Logger& logger) { logger_ptr_ = &logger; } + private: // Sets voltage corner votes for HTP based on the given performance mode Ort::Status SetHtpPerformancePowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, @@ -107,7 +102,7 @@ class HtpPowerConfigManager { std::vector power_configs_; - const Ort::Logger logger_; + const Ort::Logger* logger_ptr_; const QNN_INTERFACE_VER_TYPE* qnn_interface_ = nullptr; std::mutex perf_mutex_; @@ -128,6 +123,7 @@ class HtpPowerConfigManager { }; std::unique_ptr timer_callback_arg_; }; + } // namespace power } // namespace qnn } // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/builder/timer.cc b/onnxruntime/core/providers/qnn/builder/timer.cc index f2cf07bb4d..b9efe5f5dd 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.cc +++ b/onnxruntime/core/providers/qnn/builder/timer.cc @@ -3,6 +3,10 @@ #include "timer.h" +namespace onnxruntime { +namespace qnn { +namespace power { + void Timer::DeInitialize() { std::unique_lock lk(mtx_); is_timer_deinit_ = true; @@ -88,3 +92,7 @@ bool Timer::TimerInUse() { std::unique_lock lk(mtx_); return thread_status_ == threadState::LAUNCH; } + +} // namespace power +} // namespace qnn +} // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/builder/timer.h b/onnxruntime/core/providers/qnn/builder/timer.h index 53c424c795..fd19c20f37 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.h +++ b/onnxruntime/core/providers/qnn/builder/timer.h @@ -10,6 +10,10 @@ #include #include "core/providers/qnn/ort_api.h" +namespace onnxruntime { +namespace qnn { +namespace power { + class Timer { public: enum class threadState { @@ -71,3 +75,7 @@ class Timer { std::atomic is_timer_deinit_ = false; std::atomic is_timer_launched_ = false; }; + +} // namespace power +} // namespace qnn +} // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index fadf7040f4..48ea3e3423 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2069,7 +2069,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, if (qnn::IsOrtGraphHasCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; - bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id) ? true : false; + bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::ScopedGraphState state_guard( ep->qnn_backend_manager_.get(), power_config_valid, @@ -2082,7 +2082,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, return status; } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; - bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id) ? true : false; + bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::ScopedGraphState state_guard( ep->qnn_backend_manager_.get(), power_config_valid, @@ -2401,17 +2401,17 @@ void QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thr per_thread_htp_power_configs.rpc_control_latency = default_rpc_control_latency_; } - if (qnn::HtpPerformanceMode::kHtpBurst == pre_run_htp_performance_mode) { - per_thread_htp_power_configs.rpc_polling_time = 9999; - } else { - per_thread_htp_power_configs.rpc_polling_time = default_rpc_polling_time_; - } - if (qnn::HtpPerformanceMode::kHtpDefault != dynamic_htp_performance_mode_) { // reset perf mode, rpc control latency and rpc polling time to dynamic perf mode values per_thread_htp_power_configs.default_perf_mode = dynamic_htp_performance_mode_; + per_thread_htp_power_configs.rpc_polling_time = dynamic_rpc_polling_time_; } else if (qnn::HtpPerformanceMode::kHtpDefault != default_htp_performance_mode_) { per_thread_htp_power_configs.default_perf_mode = default_htp_performance_mode_; + per_thread_htp_power_configs.rpc_polling_time = default_rpc_polling_time_; + } + + if (qnn::HtpPerformanceMode::kHtpBurst == pre_run_htp_performance_mode) { + per_thread_htp_power_configs.rpc_polling_time = 9999; } if (qnn::HtpPerformanceMode::kHtpDefault != pre_run_htp_performance_mode) { @@ -2541,9 +2541,9 @@ OrtStatus* ORT_API_CALL QnnEp::SetDynamicOptionsImpl(_In_ OrtEp* this_ptr, if (htp_performance_mode != qnn::HtpPerformanceMode::kHtpDefault) { ep->dynamic_htp_performance_mode_ = htp_performance_mode; if (htp_performance_mode == qnn::HtpPerformanceMode::kHtpBurst) { - ep->default_rpc_polling_time_ = 9999; + ep->dynamic_rpc_polling_time_ = 9999; } else { - ep->default_rpc_polling_time_ = 0; + ep->dynamic_rpc_polling_time_ = 0; } } } else { diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.h b/onnxruntime/core/providers/qnn/qnn_execution_provider.h index c8cbbe81a1..39be097f32 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.h +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.h @@ -206,6 +206,7 @@ class QnnEp : public OrtEp, public ApiPtrs { qnn::HtpPerformanceMode dynamic_htp_performance_mode_{qnn::HtpPerformanceMode::kHtpDefault}; uint32_t default_rpc_control_latency_ = 0; uint32_t default_rpc_polling_time_ = 0; + uint32_t dynamic_rpc_polling_time_ = 0; qnn::ModelSettings model_settings_ = {}; qnn::HtpGraphFinalizationOptimizationMode htp_graph_finalization_opt_mode_ = qnn::HtpGraphFinalizationOptimizationMode::kDefault; int32_t vtcm_size_in_mb_ = 0; From 0d4188a9bb29de0d454bba85bae5a0dec357140f Mon Sep 17 00:00:00 2001 From: monumeen Date: Thu, 23 Apr 2026 00:54:56 +0530 Subject: [PATCH 19/36] addressed review comments --- .../core/providers/qnn/builder/qnn_backend_manager.cc | 2 +- onnxruntime/core/providers/qnn/builder/timer.cc | 2 +- onnxruntime/core/providers/qnn/qnn_execution_provider.cc | 2 +- onnxruntime/test/providers/qnn/qnn_basic_test.cc | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index cb4519a0b8..0810cd2c89 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -2088,7 +2088,7 @@ void QnnBackendManager::RemovePerThreadHtpPowerConfigMapping(const std::thread:: per_thread_power_configs_.erase(thread_id); } -Ort::Status QnnBackendManager::DestroyHTPPowerConfigID(uint32_t htp_power_config_id) { +Ort::Status QnnBackendManager::DestroyHtpPowerConfigId(uint32_t htp_power_config_id) { QnnDevice_Infrastructure_t qnn_device_infra = nullptr; auto status = qnn_interface_.deviceGetInfrastructure(&qnn_device_infra); RETURN_IF(QNN_SUCCESS != status, "backendGetPerfInfrastructure failed."); diff --git a/onnxruntime/core/providers/qnn/builder/timer.cc b/onnxruntime/core/providers/qnn/builder/timer.cc index b9efe5f5dd..1fcdc4e2b8 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.cc +++ b/onnxruntime/core/providers/qnn/builder/timer.cc @@ -90,7 +90,7 @@ void Timer::AbortTimer() { bool Timer::TimerInUse() { std::unique_lock lk(mtx_); - return thread_status_ == threadState::LAUNCH; + return thread_status_ == threadState::LAUNCH || thread_status_ == threadState::CALLING; } } // namespace power diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 48ea3e3423..38830b494b 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -1106,7 +1106,7 @@ QnnEp::~QnnEp() { qnn_backend_manager_->DeInitializePerfTimer(); std::lock_guard lock(config_id_mutex_); if (htp_power_config_id_.has_value()) { - qnn_backend_manager_->DestroyHTPPowerConfigID(*htp_power_config_id_); + qnn_backend_manager_->DestroyHtpPowerConfigId(*htp_power_config_id_); } } diff --git a/onnxruntime/test/providers/qnn/qnn_basic_test.cc b/onnxruntime/test/providers/qnn/qnn_basic_test.cc index c49fe99f26..7429453ce4 100644 --- a/onnxruntime/test/providers/qnn/qnn_basic_test.cc +++ b/onnxruntime/test/providers/qnn/qnn_basic_test.cc @@ -1007,9 +1007,6 @@ TEST_F(QnnHTPBackendTests, MultithreadSustainedHighPowerCfgFromEpOption) { options["offload_graph_io_quantization"] = "0"; options["htp_performance_mode"] = "sustained_high_performance"; - Ort::RunOptions run_opts; - run_opts.SetRunTag("logger0"); - Ort::SessionOptions session_opts; session_opts.SetLogId("logger0"); @@ -1023,6 +1020,8 @@ TEST_F(QnnHTPBackendTests, MultithreadSustainedHighPowerCfgFromEpOption) { constexpr int loop_count = 10; for (int i = 0; i < num_threads; i++) { + Ort::RunOptions run_opts; + run_opts.SetRunTag("logger0"); threads.push_back(std::thread(RunSessionAndVerify, std::ref(session), std::move(run_opts), std::ref(model->builder.feeds_), output_shapes, output_values, loop_count)); } From 1f8c8c368f77a817b03c6d6a849e753c630c08f5 Mon Sep 17 00:00:00 2001 From: monumeen Date: Thu, 23 Apr 2026 01:20:22 +0530 Subject: [PATCH 20/36] fix build failure --- onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index 83f071adc6..cf3f94d757 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -189,7 +189,7 @@ class QnnBackendManager : public std::enable_shared_from_this void DeInitializePerfTimer(); - Ort::Status DestroyHTPPowerConfigID(uint32_t htp_power_config_id); + Ort::Status DestroyHtpPowerConfigId(uint32_t htp_power_config_id); Ort::Status SetPerThreadHtpPowerConfigs(const std::thread::id& thread_id, bool pre_run); From 8081636becf2788dccd9c74326e9dcb5efbdac86 Mon Sep 17 00:00:00 2001 From: monumeen Date: Thu, 23 Apr 2026 15:33:39 +0530 Subject: [PATCH 21/36] addressing comments --- .../qnn/builder/qnn_backend_manager.cc | 12 ++-- .../qnn/builder/qnn_backend_manager.h | 41 ++++-------- .../core/providers/qnn/builder/qnn_def.h | 7 ++ .../builder/qnn_htp_power_config_manager.cc | 64 ++++++++----------- .../builder/qnn_htp_power_config_manager.h | 11 ++-- .../providers/qnn/qnn_execution_provider.cc | 36 +++++++---- 6 files changed, 85 insertions(+), 86 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 0810cd2c89..e9b899dbac 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -2043,15 +2043,19 @@ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id if (pre_run) { // add in htp_power_configs the default power config id also so to run when we execute if (htp_power_configs.pre_run_perf_mode.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config)); } } else { if (htp_power_configs.post_run_perf_mode.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config)); } else if (htp_power_configs.default_perf_mode.has_value()) { - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency)); + HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config)); } } return Ort::Status(); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index cf3f94d757..3c4fd0d4c1 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -328,7 +328,9 @@ class QnnBackendManager : public std::enable_shared_from_this htp_power_config_manager_.ResetLogger(logger); } - power::HtpPowerConfigManager& GetHtpPowerConfigManager() { return htp_power_config_manager_; } + Ort::Status SetHtpPowerState(GraphState state, const HtpPerfConfig_t& config) { + return htp_power_config_manager_.SetState(state, config); + } private: Ort::Status LoadBackend(); @@ -702,11 +704,11 @@ class QnnBackendManager : public std::enable_shared_from_this // // Typical usage (INIT_START / INIT_DONE pair): // -// uint32_t htp_power_config_id = 0; -// QnnBackendManager* mgr = ep->GetHtpPowerConfigId(htp_power_config_id) -// ? ep->qnn_backend_manager_.get() : nullptr; -// ScopedGraphState state_guard(mgr, GraphState::INIT_START, GraphState::INIT_DONE, -// htp_power_config_id, perf_mode, rpc_poll, rpc_latency); +// QnnBackendManager* mgr points to a valid manager instance; +// bool valid_power_config_id = ...; // determined by caller based on whether power config id was successfully created +// HtpPerfConfig_t config = ...; // configured as needed for the operation +// ScopedGraphState state_guard(mgr, valid_power_config_id, GraphState::INIT_START, GraphState::INIT_DONE, +// config); // RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); // auto status = DoWork(...); // RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); // optional: capture post-run perf error @@ -719,30 +721,20 @@ class ScopedGraphState { bool valid_power_config_id, GraphState start_state, GraphState done_state, - uint32_t htp_power_config_client_id, - qnn::HtpPerformanceMode perf_mode, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency) + const HtpPerfConfig_t& config) : manager_(manager), valid_power_config_id_(valid_power_config_id), done_state_(done_state), - htp_power_config_client_id_(htp_power_config_client_id), - perf_mode_(perf_mode), - rpc_polling_time_(rpc_polling_time), - rpc_control_latency_(rpc_control_latency), + config_(config), finalized_(false) { if (manager_ && valid_power_config_id_) { - power::HtpPowerConfigManager& htp_power_config_manager = manager_->GetHtpPowerConfigManager(); - start_status_ = htp_power_config_manager.SetState(start_state, htp_power_config_client_id_, - perf_mode_, rpc_polling_time_, rpc_control_latency_); + start_status_ = manager_->SetHtpPowerState(start_state, config_); } } ~ScopedGraphState() { if (!finalized_ && manager_ && valid_power_config_id_) { // Error cannot be propagated from a destructor; silently ignore. - power::HtpPowerConfigManager& htp_power_config_manager = manager_->GetHtpPowerConfigManager(); - htp_power_config_manager.SetState(done_state_, htp_power_config_client_id_, - perf_mode_, rpc_polling_time_, rpc_control_latency_); + manager_->SetHtpPowerState(done_state_, config_); } } // Returns (by move) the status of setting HTP performance before work begins. @@ -753,9 +745,7 @@ class ScopedGraphState { Ort::Status SetPostRunHtpPerf() { finalized_ = true; if (manager_ && valid_power_config_id_) { - power::HtpPowerConfigManager& htp_power_config_manager = manager_->GetHtpPowerConfigManager(); - return htp_power_config_manager.SetState(done_state_, htp_power_config_client_id_, - perf_mode_, rpc_polling_time_, rpc_control_latency_); + return manager_->SetHtpPowerState(done_state_, config_); } return Ort::Status(); } @@ -766,10 +756,7 @@ class ScopedGraphState { QnnBackendManager* manager_; bool valid_power_config_id_; GraphState done_state_; - uint32_t htp_power_config_client_id_; - qnn::HtpPerformanceMode perf_mode_; - uint32_t rpc_polling_time_; - uint32_t rpc_control_latency_; + HtpPerfConfig_t config_; Ort::Status start_status_; bool finalized_; }; diff --git a/onnxruntime/core/providers/qnn/builder/qnn_def.h b/onnxruntime/core/providers/qnn/builder/qnn_def.h index 8e7abe62ae..7be1ed4a5e 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_def.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_def.h @@ -101,6 +101,13 @@ enum class GraphState { NONE }; +typedef struct HtpPerfConfig { + uint32_t htp_power_config_client_id; + HtpPerformanceMode perf_mode; + uint32_t rpc_polling_time; + uint32_t rpc_control_latency; +} HtpPerfConfig_t; + typedef struct PerThreadHtpPowerConfigs { std::optional pre_run_perf_mode; std::optional post_run_perf_mode; diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index aff67d5fba..edf6bb9d7d 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -410,7 +410,7 @@ void HtpPowerConfigManager::ReleaseTimerThread() { } } -Ort::Status HtpPowerConfigManager::SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { +Ort::Status HtpPowerConfigManager::SetSustainedPerformance(const HtpPerfConfig_t& config) { std::lock_guard lk(perf_mutex_); Ort::Status status = Ort::Status(); @@ -429,15 +429,15 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(uint32_t htp_power_co if (IsTimerThreadRunning()) { timer_->AbortTimer(); } else { - status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + status = SetHtpPowerConfigs(config); } graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = true; break; case GraphState::INIT_DONE: { QnnHtpPerfInfrastructure_PowerConfig_t init_done_htp_performance_cfg{}; - SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, init_done_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, init_done_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = false; break; @@ -446,7 +446,7 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(uint32_t htp_power_co if (IsTimerThreadRunning()) { timer_->AbortTimer(); } else { - status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + status = SetHtpPowerConfigs(config); } graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = true; @@ -454,8 +454,8 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(uint32_t htp_power_co case GraphState::TIMEOUT: { if (!timer_resource_.caller_busy_) { QnnHtpPerfInfrastructure_PowerConfig_t timeout_htp_performance_cfg{}; - SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, timeout_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, timeout_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); graph_state_ = GraphState::NONE; } break; @@ -467,33 +467,33 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(uint32_t htp_power_co return status; } -Ort::Status HtpPowerConfigManager::SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { +Ort::Status HtpPowerConfigManager::SetPerformance(const HtpPerfConfig_t& config) { std::lock_guard lk(perf_mutex_); Ort::Status status = Ort::Status(); switch (graph_state_) { case GraphState::RUN_DONE: case GraphState::INIT_DONE: - switch (performance_mode) { + switch (config.perf_mode) { case qnn::HtpPerformanceMode::kHtpLowBalanced: case qnn::HtpPerformanceMode::kHtpBalanced: case qnn::HtpPerformanceMode::kHtpHighPerformance: { QnnHtpPerfInfrastructure_PowerConfig_t relaxed_htp_performance_cfg{}; - SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, relaxed_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, relaxed_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); break; } case qnn::HtpPerformanceMode::kHtpExtremePowerSaver: { QnnHtpPerfInfrastructure_PowerConfig_t extreme_power_saver_htp_performance_cfg{}; - SetExtremeLowPerfPowerConfig(extreme_power_saver_htp_performance_cfg, htp_power_config_client_id); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, extreme_power_saver_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + SetExtremeLowPerfPowerConfig(extreme_power_saver_htp_performance_cfg, config.htp_power_config_client_id); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, extreme_power_saver_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); break; } case qnn::HtpPerformanceMode::kHtpLowPowerSaver: case qnn::HtpPerformanceMode::kHtpHighPowerSaver: case qnn::HtpPerformanceMode::kHtpPowerSaver: { QnnHtpPerfInfrastructure_PowerConfig_t released_htp_performance_cfg{}; - SetReleasedPerfPowerConfig(released_htp_performance_cfg, htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(htp_power_config_client_id, released_htp_performance_cfg, rpc_polling_time, rpc_control_latency); + SetReleasedPerfPowerConfig(released_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, released_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); break; } default: @@ -504,7 +504,7 @@ Ort::Status HtpPowerConfigManager::SetPerformance(uint32_t htp_power_config_clie break; case GraphState::RUN_START: case GraphState::INIT_START: - status = SetHtpPowerConfigs(htp_power_config_client_id, performance_mode, rpc_polling_time, rpc_control_latency); + status = SetHtpPowerConfigs(config); graph_state_ = GraphState::NONE; break; default: @@ -514,7 +514,7 @@ Ort::Status HtpPowerConfigManager::SetPerformance(uint32_t htp_power_config_clie return status; } -Ort::Status HtpPowerConfigManager::SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { +Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfig_t& config) { std::lock_guard lk(state_mutex_); if (state != graph_state_) { graph_state_ = state; @@ -522,11 +522,11 @@ Ort::Status HtpPowerConfigManager::SetState(GraphState state, uint32_t htp_power ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); return Ort::Status(); } - if (perfMode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || perfMode == qnn::HtpPerformanceMode::kHtpBurst) { + if (config.perf_mode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || config.perf_mode == qnn::HtpPerformanceMode::kHtpBurst) { RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); RETURN_IF(timer_ == nullptr, "timer is not started"); - return SetSustainedPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); - } else if (perfMode == qnn::HtpPerformanceMode::kHtpDefault) { + return SetSustainedPerformance(config); + } else if (config.perf_mode == qnn::HtpPerformanceMode::kHtpDefault) { if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); } @@ -535,7 +535,7 @@ Ort::Status HtpPowerConfigManager::SetState(GraphState state, uint32_t htp_power if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); } - return SetPerformance(htp_power_config_client_id, perfMode, rpc_polling_time, rpc_control_latency); + return SetPerformance(config); } } @@ -543,7 +543,7 @@ void HtpPowerConfigManager::TimerCallback(void* user_data) { TimerCallbackArg* args = static_cast(user_data); HtpPowerConfigManager* instance = args->instance_; if (instance->timer_resource_.timer_active_) { - auto rt = instance->SetState(GraphState::TIMEOUT, args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0); + auto rt = instance->SetState(GraphState::TIMEOUT, {args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0}); if (!rt.IsOK()) { ORT_CXX_LOG_PTR(instance->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); } @@ -560,19 +560,12 @@ bool HtpPowerConfigManager::IsTimerThreadRunning() { return false; } -Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(uint32_t htp_power_config_client_id, - HtpPerformanceMode htp_performance_mode, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency) { - // This function is called in QNN EP's OnRunStart() even if QNN backend setup failed and the model is assigned - // to a different EP. Therefore, we have to check that backend setup actually completed before trying to - // set an HTP power config ID. Otherwise, this causes a segfault because the QNN backend lib is unloaded. - // RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete."); - RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time)); - RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency)); - RETURN_IF_ERROR(AddHtpPerformanceMode(htp_performance_mode, - htp_power_config_client_id)); - RETURN_IF_ERROR(SetPowerConfig(htp_power_config_client_id, +Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(const HtpPerfConfig_t& config) { + RETURN_IF_ERROR(AddRpcPollingTime(config.rpc_polling_time)); + RETURN_IF_ERROR(AddRpcControlLatency(config.rpc_control_latency)); + RETURN_IF_ERROR(AddHtpPerformanceMode(config.perf_mode, + config.htp_power_config_client_id)); + RETURN_IF_ERROR(SetPowerConfig(config.htp_power_config_client_id, *qnn_interface_)); return Ort::Status(); @@ -582,7 +575,6 @@ Ort::Status HtpPowerConfigManager::SetHtpPowerCustomConfigs(uint32_t htp_power_c QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { - // RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete."); RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time)); RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency)); RETURN_IF_ERROR(AddHtpPerformanceConfig(power_config)); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 0ad4590d3f..964fb06fd7 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -65,7 +65,7 @@ class HtpPowerConfigManager { void ReleaseTimerThread(); - Ort::Status SetState(GraphState state, uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode perfMode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetState(GraphState state, const HtpPerfConfig_t& config); void Init(const QNN_INTERFACE_VER_TYPE& qnn_interface) { qnn_interface_ = &qnn_interface; } @@ -77,18 +77,15 @@ class HtpPowerConfigManager { uint32_t htp_power_config_client_id, const HtpPerformanceMode& htp_performance_mode); - Ort::Status SetSustainedPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetSustainedPerformance(const HtpPerfConfig_t& config); - Ort::Status SetPerformance(uint32_t htp_power_config_client_id, qnn::HtpPerformanceMode performance_mode, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetPerformance(const HtpPerfConfig_t& config); static void TimerCallback(void* user_data); bool IsTimerThreadRunning(); - Ort::Status SetHtpPowerConfigs(uint32_t htp_power_config_client_id, - HtpPerformanceMode htp_performance_mode, - uint32_t rpc_polling_time, - uint32_t rpc_control_latency); + Ort::Status SetHtpPowerConfigs(const HtpPerfConfig_t& config); Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency); diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 38830b494b..bc18bbfac7 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2070,12 +2070,12 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, if (qnn::IsOrtGraphHasCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); + qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::ScopedGraphState state_guard( ep->qnn_backend_manager_.get(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - htp_power_config_id, ep->default_htp_performance_mode_, - ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + perf_config); RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileContextModel(graphs, fused_nodes, count, node_compute_infos); RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); @@ -2083,12 +2083,25 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); + qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::ScopedGraphState state_guard( ep->qnn_backend_manager_.get(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - htp_power_config_id, ep->default_htp_performance_mode_, - ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + perf_config); + RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); + auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); + RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); + return status; + } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { + uint32_t htp_power_config_id = 0; + bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); + qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; + qnn::ScopedGraphState state_guard( + ep->qnn_backend_manager_.get(), + power_config_valid, + qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + perf_config); RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); @@ -2206,12 +2219,12 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, #endif uint32_t htp_power_config_id = 0; bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); + qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::ScopedGraphState state_guard( ep->qnn_backend_manager_.get(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - htp_power_config_id, ep->default_htp_performance_mode_, - ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + perf_config); RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); auto finalize_status = qnn_model->FinalizeGraphs(ep->logger_); RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); @@ -2244,12 +2257,12 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, tp.Start(); uint32_t htp_power_config_id = 0; bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); + qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::ScopedGraphState state_guard( ep->qnn_backend_manager_.get(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - htp_power_config_id, ep->default_htp_performance_mode_, - ep->default_rpc_polling_time_, ep->default_rpc_control_latency_); + perf_config); RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); finalize_start = std::chrono::steady_clock::now(); for (auto& model_info : model_infos) { @@ -2410,13 +2423,12 @@ void QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thr per_thread_htp_power_configs.rpc_polling_time = default_rpc_polling_time_; } - if (qnn::HtpPerformanceMode::kHtpBurst == pre_run_htp_performance_mode) { - per_thread_htp_power_configs.rpc_polling_time = 9999; - } - if (qnn::HtpPerformanceMode::kHtpDefault != pre_run_htp_performance_mode) { per_thread_htp_power_configs.pre_run_perf_mode = pre_run_htp_performance_mode; // rpc polling time will only be updated with perf mode changes + if (qnn::HtpPerformanceMode::kHtpBurst == pre_run_htp_performance_mode) { + per_thread_htp_power_configs.rpc_polling_time = 9999; + } } if (qnn::HtpPerformanceMode::kHtpDefault != post_run_htp_performance_mode) { From 33eda5bb0790714acc6abb774686cf7bf148451b Mon Sep 17 00:00:00 2001 From: monumeen Date: Fri, 24 Apr 2026 16:31:46 +0530 Subject: [PATCH 22/36] adressing new comments --- .../qnn/builder/qnn_backend_manager.h | 63 ------------------ .../core/providers/qnn/builder/qnn_def.h | 6 ++ .../builder/qnn_htp_power_config_manager.cc | 34 ++++------ .../builder/qnn_htp_power_config_manager.h | 1 - .../providers/qnn/qnn_execution_provider.cc | 39 ++++------- .../providers/qnn/qnn_execution_provider.h | 66 +++++++++++++++++++ 6 files changed, 96 insertions(+), 113 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index 3c4fd0d4c1..7213064a08 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -697,68 +697,5 @@ class QnnBackendManager : public std::enable_shared_from_this std::shared_ptr rpcmem_library_ = nullptr; }; -// RAII guard for QnnBackendManager::SetState. -// -// Calls SetState(start_state, ...) on construction and SetState(done_state, ...) -// on destruction, ensuring the done state is always reached even on early returns. -// -// Typical usage (INIT_START / INIT_DONE pair): -// -// QnnBackendManager* mgr points to a valid manager instance; -// bool valid_power_config_id = ...; // determined by caller based on whether power config id was successfully created -// HtpPerfConfig_t config = ...; // configured as needed for the operation -// ScopedGraphState state_guard(mgr, valid_power_config_id, GraphState::INIT_START, GraphState::INIT_DONE, -// config); -// RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); -// auto status = DoWork(...); -// RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); // optional: capture post-run perf error -// return status; -// -// Passing nullptr as manager creates a no-op guard (all calls succeed immediately). -class ScopedGraphState { - public: - ScopedGraphState(QnnBackendManager* manager, - bool valid_power_config_id, - GraphState start_state, - GraphState done_state, - const HtpPerfConfig_t& config) - : manager_(manager), - valid_power_config_id_(valid_power_config_id), - done_state_(done_state), - config_(config), - finalized_(false) { - if (manager_ && valid_power_config_id_) { - start_status_ = manager_->SetHtpPowerState(start_state, config_); - } - } - ~ScopedGraphState() { - if (!finalized_ && manager_ && valid_power_config_id_) { - // Error cannot be propagated from a destructor; silently ignore. - manager_->SetHtpPowerState(done_state_, config_); - } - } - // Returns (by move) the status of setting HTP performance before work begins. - // Should be checked immediately after construction. - Ort::Status SetPreRunHtpPerfStatus() { return std::move(start_status_); } - // Explicitly sets HTP performance after work is done and returns its status. - // After this call the destructor will not invoke SetState again. - Ort::Status SetPostRunHtpPerf() { - finalized_ = true; - if (manager_ && valid_power_config_id_) { - return manager_->SetHtpPowerState(done_state_, config_); - } - return Ort::Status(); - } - ScopedGraphState(const ScopedGraphState&) = delete; - ScopedGraphState& operator=(const ScopedGraphState&) = delete; - - private: - QnnBackendManager* manager_; - bool valid_power_config_id_; - GraphState done_state_; - HtpPerfConfig_t config_; - Ort::Status start_status_; - bool finalized_; -}; } // namespace qnn } // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/builder/qnn_def.h b/onnxruntime/core/providers/qnn/builder/qnn_def.h index 7be1ed4a5e..990ea684a8 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_def.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_def.h @@ -108,6 +108,12 @@ typedef struct HtpPerfConfig { uint32_t rpc_control_latency; } HtpPerfConfig_t; +// pre_run_perf_mode and post_run_perf_mode takes precedence over default_perf_mode. If pre_run_perf_mode is set, +// it will be used for performance setting in OnRunStart(). +// If post_run_perf_mode is set, it will be used for performance setting in OnRunDone(). +// If default_perf_mode is set and pre_run_perf_mode or post_run_perf_mode is not set, +// default_perf_mode will be used for performance setting in both OnRunStart() and OnRunDone(). +// rpc_control_latency and rpc_polling_time will be set beforehand in OnRunStart() as it depends on the performance mode set in OnRunStart(). typedef struct PerThreadHtpPowerConfigs { std::optional pre_run_perf_mode; std::optional post_run_perf_mode; diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index edf6bb9d7d..d12ddf9cc3 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -96,7 +96,6 @@ static std::string_view PerformanceModeToString(HtpPerformanceMode htp_performan Ort::Status HtpPowerConfigManager::AddHtpPerformanceConfig(QnnHtpPerfInfrastructure_PowerConfig_t htp_performance_cfg) { power_configs_.emplace_back(std::move(htp_performance_cfg)); - last_set_htp_performance_mode_ = HtpPerformanceMode::kHtpDefault; htp_performance_mode_set_ = true; return Ort::Status(); } @@ -104,30 +103,19 @@ Ort::Status HtpPowerConfigManager::AddHtpPerformanceConfig(QnnHtpPerfInfrastruct Ort::Status HtpPowerConfigManager::AddHtpPerformanceMode(HtpPerformanceMode htp_performance_mode, uint32_t htp_power_config_client_id) { RETURN_IF(htp_performance_mode_set_, "There is already a pending HTP performance mode config"); - if (htp_performance_mode == last_set_htp_performance_mode_) { - ORT_CXX_LOG_PTR(logger_ptr_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Requested htp performance mode is the same as last set (" + - std::string(PerformanceModeToString(last_set_htp_performance_mode_)) + - "). Ignoring request") - .c_str()); - } else { - ORT_CXX_LOG_PTR(logger_ptr_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Updating htp performance mode to: " + - std::string(PerformanceModeToString(htp_performance_mode)) + ".") - .c_str()); - - QnnHtpPerfInfrastructure_PowerConfig_t htp_performance_cfg{}; - RETURN_IF_ERROR(SetHtpPerformancePowerConfig(htp_performance_cfg, - htp_power_config_client_id, - htp_performance_mode)); + ORT_CXX_LOG_PTR(logger_ptr_, + ORT_LOGGING_LEVEL_VERBOSE, + ("Updating htp performance mode to: " + + std::string(PerformanceModeToString(htp_performance_mode)) + ".") + .c_str()); - power_configs_.emplace_back(std::move(htp_performance_cfg)); + QnnHtpPerfInfrastructure_PowerConfig_t htp_performance_cfg{}; + RETURN_IF_ERROR(SetHtpPerformancePowerConfig(htp_performance_cfg, + htp_power_config_client_id, + htp_performance_mode)); - last_set_htp_performance_mode_ = htp_performance_mode; - htp_performance_mode_set_ = true; - } + power_configs_.emplace_back(std::move(htp_performance_cfg)); + htp_performance_mode_set_ = true; return Ort::Status(); } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 964fb06fd7..79183108f8 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -91,7 +91,6 @@ class HtpPowerConfigManager { uint32_t last_set_rpc_polling_time_ = kDisableRpcPolling; uint32_t last_set_rpc_control_latency_ = kDisableRpcControlLatency; - HtpPerformanceMode last_set_htp_performance_mode_ = HtpPerformanceMode::kHtpDefault; bool rpc_polling_time_set_ = false; bool rpc_control_latency_set_ = false; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index bc18bbfac7..75abbf52f5 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2071,40 +2071,27 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, uint32_t htp_power_config_id = 0; bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; - qnn::ScopedGraphState state_guard( + qnn::HtpPowerStateGuard power_guard( ep->qnn_backend_manager_.get(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); - RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); + RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileContextModel(graphs, fused_nodes, count, node_compute_infos); - RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); + RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); return status; } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; - qnn::ScopedGraphState state_guard( + qnn::HtpPowerStateGuard power_guard( ep->qnn_backend_manager_.get(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); - RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); + RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); - RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); - return status; - } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { - uint32_t htp_power_config_id = 0; - bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); - qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; - qnn::ScopedGraphState state_guard( - ep->qnn_backend_manager_.get(), - power_config_valid, - qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - perf_config); - RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); - auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); - RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); + RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); return status; } @@ -2220,14 +2207,14 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, uint32_t htp_power_config_id = 0; bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; - qnn::ScopedGraphState state_guard( + qnn::HtpPowerStateGuard power_guard( ep->qnn_backend_manager_.get(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); - RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); + RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); auto finalize_status = qnn_model->FinalizeGraphs(ep->logger_); - RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); + RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); if (!finalize_status.IsOK()) { return finalize_status.release(); } @@ -2258,12 +2245,12 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, uint32_t htp_power_config_id = 0; bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; - qnn::ScopedGraphState state_guard( + qnn::HtpPowerStateGuard power_guard( ep->qnn_backend_manager_.get(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); - RETURN_IF_NOT_OK(state_guard.SetPreRunHtpPerfStatus()); + RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); finalize_start = std::chrono::steady_clock::now(); for (auto& model_info : model_infos) { tp.SubmitJob([qnn_model = model_info.model.get(), &logger = ep->logger_, res = &model_info.result] { @@ -2273,7 +2260,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, tp.WaitForQueuedJobsToFinish(); end = std::chrono::steady_clock::now(); total_finalize_time = std::chrono::duration_cast(end - finalize_start); - RETURN_IF_NOT_OK(state_guard.SetPostRunHtpPerf()); + RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); for (auto& model_info : model_infos) { RETURN_IF_NOT_OK(std::move(model_info.result)); @@ -2549,7 +2536,7 @@ OrtStatus* ORT_API_CALL QnnEp::SetDynamicOptionsImpl(_In_ OrtEp* this_ptr, } qnn::HtpPerformanceMode htp_performance_mode = qnn::HtpPerformanceMode::kHtpDefault; ParseHtpPerformanceMode(value, htp_performance_mode, ep->logger_); - + // Dynamic HTP performance mode is used for performance setting for execute so it will be set in OnRunStart. if (htp_performance_mode != qnn::HtpPerformanceMode::kHtpDefault) { ep->dynamic_htp_performance_mode_ = htp_performance_mode; if (htp_performance_mode == qnn::HtpPerformanceMode::kHtpBurst) { diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.h b/onnxruntime/core/providers/qnn/qnn_execution_provider.h index 39be097f32..7bf2e5f62a 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.h +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.h @@ -248,4 +248,70 @@ class QnnEp : public OrtEp, public ApiPtrs { mutable std::atomic genie_kv_cache_rewind_{1}; }; +namespace qnn { +// RAII guard for QnnBackendManager::SetHtpPowerState. +// +// Calls SetHtpPowerState(start_state, ...) on construction and SetHtpPowerState(done_state, ...) +// on destruction, ensuring the done state is always reached even on early returns. +// +// Typical usage (INIT_START / INIT_DONE pair): +// +// QnnBackendManager* mgr points to a valid manager instance; +// bool valid_power_config_id = ...; // determined by caller based on whether power config id was successfully created +// HtpPerfConfig_t config = ...; // configured as needed for the operation +// HtpPowerStateGuard power_guard(mgr, valid_power_config_id, GraphState::INIT_START, GraphState::INIT_DONE, +// config); +// RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); +// auto status = DoWork(...); +// RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); // optional: capture post-run perf error +// return status; +// +// Passing nullptr as manager creates a no-op guard (all calls succeed immediately). +class HtpPowerStateGuard { + public: + HtpPowerStateGuard(QnnBackendManager* manager, + bool valid_power_config_id, + GraphState start_state, + GraphState done_state, + const HtpPerfConfig_t& config) + : manager_(manager), + valid_power_config_id_(valid_power_config_id), + done_state_(done_state), + config_(config), + finalized_(false) { + if (manager_ && valid_power_config_id_) { + start_status_ = manager_->SetHtpPowerState(start_state, config_); + } + } + ~HtpPowerStateGuard() { + if (!finalized_ && manager_ && valid_power_config_id_) { + // Error cannot be propagated from a destructor; silently ignore. + manager_->SetHtpPowerState(done_state_, config_); + } + } + // Returns (by move) the status of setting HTP performance before work begins. + // Should be checked immediately after construction. + Ort::Status SetPreRunHtpPerfStatus() { return std::move(start_status_); } + // Explicitly sets HTP performance after work is done and returns its status. + // After this call the destructor will not invoke SetState again. + Ort::Status SetPostRunHtpPerf() { + finalized_ = true; + if (manager_ && valid_power_config_id_) { + return manager_->SetHtpPowerState(done_state_, config_); + } + return Ort::Status(); + } + HtpPowerStateGuard(const HtpPowerStateGuard&) = delete; + HtpPowerStateGuard& operator=(const HtpPowerStateGuard&) = delete; + + private: + QnnBackendManager* manager_; + bool valid_power_config_id_; + GraphState done_state_; + HtpPerfConfig_t config_; + Ort::Status start_status_; + bool finalized_; +}; +} // namespace qnn + } // namespace onnxruntime From 6c7015f5376b8cb67da54a595720d8b072344940 Mon Sep 17 00:00:00 2001 From: monumeen Date: Sun, 26 Apr 2026 17:07:20 +0530 Subject: [PATCH 23/36] changing implementation of release timer --- .../builder/qnn_htp_power_config_manager.cc | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index d12ddf9cc3..39ba2650a8 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -381,20 +381,27 @@ void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_i } void HtpPowerConfigManager::ReleaseTimerThread() { + std::unique_ptr local_timer; + std::unique_ptr local_callback_arg; { std::lock_guard lk(state_mutex_); if (timer_ != nullptr) { - { - timer_resource_.timer_active_ = false; - graph_state_ = GraphState::NONE; - timer_resource_.caller_busy_ = false; - } + timer_resource_.timer_active_ = false; + graph_state_ = GraphState::NONE; + timer_resource_.caller_busy_ = false; + // Move ownership out while holding the lock: timer_ becomes nullptr + // atomically, so CreateTimerThread sees null and can safely create + // a new timer. We hold exclusive ownership in the locals. + local_timer = std::move(timer_); + local_callback_arg = std::move(timer_callback_arg_); } } - if (timer_ != nullptr) { - timer_->DeInitialize(); - timer_callback_arg_.reset(); - timer_.reset(); + // Deinitialize outside the lock to avoid deadlock: an in-flight + // TimerCallback calls SetState() which acquires state_mutex_. + if (local_timer != nullptr) { + local_timer->DeInitialize(); + local_callback_arg.reset(); + local_timer.reset(); } } From 791b5a844e1a571cb222b4bfa78e080c5f53b55b Mon Sep 17 00:00:00 2001 From: monumeen Date: Tue, 28 Apr 2026 18:58:19 +0530 Subject: [PATCH 24/36] moving raii wrapper from qnn execution provider --- .../qnn/builder/qnn_backend_manager.h | 4 +- .../qnn/builder/qnn_htp_power_state_guard.h | 76 +++++++++++++++++++ .../providers/qnn/qnn_execution_provider.cc | 11 ++- .../providers/qnn/qnn_execution_provider.h | 67 +--------------- 4 files changed, 86 insertions(+), 72 deletions(-) create mode 100644 onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index 7213064a08..f9902916f8 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -328,8 +328,8 @@ class QnnBackendManager : public std::enable_shared_from_this htp_power_config_manager_.ResetLogger(logger); } - Ort::Status SetHtpPowerState(GraphState state, const HtpPerfConfig_t& config) { - return htp_power_config_manager_.SetState(state, config); + power::HtpPowerConfigManager& GetHtpPowerConfigManager() { + return htp_power_config_manager_; } private: diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h new file mode 100644 index 0000000000..27ca1c12c5 --- /dev/null +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License + +#pragma once + +#include "core/providers/qnn/builder/qnn_htp_power_config_manager.h" + +namespace onnxruntime { +namespace qnn { + +// RAII guard for HtpPowerConfigManager::SetState. +// +// Calls SetState(start_state, ...) on construction and SetState(done_state, ...) +// on destruction, ensuring the done state is always reached even on early returns. +// +// Typical usage (INIT_START / INIT_DONE pair): +// +// power::HtpPowerConfigManager* power_manager = ...; +// bool valid_power_config_id = ...; // determined by caller based on whether power config id was successfully created +// HtpPerfConfig_t config = ...; // configured as needed for the operation +// HtpPowerStateGuard power_guard(power_manager, valid_power_config_id, GraphState::INIT_START, GraphState::INIT_DONE, +// config); +// RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); +// auto status = DoWork(...); +// RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); // optional: capture post-run perf error +// return status; +// +// Passing nullptr as power_manager creates a no-op guard (all calls succeed immediately). +class HtpPowerStateGuard { + public: + HtpPowerStateGuard(power::HtpPowerConfigManager* power_manager, + bool valid_power_config_id, + GraphState start_state, + GraphState done_state, + const HtpPerfConfig_t& config) + : power_manager_(power_manager), + valid_power_config_id_(valid_power_config_id), + done_state_(done_state), + config_(config), + finalized_(false) { + if (power_manager_ && valid_power_config_id_) { + start_status_ = power_manager_->SetState(start_state, config_); + } + } + ~HtpPowerStateGuard() { + if (!finalized_ && power_manager_ && valid_power_config_id_) { + // Error cannot be propagated from a destructor; silently ignore. + power_manager_->SetState(done_state_, config_); + } + } + // Returns (by move) the status of setting HTP performance before work begins. + // Should be checked immediately after construction. + Ort::Status SetPreRunHtpPerfStatus() { return std::move(start_status_); } + // Explicitly sets HTP performance after work is done and returns its status. + // After this call the destructor will not invoke SetState again. + Ort::Status SetPostRunHtpPerf() { + finalized_ = true; + if (power_manager_ && valid_power_config_id_) { + return power_manager_->SetState(done_state_, config_); + } + return Ort::Status(); + } + HtpPowerStateGuard(const HtpPowerStateGuard&) = delete; + HtpPowerStateGuard& operator=(const HtpPowerStateGuard&) = delete; + + private: + power::HtpPowerConfigManager* power_manager_; + bool valid_power_config_id_; + GraphState done_state_; + HtpPerfConfig_t config_; + Ort::Status start_status_; + bool finalized_; +}; + +} // namespace qnn +} // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 75abbf52f5..cb9bb36bef 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2072,7 +2072,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( - ep->qnn_backend_manager_.get(), + &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); @@ -2085,7 +2085,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( - ep->qnn_backend_manager_.get(), + &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); @@ -2208,7 +2208,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( - ep->qnn_backend_manager_.get(), + &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); @@ -2246,7 +2246,7 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( - ep->qnn_backend_manager_.get(), + &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, perf_config); @@ -2401,6 +2401,9 @@ void QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thr per_thread_htp_power_configs.rpc_control_latency = default_rpc_control_latency_; } + // This ensures that rpc polling time is always set to a value + per_thread_htp_power_configs.rpc_polling_time = kDisableRpcPolling; + if (qnn::HtpPerformanceMode::kHtpDefault != dynamic_htp_performance_mode_) { // reset perf mode, rpc control latency and rpc polling time to dynamic perf mode values per_thread_htp_power_configs.default_perf_mode = dynamic_htp_performance_mode_; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.h b/onnxruntime/core/providers/qnn/qnn_execution_provider.h index 7bf2e5f62a..18e35fc128 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.h +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.h @@ -26,6 +26,7 @@ #include "core/providers/qnn/genie/genie_api_loader.h" #include "core/providers/qnn/genie/genie_node.h" #include "core/providers/qnn/genie/genie_node_compute_info.h" +#include "core/providers/qnn/builder/qnn_htp_power_state_guard.h" namespace onnxruntime { class QnnEpFactory; @@ -248,70 +249,4 @@ class QnnEp : public OrtEp, public ApiPtrs { mutable std::atomic genie_kv_cache_rewind_{1}; }; -namespace qnn { -// RAII guard for QnnBackendManager::SetHtpPowerState. -// -// Calls SetHtpPowerState(start_state, ...) on construction and SetHtpPowerState(done_state, ...) -// on destruction, ensuring the done state is always reached even on early returns. -// -// Typical usage (INIT_START / INIT_DONE pair): -// -// QnnBackendManager* mgr points to a valid manager instance; -// bool valid_power_config_id = ...; // determined by caller based on whether power config id was successfully created -// HtpPerfConfig_t config = ...; // configured as needed for the operation -// HtpPowerStateGuard power_guard(mgr, valid_power_config_id, GraphState::INIT_START, GraphState::INIT_DONE, -// config); -// RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); -// auto status = DoWork(...); -// RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); // optional: capture post-run perf error -// return status; -// -// Passing nullptr as manager creates a no-op guard (all calls succeed immediately). -class HtpPowerStateGuard { - public: - HtpPowerStateGuard(QnnBackendManager* manager, - bool valid_power_config_id, - GraphState start_state, - GraphState done_state, - const HtpPerfConfig_t& config) - : manager_(manager), - valid_power_config_id_(valid_power_config_id), - done_state_(done_state), - config_(config), - finalized_(false) { - if (manager_ && valid_power_config_id_) { - start_status_ = manager_->SetHtpPowerState(start_state, config_); - } - } - ~HtpPowerStateGuard() { - if (!finalized_ && manager_ && valid_power_config_id_) { - // Error cannot be propagated from a destructor; silently ignore. - manager_->SetHtpPowerState(done_state_, config_); - } - } - // Returns (by move) the status of setting HTP performance before work begins. - // Should be checked immediately after construction. - Ort::Status SetPreRunHtpPerfStatus() { return std::move(start_status_); } - // Explicitly sets HTP performance after work is done and returns its status. - // After this call the destructor will not invoke SetState again. - Ort::Status SetPostRunHtpPerf() { - finalized_ = true; - if (manager_ && valid_power_config_id_) { - return manager_->SetHtpPowerState(done_state_, config_); - } - return Ort::Status(); - } - HtpPowerStateGuard(const HtpPowerStateGuard&) = delete; - HtpPowerStateGuard& operator=(const HtpPowerStateGuard&) = delete; - - private: - QnnBackendManager* manager_; - bool valid_power_config_id_; - GraphState done_state_; - HtpPerfConfig_t config_; - Ort::Status start_status_; - bool finalized_; -}; -} // namespace qnn - } // namespace onnxruntime From 8f70095c5ed653cf83120c83a95e043b74b7bef3 Mon Sep 17 00:00:00 2001 From: monumeen Date: Tue, 28 Apr 2026 21:23:15 +0530 Subject: [PATCH 25/36] fix for test failure --- onnxruntime/core/providers/qnn/qnn_execution_provider.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index cb9bb36bef..21f8dd2f7b 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2402,7 +2402,7 @@ void QnnEp::GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thr } // This ensures that rpc polling time is always set to a value - per_thread_htp_power_configs.rpc_polling_time = kDisableRpcPolling; + per_thread_htp_power_configs.rpc_polling_time = qnn::kDisableRpcPolling; if (qnn::HtpPerformanceMode::kHtpDefault != dynamic_htp_performance_mode_) { // reset perf mode, rpc control latency and rpc polling time to dynamic perf mode values From bd73eae888f257b114275c6b0717b774d4afd5d5 Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 29 Apr 2026 00:42:32 +0530 Subject: [PATCH 26/36] addressing comments --- .../core/providers/qnn/builder/qnn_def.h | 7 ---- .../builder/qnn_htp_power_config_manager.cc | 24 +++++++------ .../builder/qnn_htp_power_config_manager.h | 36 ++++++++++--------- .../test/providers/qnn/qnn_basic_test.cc | 32 ++++++++++------- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_def.h b/onnxruntime/core/providers/qnn/builder/qnn_def.h index 990ea684a8..b1564c5b15 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_def.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_def.h @@ -84,13 +84,6 @@ enum class HtpPerformanceMode : uint8_t { kHtpExtremePowerSaver, }; -enum class DcvsState { - DCVS_DEFAULT = 0, - DCVS_DISABLE = 1, - DCVS_ENABLE = 2, - DCVS_NUM_STATES -}; - // Graph states to tune the power/performance configurations enum class GraphState { INIT_START, diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index 39ba2650a8..21a5f34748 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -405,13 +405,13 @@ void HtpPowerConfigManager::ReleaseTimerThread() { } } -Ort::Status HtpPowerConfigManager::SetSustainedPerformance(const HtpPerfConfig_t& config) { +Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, const HtpPerfConfig_t& config) { std::lock_guard lk(perf_mutex_); Ort::Status status = Ort::Status(); std::chrono::microseconds sustainedDurationUs(timer_resource_.sustained_timer_duration_); - switch (graph_state_) { + switch (state) { case GraphState::RUN_DONE: if (IsTimerThreadRunning()) { timer_->AbortTimer(); @@ -431,7 +431,7 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(const HtpPerfConfig_t break; case GraphState::INIT_DONE: { QnnHtpPerfInfrastructure_PowerConfig_t init_done_htp_performance_cfg{}; - SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, init_done_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = false; @@ -449,7 +449,7 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(const HtpPerfConfig_t case GraphState::TIMEOUT: { if (!timer_resource_.caller_busy_) { QnnHtpPerfInfrastructure_PowerConfig_t timeout_htp_performance_cfg{}; - SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, timeout_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); graph_state_ = GraphState::NONE; } @@ -462,10 +462,10 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(const HtpPerfConfig_t return status; } -Ort::Status HtpPowerConfigManager::SetPerformance(const HtpPerfConfig_t& config) { +Ort::Status HtpPowerConfigManager::SetPerformance(GraphState state, const HtpPerfConfig_t& config) { std::lock_guard lk(perf_mutex_); Ort::Status status = Ort::Status(); - switch (graph_state_) { + switch (state) { case GraphState::RUN_DONE: case GraphState::INIT_DONE: switch (config.perf_mode) { @@ -473,7 +473,7 @@ Ort::Status HtpPowerConfigManager::SetPerformance(const HtpPerfConfig_t& config) case qnn::HtpPerformanceMode::kHtpBalanced: case qnn::HtpPerformanceMode::kHtpHighPerformance: { QnnHtpPerfInfrastructure_PowerConfig_t relaxed_htp_performance_cfg{}; - SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, relaxed_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); break; } @@ -487,7 +487,7 @@ Ort::Status HtpPowerConfigManager::SetPerformance(const HtpPerfConfig_t& config) case qnn::HtpPerformanceMode::kHtpHighPowerSaver: case qnn::HtpPerformanceMode::kHtpPowerSaver: { QnnHtpPerfInfrastructure_PowerConfig_t released_htp_performance_cfg{}; - SetReleasedPerfPowerConfig(released_htp_performance_cfg, config.htp_power_config_client_id, onnxruntime::qnn::DcvsState::DCVS_DEFAULT); + SetReleasedPerfPowerConfig(released_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, released_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); break; } @@ -520,7 +520,7 @@ Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfi if (config.perf_mode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || config.perf_mode == qnn::HtpPerformanceMode::kHtpBurst) { RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); RETURN_IF(timer_ == nullptr, "timer is not started"); - return SetSustainedPerformance(config); + return SetSustainedPerformance(state, config); } else if (config.perf_mode == qnn::HtpPerformanceMode::kHtpDefault) { if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); @@ -530,7 +530,7 @@ Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfi if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); } - return SetPerformance(config); + return SetPerformance(state, config); } } @@ -556,6 +556,7 @@ bool HtpPowerConfigManager::IsTimerThreadRunning() { } Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(const HtpPerfConfig_t& config) { + RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized"); RETURN_IF_ERROR(AddRpcPollingTime(config.rpc_polling_time)); RETURN_IF_ERROR(AddRpcControlLatency(config.rpc_control_latency)); RETURN_IF_ERROR(AddHtpPerformanceMode(config.perf_mode, @@ -567,9 +568,10 @@ Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(const HtpPerfConfig_t& con } Ort::Status HtpPowerConfigManager::SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, - QnnHtpPerfInfrastructure_PowerConfig_t power_config, + const QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency) { + RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized"); RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time)); RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency)); RETURN_IF_ERROR(AddHtpPerformanceConfig(power_config)); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 79183108f8..54f2680b1a 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -35,8 +35,6 @@ class HtpPowerConfigManager { Ort::Status AddRpcControlLatency(uint32_t rpc_control_latency); // Stages a new performance mode for next power config update - // If the value is the same as the last previously set, then - // there will be no new performance mode staged Ort::Status AddHtpPerformanceMode(HtpPerformanceMode htp_performance_mode, uint32_t htp_power_config_client_id); @@ -50,17 +48,6 @@ class HtpPowerConfigManager { Ort::Status SetPowerConfig(uint32_t htp_power_config_client_id, const QNN_INTERFACE_VER_TYPE& qnn_interface); - // Sets power config for relaxed performance mode based on DCVS state - void SetRelaxedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, - uint32_t htp_power_config_client_id, - DcvsState dcvsState); - - // Sets power config for released performance mode based on DCVS state - void SetReleasedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, DcvsState dcvsState); - - // Sets power config for extreme low performance mode - void SetExtremeLowPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id); - void CreateTimerThread(uint32_t htp_power_config_client_id); void ReleaseTimerThread(); @@ -77,9 +64,9 @@ class HtpPowerConfigManager { uint32_t htp_power_config_client_id, const HtpPerformanceMode& htp_performance_mode); - Ort::Status SetSustainedPerformance(const HtpPerfConfig_t& config); + Ort::Status SetSustainedPerformance(GraphState state, const HtpPerfConfig_t& config); - Ort::Status SetPerformance(const HtpPerfConfig_t& config); + Ort::Status SetPerformance(GraphState state, const HtpPerfConfig_t& config); static void TimerCallback(void* user_data); @@ -87,7 +74,24 @@ class HtpPowerConfigManager { Ort::Status SetHtpPowerConfigs(const HtpPerfConfig_t& config); - Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, QnnHtpPerfInfrastructure_PowerConfig_t power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, const QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + + enum class DcvsState { + DCVS_DEFAULT = 0, + DCVS_DISABLE = 1, + DCVS_ENABLE = 2 + }; + + // Sets power config for relaxed performance mode based on DCVS state + void SetRelaxedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, + uint32_t htp_power_config_client_id, + DcvsState dcvsState); + + // Sets power config for released performance mode based on DCVS state + void SetReleasedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, DcvsState dcvsState); + + // Sets power config for extreme low performance mode + void SetExtremeLowPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id); uint32_t last_set_rpc_polling_time_ = kDisableRpcPolling; uint32_t last_set_rpc_control_latency_ = kDisableRpcControlLatency; diff --git a/onnxruntime/test/providers/qnn/qnn_basic_test.cc b/onnxruntime/test/providers/qnn/qnn_basic_test.cc index 7429453ce4..46dd69e41e 100644 --- a/onnxruntime/test/providers/qnn/qnn_basic_test.cc +++ b/onnxruntime/test/providers/qnn/qnn_basic_test.cc @@ -1007,27 +1007,35 @@ TEST_F(QnnHTPBackendTests, MultithreadSustainedHighPowerCfgFromEpOption) { options["offload_graph_io_quantization"] = "0"; options["htp_performance_mode"] = "sustained_high_performance"; +#if defined(_WIN32) && (defined(__aarch64__) || defined(_M_ARM64)) + // By default, 8 is used, which will impact time to run all + // unit tests due to overhead of thread creation/destruction + options["num_graph_prepare_threads"] = "1"; +#endif + Ort::SessionOptions session_opts; session_opts.SetLogId("logger0"); RegisteredEpDeviceUniquePtr registered_ep_device; RegisterQnnEpLibrary(registered_ep_device, session_opts, onnxruntime::kQnnExecutionProvider, options); - Ort::Session session(*ort_env, model->model_data.data(), model->model_data.size(), session_opts); + { + Ort::Session session(*ort_env, model->model_data.data(), model->model_data.size(), session_opts); - std::vector threads; - constexpr int num_threads = 5; - constexpr int loop_count = 10; + std::vector threads; + constexpr int num_threads = 5; + constexpr int loop_count = 10; - for (int i = 0; i < num_threads; i++) { - Ort::RunOptions run_opts; - run_opts.SetRunTag("logger0"); - threads.push_back(std::thread(RunSessionAndVerify, std::ref(session), std::move(run_opts), - std::ref(model->builder.feeds_), output_shapes, output_values, loop_count)); - } + for (int i = 0; i < num_threads; i++) { + Ort::RunOptions run_opts; + run_opts.SetRunTag("logger0"); + threads.push_back(std::thread(RunSessionAndVerify, std::ref(session), std::move(run_opts), + std::ref(model->builder.feeds_), output_shapes, output_values, loop_count)); + } - for (auto& th : threads) { - th.join(); + for (auto& th : threads) { + th.join(); + } } } From 39b1bc229ee78f7cdbadb65881e7a71f33cf3510 Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 29 Apr 2026 10:22:53 +0530 Subject: [PATCH 27/36] addressing comments --- .../qnn/builder/qnn_backend_manager.cc | 10 +- .../qnn/builder/qnn_backend_manager.h | 3 +- .../builder/qnn_htp_power_config_manager.cc | 127 +++++++++--------- .../builder/qnn_htp_power_config_manager.h | 32 ++--- .../qnn/builder/qnn_htp_power_state_guard.h | 16 +-- .../providers/qnn/qnn_execution_provider.cc | 12 +- 6 files changed, 101 insertions(+), 99 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index e9b899dbac..94ec5d4bec 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -2005,7 +2005,7 @@ Ort::Status QnnBackendManager::SetupBackend( Ort::Status QnnBackendManager::InitializePowerCfgId(uint32_t device_id, uint32_t core_id, uint32_t& htp_power_config_id) { RETURN_IF_ERROR(CreateHtpPowerCfgId(device_id, core_id, htp_power_config_id)); - htp_power_config_manager_.CreateTimerThread(htp_power_config_id); + htp_power_config_manager_.CreateTimerThread(htp_power_config_id, *logger_ptr_); return Ort::Status(); } @@ -2044,18 +2044,18 @@ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id // add in htp_power_configs the default power config id also so to run when we execute if (htp_power_configs.pre_run_perf_mode.has_value()) { HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config, *logger_ptr_)); } else if (htp_power_configs.default_perf_mode.has_value()) { HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config, *logger_ptr_)); } } else { if (htp_power_configs.post_run_perf_mode.has_value()) { HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config, *logger_ptr_)); } else if (htp_power_configs.default_perf_mode.has_value()) { HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config)); + RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config, *logger_ptr_)); } } return Ort::Status(); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h index f9902916f8..7961258ce3 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.h @@ -153,7 +153,7 @@ class QnnBackendManager : public std::enable_shared_from_this soc_model_(config.soc_model), op_packages_(config.op_packages), skip_qnn_version_check_(config.skip_qnn_version_check), - htp_power_config_manager_(power::HtpPowerConfigManager(logger)), + htp_power_config_manager_(power::HtpPowerConfigManager()), api_ptrs_(api_ptrs), logger_ptr_(&logger) { } @@ -325,7 +325,6 @@ class QnnBackendManager : public std::enable_shared_from_this void ResetLogger(const Ort::Logger& logger) { logger_ptr_ = &logger; - htp_power_config_manager_.ResetLogger(logger); } power::HtpPowerConfigManager& GetHtpPowerConfigManager() { diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index 21a5f34748..0041dc3103 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -14,14 +14,14 @@ namespace onnxruntime { namespace qnn { namespace power { -HtpPowerConfigManager::HtpPowerConfigManager(const Ort::Logger& logger) : logger_ptr_(&logger) { +HtpPowerConfigManager::HtpPowerConfigManager() { constexpr int kMaxNumConfigs = 3; power_configs_.reserve(kMaxNumConfigs); } HtpPowerConfigManager::~HtpPowerConfigManager() {} -Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time) { +Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time, const Ort::Logger& logger) { RETURN_IF(rpc_polling_time > kMaxRpcPolling, ("Cannot set RPC polling time to " + std::to_string(rpc_polling_time) + ". Max allowable RPC polling time is: " + std::to_string(kMaxRpcPolling)) @@ -30,13 +30,13 @@ Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time) RETURN_IF(rpc_polling_time_set_, "There is already a pending RPC polling time config"); if (rpc_polling_time == last_set_rpc_polling_time_) { - ORT_CXX_LOG_PTR(logger_ptr_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Requested rpc polling time is the same as last set (" + std::to_string(last_set_rpc_polling_time_) + "). Ignoring request").c_str()); + ORT_CXX_LOG(logger, + ORT_LOGGING_LEVEL_VERBOSE, + ("Requested rpc polling time is the same as last set (" + std::to_string(last_set_rpc_polling_time_) + "). Ignoring request").c_str()); } else { - ORT_CXX_LOG_PTR(logger_ptr_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Updating rpc polling time to: " + std::to_string(rpc_polling_time) + "us.").c_str()); + ORT_CXX_LOG(logger, + ORT_LOGGING_LEVEL_VERBOSE, + ("Updating rpc polling time to: " + std::to_string(rpc_polling_time) + "us.").c_str()); auto& rpc_polling_time_cfg = power_configs_.emplace_back(); rpc_polling_time_cfg.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_RPC_POLLING_TIME; rpc_polling_time_cfg.rpcPollingTimeConfig = rpc_polling_time; @@ -47,18 +47,18 @@ Ort::Status HtpPowerConfigManager::AddRpcPollingTime(uint32_t rpc_polling_time) return Ort::Status(); } -Ort::Status HtpPowerConfigManager::AddRpcControlLatency(uint32_t rpc_control_latency) { +Ort::Status HtpPowerConfigManager::AddRpcControlLatency(uint32_t rpc_control_latency, const Ort::Logger& logger) { RETURN_IF(rpc_control_latency_set_, "There is already a pending RPC control latency config"); if (rpc_control_latency == last_set_rpc_control_latency_) { - ORT_CXX_LOG_PTR(logger_ptr_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Requested rpc control latency is the same as last set (" + - std::to_string(last_set_rpc_control_latency_) + "). Ignoring request") - .c_str()); + ORT_CXX_LOG(logger, + ORT_LOGGING_LEVEL_VERBOSE, + ("Requested rpc control latency is the same as last set (" + + std::to_string(last_set_rpc_control_latency_) + "). Ignoring request") + .c_str()); } else { - ORT_CXX_LOG_PTR(logger_ptr_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Updating rpc control latency to: " + std::to_string(rpc_control_latency) + "us.").c_str()); + ORT_CXX_LOG(logger, + ORT_LOGGING_LEVEL_VERBOSE, + ("Updating rpc control latency to: " + std::to_string(rpc_control_latency) + "us.").c_str()); auto& rpc_control_latency_cfg = power_configs_.emplace_back(); rpc_control_latency_cfg.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_RPC_CONTROL_LATENCY; rpc_control_latency_cfg.rpcControlLatencyConfig = rpc_control_latency; @@ -101,13 +101,14 @@ Ort::Status HtpPowerConfigManager::AddHtpPerformanceConfig(QnnHtpPerfInfrastruct } Ort::Status HtpPowerConfigManager::AddHtpPerformanceMode(HtpPerformanceMode htp_performance_mode, - uint32_t htp_power_config_client_id) { + uint32_t htp_power_config_client_id, + const Ort::Logger& logger) { RETURN_IF(htp_performance_mode_set_, "There is already a pending HTP performance mode config"); - ORT_CXX_LOG_PTR(logger_ptr_, - ORT_LOGGING_LEVEL_VERBOSE, - ("Updating htp performance mode to: " + - std::string(PerformanceModeToString(htp_performance_mode)) + ".") - .c_str()); + ORT_CXX_LOG(logger, + ORT_LOGGING_LEVEL_VERBOSE, + ("Updating htp performance mode to: " + + std::string(PerformanceModeToString(htp_performance_mode)) + ".") + .c_str()); QnnHtpPerfInfrastructure_PowerConfig_t htp_performance_cfg{}; RETURN_IF_ERROR(SetHtpPerformancePowerConfig(htp_performance_cfg, @@ -121,7 +122,8 @@ Ort::Status HtpPowerConfigManager::AddHtpPerformanceMode(HtpPerformanceMode htp_ } Ort::Status HtpPowerConfigManager::SetPowerConfig(uint32_t htp_power_config_client_id, - const QNN_INTERFACE_VER_TYPE& qnn_interface) { + const QNN_INTERFACE_VER_TYPE& qnn_interface, + const Ort::Logger& logger) { if (!power_configs_.empty()) { QnnDevice_Infrastructure_t qnn_device_infra = nullptr; auto status = qnn_interface.deviceGetInfrastructure(&qnn_device_infra); @@ -147,7 +149,7 @@ Ort::Status HtpPowerConfigManager::SetPowerConfig(uint32_t htp_power_config_clie htp_performance_mode_set_ = false; power_configs_.clear(); } else { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "SetPowerConfig called but no configs to be set."); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "SetPowerConfig called but no configs to be set."); } return Ort::Status(); @@ -353,30 +355,25 @@ void HtpPowerConfigManager::SetReleasedPerfPowerConfig(QnnHtpPerfInfrastructure_ dcvs_v3.setCoreParams = 1; } -void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_id) { +void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_id, const Ort::Logger& logger) { std::lock_guard lk(state_mutex_); if (timer_ == nullptr) { std::unique_ptr temp(new Timer()); if (temp != nullptr) { timer_ = std::move(temp); - timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this); - if (timer_callback_arg_ == nullptr) { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create Timer argument"); - timer_.reset(); - return; - } + timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this, logger); if (!timer_->Initialize(TimerCallback, timer_callback_arg_.get())) { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); timer_callback_arg_.reset(); timer_.reset(); } else { timer_resource_.timer_active_ = true; } } else { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Failed: Timer is nullptr"); } } else { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Timer already created"); } } @@ -405,7 +402,7 @@ void HtpPowerConfigManager::ReleaseTimerThread() { } } -Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, const HtpPerfConfig_t& config) { +Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, const HtpPerfConfig_t& config, const Ort::Logger& logger) { std::lock_guard lk(perf_mutex_); Ort::Status status = Ort::Status(); @@ -424,7 +421,7 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con if (IsTimerThreadRunning()) { timer_->AbortTimer(); } else { - status = SetHtpPowerConfigs(config); + status = SetHtpPowerConfigs(config, logger); } graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = true; @@ -432,7 +429,7 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con case GraphState::INIT_DONE: { QnnHtpPerfInfrastructure_PowerConfig_t init_done_htp_performance_cfg{}; SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, init_done_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, init_done_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency, logger); graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = false; break; @@ -441,7 +438,7 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con if (IsTimerThreadRunning()) { timer_->AbortTimer(); } else { - status = SetHtpPowerConfigs(config); + status = SetHtpPowerConfigs(config, logger); } graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = true; @@ -450,19 +447,19 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con if (!timer_resource_.caller_busy_) { QnnHtpPerfInfrastructure_PowerConfig_t timeout_htp_performance_cfg{}; SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, timeout_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, timeout_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency, logger); graph_state_ = GraphState::NONE; } break; } default: - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); break; } return status; } -Ort::Status HtpPowerConfigManager::SetPerformance(GraphState state, const HtpPerfConfig_t& config) { +Ort::Status HtpPowerConfigManager::SetPerformance(GraphState state, const HtpPerfConfig_t& config, const Ort::Logger& logger) { std::lock_guard lk(perf_mutex_); Ort::Status status = Ort::Status(); switch (state) { @@ -474,13 +471,13 @@ Ort::Status HtpPowerConfigManager::SetPerformance(GraphState state, const HtpPer case qnn::HtpPerformanceMode::kHtpHighPerformance: { QnnHtpPerfInfrastructure_PowerConfig_t relaxed_htp_performance_cfg{}; SetRelaxedPerfPowerConfig(relaxed_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, relaxed_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, relaxed_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency, logger); break; } case qnn::HtpPerformanceMode::kHtpExtremePowerSaver: { QnnHtpPerfInfrastructure_PowerConfig_t extreme_power_saver_htp_performance_cfg{}; SetExtremeLowPerfPowerConfig(extreme_power_saver_htp_performance_cfg, config.htp_power_config_client_id); - status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, extreme_power_saver_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, extreme_power_saver_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency, logger); break; } case qnn::HtpPerformanceMode::kHtpLowPowerSaver: @@ -488,39 +485,39 @@ Ort::Status HtpPowerConfigManager::SetPerformance(GraphState state, const HtpPer case qnn::HtpPerformanceMode::kHtpPowerSaver: { QnnHtpPerfInfrastructure_PowerConfig_t released_htp_performance_cfg{}; SetReleasedPerfPowerConfig(released_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); - status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, released_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency); + status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, released_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency, logger); break; } default: - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); break; } graph_state_ = GraphState::NONE; break; case GraphState::RUN_START: case GraphState::INIT_START: - status = SetHtpPowerConfigs(config); + status = SetHtpPowerConfigs(config, logger); graph_state_ = GraphState::NONE; break; default: - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); break; } return status; } -Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfig_t& config) { +Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfig_t& config, const Ort::Logger& logger) { std::lock_guard lk(state_mutex_); if (state != graph_state_) { graph_state_ = state; } else { - ORT_CXX_LOG_PTR(logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); return Ort::Status(); } if (config.perf_mode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || config.perf_mode == qnn::HtpPerformanceMode::kHtpBurst) { RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); RETURN_IF(timer_ == nullptr, "timer is not started"); - return SetSustainedPerformance(state, config); + return SetSustainedPerformance(state, config, logger); } else if (config.perf_mode == qnn::HtpPerformanceMode::kHtpDefault) { if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); @@ -530,17 +527,20 @@ Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfi if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); } - return SetPerformance(state, config); + return SetPerformance(state, config, logger); } } void HtpPowerConfigManager::TimerCallback(void* user_data) { TimerCallbackArg* args = static_cast(user_data); + if (args == nullptr) { + return; + } HtpPowerConfigManager* instance = args->instance_; if (instance->timer_resource_.timer_active_) { - auto rt = instance->SetState(GraphState::TIMEOUT, {args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0}); + auto rt = instance->SetState(GraphState::TIMEOUT, {args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0}, *args->logger_ptr_); if (!rt.IsOK()) { - ORT_CXX_LOG_PTR(instance->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); + ORT_CXX_LOG_PTR(args->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); } } } @@ -555,14 +555,14 @@ bool HtpPowerConfigManager::IsTimerThreadRunning() { return false; } -Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(const HtpPerfConfig_t& config) { +Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(const HtpPerfConfig_t& config, const Ort::Logger& logger) { RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized"); - RETURN_IF_ERROR(AddRpcPollingTime(config.rpc_polling_time)); - RETURN_IF_ERROR(AddRpcControlLatency(config.rpc_control_latency)); + RETURN_IF_ERROR(AddRpcPollingTime(config.rpc_polling_time, logger)); + RETURN_IF_ERROR(AddRpcControlLatency(config.rpc_control_latency, logger)); RETURN_IF_ERROR(AddHtpPerformanceMode(config.perf_mode, - config.htp_power_config_client_id)); + config.htp_power_config_client_id, logger)); RETURN_IF_ERROR(SetPowerConfig(config.htp_power_config_client_id, - *qnn_interface_)); + *qnn_interface_, logger)); return Ort::Status(); } @@ -570,12 +570,13 @@ Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(const HtpPerfConfig_t& con Ort::Status HtpPowerConfigManager::SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, const QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t rpc_polling_time, - uint32_t rpc_control_latency) { + uint32_t rpc_control_latency, + const Ort::Logger& logger) { RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized"); - RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time)); - RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency)); + RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time, logger)); + RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency, logger)); RETURN_IF_ERROR(AddHtpPerformanceConfig(power_config)); - RETURN_IF_ERROR(SetPowerConfig(htp_power_config_client_id, *qnn_interface_)); + RETURN_IF_ERROR(SetPowerConfig(htp_power_config_client_id, *qnn_interface_, logger)); return Ort::Status(); } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 54f2680b1a..4107a62016 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -21,22 +21,23 @@ namespace power { // updates power configurations for the HTP backend class HtpPowerConfigManager { public: - HtpPowerConfigManager(const Ort::Logger& logger); + HtpPowerConfigManager(); ~HtpPowerConfigManager(); // Stages a new rpc polling time for next power config update // If the value is the same as the last previously set, then // there will be no new rpc polling time staged - Ort::Status AddRpcPollingTime(uint32_t rpc_polling_time); + Ort::Status AddRpcPollingTime(uint32_t rpc_polling_time, const Ort::Logger& logger); // Stages a new rpc control latency for next power config update // If the value is the same as the last previously set, then // there will be no new rpc control latency staged - Ort::Status AddRpcControlLatency(uint32_t rpc_control_latency); + Ort::Status AddRpcControlLatency(uint32_t rpc_control_latency, const Ort::Logger& logger); // Stages a new performance mode for next power config update Ort::Status AddHtpPerformanceMode(HtpPerformanceMode htp_performance_mode, - uint32_t htp_power_config_client_id); + uint32_t htp_power_config_client_id, + const Ort::Logger& logger); // Stages a new HTP power configuration for next power config update // performance mode is set to default after setting the power config @@ -46,35 +47,34 @@ class HtpPowerConfigManager { // the HTP power configurations. If there is nothing staged, // then no attempt will be made. Ort::Status SetPowerConfig(uint32_t htp_power_config_client_id, - const QNN_INTERFACE_VER_TYPE& qnn_interface); + const QNN_INTERFACE_VER_TYPE& qnn_interface, + const Ort::Logger& logger); - void CreateTimerThread(uint32_t htp_power_config_client_id); + void CreateTimerThread(uint32_t htp_power_config_client_id, const Ort::Logger& logger); void ReleaseTimerThread(); - Ort::Status SetState(GraphState state, const HtpPerfConfig_t& config); + Ort::Status SetState(GraphState state, const HtpPerfConfig_t& config, const Ort::Logger& logger); void Init(const QNN_INTERFACE_VER_TYPE& qnn_interface) { qnn_interface_ = &qnn_interface; } - void ResetLogger(const Ort::Logger& logger) { logger_ptr_ = &logger; } - private: // Sets voltage corner votes for HTP based on the given performance mode Ort::Status SetHtpPerformancePowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, const HtpPerformanceMode& htp_performance_mode); - Ort::Status SetSustainedPerformance(GraphState state, const HtpPerfConfig_t& config); + Ort::Status SetSustainedPerformance(GraphState state, const HtpPerfConfig_t& config, const Ort::Logger& logger); - Ort::Status SetPerformance(GraphState state, const HtpPerfConfig_t& config); + Ort::Status SetPerformance(GraphState state, const HtpPerfConfig_t& config, const Ort::Logger& logger); static void TimerCallback(void* user_data); bool IsTimerThreadRunning(); - Ort::Status SetHtpPowerConfigs(const HtpPerfConfig_t& config); + Ort::Status SetHtpPowerConfigs(const HtpPerfConfig_t& config, const Ort::Logger& logger); - Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, const QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency); + Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, const QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency, const Ort::Logger& logger); enum class DcvsState { DCVS_DEFAULT = 0, @@ -102,7 +102,6 @@ class HtpPowerConfigManager { std::vector power_configs_; - const Ort::Logger* logger_ptr_; const QNN_INTERFACE_VER_TYPE* qnn_interface_ = nullptr; std::mutex perf_mutex_; @@ -118,8 +117,9 @@ class HtpPowerConfigManager { struct TimerCallbackArg { uint32_t power_config_id_; HtpPowerConfigManager* instance_; - TimerCallbackArg(uint32_t id, HtpPowerConfigManager* manager) - : power_config_id_(id), instance_(manager) {} + const Ort::Logger* logger_ptr_; + TimerCallbackArg(uint32_t id, HtpPowerConfigManager* manager, const Ort::Logger& logger) + : power_config_id_(id), instance_(manager), logger_ptr_(&logger) {} }; std::unique_ptr timer_callback_arg_; }; diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h index 27ca1c12c5..13116bb785 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h @@ -1,13 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License - #pragma once - #include "core/providers/qnn/builder/qnn_htp_power_config_manager.h" - namespace onnxruntime { namespace qnn { - // RAII guard for HtpPowerConfigManager::SetState. // // Calls SetState(start_state, ...) on construction and SetState(done_state, ...) @@ -32,20 +28,22 @@ class HtpPowerStateGuard { bool valid_power_config_id, GraphState start_state, GraphState done_state, - const HtpPerfConfig_t& config) + const HtpPerfConfig_t& config, + const Ort::Logger& logger) : power_manager_(power_manager), valid_power_config_id_(valid_power_config_id), done_state_(done_state), config_(config), + logger_ptr_(&logger), finalized_(false) { if (power_manager_ && valid_power_config_id_) { - start_status_ = power_manager_->SetState(start_state, config_); + start_status_ = power_manager_->SetState(start_state, config_, *logger_ptr_); } } ~HtpPowerStateGuard() { if (!finalized_ && power_manager_ && valid_power_config_id_) { // Error cannot be propagated from a destructor; silently ignore. - power_manager_->SetState(done_state_, config_); + power_manager_->SetState(done_state_, config_, *logger_ptr_); } } // Returns (by move) the status of setting HTP performance before work begins. @@ -56,7 +54,7 @@ class HtpPowerStateGuard { Ort::Status SetPostRunHtpPerf() { finalized_ = true; if (power_manager_ && valid_power_config_id_) { - return power_manager_->SetState(done_state_, config_); + return power_manager_->SetState(done_state_, config_, *logger_ptr_); } return Ort::Status(); } @@ -68,9 +66,9 @@ class HtpPowerStateGuard { bool valid_power_config_id_; GraphState done_state_; HtpPerfConfig_t config_; + const Ort::Logger* logger_ptr_; Ort::Status start_status_; bool finalized_; }; - } // namespace qnn } // namespace onnxruntime diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 21f8dd2f7b..327adcacf4 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2075,7 +2075,8 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - perf_config); + perf_config, + ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileContextModel(graphs, fused_nodes, count, node_compute_infos); RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); @@ -2088,7 +2089,8 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), power_config_valid, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - perf_config); + perf_config, + ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); auto status = ep->CompileDlcContextModel(this_ptr, graphs, fused_nodes, count, node_compute_infos); RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); @@ -2211,7 +2213,8 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - perf_config); + perf_config, + ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); auto finalize_status = qnn_model->FinalizeGraphs(ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); @@ -2249,7 +2252,8 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), valid_power_config_id, qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, - perf_config); + perf_config, + ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); finalize_start = std::chrono::steady_clock::now(); for (auto& model_info : model_infos) { From 35a2903f70712a13dbcd7c000cae9aa5877d233a Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 29 Apr 2026 11:49:45 +0530 Subject: [PATCH 28/36] improving logger functionality of htp_power_config_manager --- .../core/providers/qnn/builder/qnn_backend_manager.cc | 2 +- .../qnn/builder/qnn_htp_power_config_manager.cc | 10 ++++++---- .../qnn/builder/qnn_htp_power_config_manager.h | 7 +++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index 94ec5d4bec..d3c4a31121 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -2005,7 +2005,7 @@ Ort::Status QnnBackendManager::SetupBackend( Ort::Status QnnBackendManager::InitializePowerCfgId(uint32_t device_id, uint32_t core_id, uint32_t& htp_power_config_id) { RETURN_IF_ERROR(CreateHtpPowerCfgId(device_id, core_id, htp_power_config_id)); - htp_power_config_manager_.CreateTimerThread(htp_power_config_id, *logger_ptr_); + htp_power_config_manager_.CreateTimerThread(htp_power_config_id); return Ort::Status(); } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index 0041dc3103..a7ff431bc4 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -355,13 +355,14 @@ void HtpPowerConfigManager::SetReleasedPerfPowerConfig(QnnHtpPerfInfrastructure_ dcvs_v3.setCoreParams = 1; } -void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_id, const Ort::Logger& logger) { +void HtpPowerConfigManager::CreateTimerThread(uint32_t htp_power_config_client_id) { std::lock_guard lk(state_mutex_); + const Ort::Logger& logger = OrtLoggingManager::GetDefaultLogger(); if (timer_ == nullptr) { std::unique_ptr temp(new Timer()); if (temp != nullptr) { timer_ = std::move(temp); - timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this, logger); + timer_callback_arg_ = std::make_unique(htp_power_config_client_id, this); if (!timer_->Initialize(TimerCallback, timer_callback_arg_.get())) { ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Failed to create timer to set performance"); timer_callback_arg_.reset(); @@ -538,9 +539,10 @@ void HtpPowerConfigManager::TimerCallback(void* user_data) { } HtpPowerConfigManager* instance = args->instance_; if (instance->timer_resource_.timer_active_) { - auto rt = instance->SetState(GraphState::TIMEOUT, {args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0}, *args->logger_ptr_); + const Ort::Logger& logger = OrtLoggingManager::GetDefaultLogger(); + auto rt = instance->SetState(GraphState::TIMEOUT, {args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0}, logger); if (!rt.IsOK()) { - ORT_CXX_LOG_PTR(args->logger_ptr_, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "State update failed"); } } } diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 4107a62016..cbc501f34e 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -50,7 +50,7 @@ class HtpPowerConfigManager { const QNN_INTERFACE_VER_TYPE& qnn_interface, const Ort::Logger& logger); - void CreateTimerThread(uint32_t htp_power_config_client_id, const Ort::Logger& logger); + void CreateTimerThread(uint32_t htp_power_config_client_id); void ReleaseTimerThread(); @@ -117,9 +117,8 @@ class HtpPowerConfigManager { struct TimerCallbackArg { uint32_t power_config_id_; HtpPowerConfigManager* instance_; - const Ort::Logger* logger_ptr_; - TimerCallbackArg(uint32_t id, HtpPowerConfigManager* manager, const Ort::Logger& logger) - : power_config_id_(id), instance_(manager), logger_ptr_(&logger) {} + TimerCallbackArg(uint32_t id, HtpPowerConfigManager* manager) + : power_config_id_(id), instance_(manager) {} }; std::unique_ptr timer_callback_arg_; }; From 9c7020e0f936e27bb3349bb488f5fc20b3ed5a88 Mon Sep 17 00:00:00 2001 From: monumeen Date: Wed, 29 Apr 2026 21:23:14 +0530 Subject: [PATCH 29/36] addressing comments --- .../qnn/builder/qnn_backend_manager.cc | 16 +++++----- .../core/providers/qnn/builder/qnn_def.h | 17 ----------- .../builder/qnn_htp_power_config_manager.h | 29 +++++++++++++++---- .../qnn/builder/qnn_htp_power_state_guard.h | 14 ++++----- .../providers/qnn/qnn_execution_provider.cc | 16 +++++----- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc index d3c4a31121..730372107b 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc @@ -2043,19 +2043,19 @@ Ort::Status QnnBackendManager::SetPerThreadHtpPowerConfigs(const std::thread::id if (pre_run) { // add in htp_power_configs the default power config id also so to run when we execute if (htp_power_configs.pre_run_perf_mode.has_value()) { - HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config, *logger_ptr_)); + power::HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.pre_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(power::GraphState::RUN_START, config, *logger_ptr_)); } else if (htp_power_configs.default_perf_mode.has_value()) { - HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_START, config, *logger_ptr_)); + power::HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(power::GraphState::RUN_START, config, *logger_ptr_)); } } else { if (htp_power_configs.post_run_perf_mode.has_value()) { - HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config, *logger_ptr_)); + power::HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.post_run_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(power::GraphState::RUN_DONE, config, *logger_ptr_)); } else if (htp_power_configs.default_perf_mode.has_value()) { - HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; - RETURN_IF_ERROR(htp_power_config_manager_.SetState(onnxruntime::qnn::GraphState::RUN_DONE, config, *logger_ptr_)); + power::HtpPerfConfig_t config{htp_power_config_id, *htp_power_configs.default_perf_mode, *htp_power_configs.rpc_polling_time, *htp_power_configs.rpc_control_latency}; + RETURN_IF_ERROR(htp_power_config_manager_.SetState(power::GraphState::RUN_DONE, config, *logger_ptr_)); } } return Ort::Status(); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_def.h b/onnxruntime/core/providers/qnn/builder/qnn_def.h index b1564c5b15..259aaa882e 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_def.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_def.h @@ -84,23 +84,6 @@ enum class HtpPerformanceMode : uint8_t { kHtpExtremePowerSaver, }; -// Graph states to tune the power/performance configurations -enum class GraphState { - INIT_START, - INIT_DONE, - RUN_START, - RUN_DONE, - TIMEOUT, - NONE -}; - -typedef struct HtpPerfConfig { - uint32_t htp_power_config_client_id; - HtpPerformanceMode perf_mode; - uint32_t rpc_polling_time; - uint32_t rpc_control_latency; -} HtpPerfConfig_t; - // pre_run_perf_mode and post_run_perf_mode takes precedence over default_perf_mode. If pre_run_perf_mode is set, // it will be used for performance setting in OnRunStart(). // If post_run_perf_mode is set, it will be used for performance setting in OnRunDone(). diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index cbc501f34e..b93828850d 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -17,6 +17,29 @@ namespace onnxruntime { namespace qnn { namespace power { +// Graph states to tune the power/performance configurations +enum class GraphState { + INIT_START, + INIT_DONE, + RUN_START, + RUN_DONE, + TIMEOUT, + NONE +}; + +typedef struct HtpPerfConfig { + uint32_t htp_power_config_client_id; + HtpPerformanceMode perf_mode; + uint32_t rpc_polling_time; + uint32_t rpc_control_latency; +} HtpPerfConfig_t; + +enum class DcvsState { + DCVS_DEFAULT = 0, + DCVS_DISABLE = 1, + DCVS_ENABLE = 2 +}; + // Manages staging of any new power configurations and // updates power configurations for the HTP backend class HtpPowerConfigManager { @@ -76,12 +99,6 @@ class HtpPowerConfigManager { Ort::Status SetHtpPowerCustomConfigs(uint32_t htp_power_config_client_id, const QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t rpc_polling_time, uint32_t rpc_control_latency, const Ort::Logger& logger); - enum class DcvsState { - DCVS_DEFAULT = 0, - DCVS_DISABLE = 1, - DCVS_ENABLE = 2 - }; - // Sets power config for relaxed performance mode based on DCVS state void SetRelaxedPerfPowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h index 13116bb785..01d8414e06 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h @@ -13,8 +13,8 @@ namespace qnn { // // power::HtpPowerConfigManager* power_manager = ...; // bool valid_power_config_id = ...; // determined by caller based on whether power config id was successfully created -// HtpPerfConfig_t config = ...; // configured as needed for the operation -// HtpPowerStateGuard power_guard(power_manager, valid_power_config_id, GraphState::INIT_START, GraphState::INIT_DONE, +// power::HtpPerfConfig_t config = ...; // configured as needed for the operation +// HtpPowerStateGuard power_guard(power_manager, valid_power_config_id, power::GraphState::INIT_START, power::GraphState::INIT_DONE, // config); // RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); // auto status = DoWork(...); @@ -26,9 +26,9 @@ class HtpPowerStateGuard { public: HtpPowerStateGuard(power::HtpPowerConfigManager* power_manager, bool valid_power_config_id, - GraphState start_state, - GraphState done_state, - const HtpPerfConfig_t& config, + power::GraphState start_state, + power::GraphState done_state, + const power::HtpPerfConfig_t& config, const Ort::Logger& logger) : power_manager_(power_manager), valid_power_config_id_(valid_power_config_id), @@ -64,8 +64,8 @@ class HtpPowerStateGuard { private: power::HtpPowerConfigManager* power_manager_; bool valid_power_config_id_; - GraphState done_state_; - HtpPerfConfig_t config_; + power::GraphState done_state_; + power::HtpPerfConfig_t config_; const Ort::Logger* logger_ptr_; Ort::Status start_status_; bool finalized_; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc index 327adcacf4..ab885af4dd 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.cc +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.cc @@ -2070,11 +2070,11 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, if (qnn::IsOrtGraphHasCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); - qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; + qnn::power::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), power_config_valid, - qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + qnn::power::GraphState::INIT_START, qnn::power::GraphState::INIT_DONE, perf_config, ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); @@ -2084,11 +2084,11 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, } else if (qnn::IsOrtGraphHasDlcCtxNode(graphs, count, ep->ort_api)) { uint32_t htp_power_config_id = 0; bool power_config_valid = ep->GetHtpPowerConfigId(htp_power_config_id); - qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; + qnn::power::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), power_config_valid, - qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + qnn::power::GraphState::INIT_START, qnn::power::GraphState::INIT_DONE, perf_config, ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); @@ -2208,11 +2208,11 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, #endif uint32_t htp_power_config_id = 0; bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); - qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; + qnn::power::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), valid_power_config_id, - qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + qnn::power::GraphState::INIT_START, qnn::power::GraphState::INIT_DONE, perf_config, ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); @@ -2247,11 +2247,11 @@ OrtStatus* ORT_API_CALL QnnEp::CompileImpl(_In_ OrtEp* this_ptr, tp.Start(); uint32_t htp_power_config_id = 0; bool valid_power_config_id = ep->GetHtpPowerConfigId(htp_power_config_id); - qnn::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; + qnn::power::HtpPerfConfig_t perf_config{htp_power_config_id, ep->default_htp_performance_mode_, ep->default_rpc_polling_time_, ep->default_rpc_control_latency_}; qnn::HtpPowerStateGuard power_guard( &ep->qnn_backend_manager_->GetHtpPowerConfigManager(), valid_power_config_id, - qnn::GraphState::INIT_START, qnn::GraphState::INIT_DONE, + qnn::power::GraphState::INIT_START, qnn::power::GraphState::INIT_DONE, perf_config, ep->logger_); RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); From 13cf72ec41570905e4e0cce4099390857d305bf6 Mon Sep 17 00:00:00 2001 From: monumeen Date: Fri, 5 Jun 2026 11:22:42 +0530 Subject: [PATCH 30/36] addressing comments --- .../builder/qnn_htp_power_config_manager.cc | 28 +++++++++++++------ .../builder/qnn_htp_power_config_manager.h | 2 ++ .../qnn/builder/qnn_htp_power_state_guard.h | 17 ++++++----- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index a7ff431bc4..d2a26e4f86 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -508,16 +508,26 @@ Ort::Status HtpPowerConfigManager::SetPerformance(GraphState state, const HtpPer } Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfig_t& config, const Ort::Logger& logger) { - std::lock_guard lk(state_mutex_); - if (state != graph_state_) { - graph_state_ = state; - } else { - ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); - return Ort::Status(); + { + std::lock_guard lk(state_mutex_); + if (state != graph_state_) { + graph_state_ = state; + } else { + ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "State is the same as current. Ignoring request."); + return Ort::Status(); + } + if (config.perf_mode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || config.perf_mode == qnn::HtpPerformanceMode::kHtpBurst) { + RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); + RETURN_IF(timer_ == nullptr, "timer is not started"); + } } + + // Dispatch to performance setters outside state_mutex_ to avoid deadlock: + // AbortTimer() blocks until the timer thread is idle, but the timer thread + // (inside TimerCallback) calls SetState() which acquires state_mutex_. + // Holding state_mutex_ across AbortTimer() would therefore deadlock. + // The same pattern is already applied in ReleaseTimerThread(). if (config.perf_mode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || config.perf_mode == qnn::HtpPerformanceMode::kHtpBurst) { - RETURN_IF(timer_resource_.timer_active_ == false, "Timer is not active. Cannot set state."); - RETURN_IF(timer_ == nullptr, "timer is not started"); return SetSustainedPerformance(state, config, logger); } else if (config.perf_mode == qnn::HtpPerformanceMode::kHtpDefault) { if (timer_ && timer_->TimerInUse()) { @@ -577,7 +587,7 @@ Ort::Status HtpPowerConfigManager::SetHtpPowerCustomConfigs(uint32_t htp_power_c RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized"); RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time, logger)); RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency, logger)); - RETURN_IF_ERROR(AddHtpPerformanceConfig(power_config)); + RETURN_IF_ERROR(AddHtpPerformanceConfig(std::move(power_config))); RETURN_IF_ERROR(SetPowerConfig(htp_power_config_client_id, *qnn_interface_, logger)); return Ort::Status(); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index b93828850d..29eff50fd4 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -121,6 +121,8 @@ class HtpPowerConfigManager { const QNN_INTERFACE_VER_TYPE* qnn_interface_ = nullptr; + // Lock acquisition order: state_mutex_ must always be acquired before perf_mutex_ + // to prevent deadlocks. Never acquire state_mutex_ while already holding perf_mutex_. std::mutex perf_mutex_; std::mutex state_mutex_; std::unique_ptr timer_; diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h index 01d8414e06..9e427c0083 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h @@ -1,7 +1,10 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: MIT + #pragma once + #include "core/providers/qnn/builder/qnn_htp_power_config_manager.h" + namespace onnxruntime { namespace qnn { // RAII guard for HtpPowerConfigManager::SetState. @@ -34,16 +37,16 @@ class HtpPowerStateGuard { valid_power_config_id_(valid_power_config_id), done_state_(done_state), config_(config), - logger_ptr_(&logger), + logger_(logger), finalized_(false) { if (power_manager_ && valid_power_config_id_) { - start_status_ = power_manager_->SetState(start_state, config_, *logger_ptr_); + start_status_ = power_manager_->SetState(start_state, config_, logger_); } } ~HtpPowerStateGuard() { if (!finalized_ && power_manager_ && valid_power_config_id_) { // Error cannot be propagated from a destructor; silently ignore. - power_manager_->SetState(done_state_, config_, *logger_ptr_); + power_manager_->SetState(done_state_, config_, logger_); } } // Returns (by move) the status of setting HTP performance before work begins. @@ -54,7 +57,7 @@ class HtpPowerStateGuard { Ort::Status SetPostRunHtpPerf() { finalized_ = true; if (power_manager_ && valid_power_config_id_) { - return power_manager_->SetState(done_state_, config_, *logger_ptr_); + return power_manager_->SetState(done_state_, config_, logger_); } return Ort::Status(); } @@ -66,7 +69,7 @@ class HtpPowerStateGuard { bool valid_power_config_id_; power::GraphState done_state_; power::HtpPerfConfig_t config_; - const Ort::Logger* logger_ptr_; + const Ort::Logger& logger_; Ort::Status start_status_; bool finalized_; }; From 74379c5484bc949cb9df9f2ef7672938e2753d22 Mon Sep 17 00:00:00 2001 From: monumeen Date: Mon, 8 Jun 2026 11:50:45 +0530 Subject: [PATCH 31/36] fix for test failure --- onnxruntime/test/providers/qnn/qnn_basic_test.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/onnxruntime/test/providers/qnn/qnn_basic_test.cc b/onnxruntime/test/providers/qnn/qnn_basic_test.cc index 46dd69e41e..14ba925161 100644 --- a/onnxruntime/test/providers/qnn/qnn_basic_test.cc +++ b/onnxruntime/test/providers/qnn/qnn_basic_test.cc @@ -994,8 +994,7 @@ TEST_F(QnnHTPBackendTests, MultithreadSustainedHighPowerCfgFromEpOption) { CreateModelInMemory(model, QDQBuildAdd3Tensors(TestInputDef(shape, false, input_data), TestInputDef(shape, false, input_data), - TestInputDef(shape, false, input_data)), - "add3.qdq"); + TestInputDef(shape, false, input_data))); onnxruntime::ProviderOptions options; From 46464614a6efea91f60be8423db158a4f4e81214 Mon Sep 17 00:00:00 2001 From: monumeen Date: Mon, 8 Jun 2026 14:35:05 +0530 Subject: [PATCH 32/36] fix for test failure --- .../test/providers/qnn/qnn_basic_test.cc | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/onnxruntime/test/providers/qnn/qnn_basic_test.cc b/onnxruntime/test/providers/qnn/qnn_basic_test.cc index 14ba925161..8381090310 100644 --- a/onnxruntime/test/providers/qnn/qnn_basic_test.cc +++ b/onnxruntime/test/providers/qnn/qnn_basic_test.cc @@ -996,8 +996,7 @@ TEST_F(QnnHTPBackendTests, MultithreadSustainedHighPowerCfgFromEpOption) { TestInputDef(shape, false, input_data), TestInputDef(shape, false, input_data))); - onnxruntime::ProviderOptions options; - + ProviderOptions options; #if defined(_WIN32) options["backend_path"] = "QnnHtp.dll"; #else @@ -1016,25 +1015,21 @@ TEST_F(QnnHTPBackendTests, MultithreadSustainedHighPowerCfgFromEpOption) { session_opts.SetLogId("logger0"); RegisteredEpDeviceUniquePtr registered_ep_device; - RegisterQnnEpLibrary(registered_ep_device, session_opts, onnxruntime::kQnnExecutionProvider, options); - - { - Ort::Session session(*ort_env, model->model_data.data(), model->model_data.size(), session_opts); + RegisterQnnEpLibrary(registered_ep_device, session_opts, kQnnExecutionProvider, options); - std::vector threads; - constexpr int num_threads = 5; - constexpr int loop_count = 10; + ScopedOrtSession scoped(std::move(registered_ep_device), + Ort::Session(*ort_env, model->model_data.data(), model->model_data.size(), session_opts)); - for (int i = 0; i < num_threads; i++) { - Ort::RunOptions run_opts; - run_opts.SetRunTag("logger0"); - threads.push_back(std::thread(RunSessionAndVerify, std::ref(session), std::move(run_opts), - std::ref(model->builder.feeds_), output_shapes, output_values, loop_count)); - } + std::vector threads; + constexpr int num_threads = 5; + constexpr int loop_count = 10; + for (int i = 0; i < num_threads; i++) { + threads.push_back(std::thread(RunSessionAndVerify, std::ref(scoped.session()), Ort::RunOptions{nullptr}, + std::ref(model->builder.feeds_), output_shapes, output_values, loop_count)); + } - for (auto& th : threads) { - th.join(); - } + for (auto& th : threads) { + th.join(); } } From 12ee3a923ad97f5cd72796193b8e0a4eaf4d0cea Mon Sep 17 00:00:00 2001 From: monumeen Date: Mon, 8 Jun 2026 23:32:11 +0530 Subject: [PATCH 33/36] added changes to improve code --- .../builder/qnn_htp_power_config_manager.cc | 34 ++++++++++++------- .../builder/qnn_htp_power_config_manager.h | 7 +++- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc index d2a26e4f86..dc8532578c 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.cc @@ -396,6 +396,9 @@ void HtpPowerConfigManager::ReleaseTimerThread() { } // Deinitialize outside the lock to avoid deadlock: an in-flight // TimerCallback calls SetState() which acquires state_mutex_. + // Note: DeInitialize()->join() ensures any in-flight callback completes + // before the timer and callback_arg are destroyed, so no additional + // synchronization is needed to protect callback access to these objects. if (local_timer != nullptr) { local_timer->DeInitialize(); local_callback_arg.reset(); @@ -415,7 +418,6 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con timer_->AbortTimer(); } RETURN_IF_NOT(timer_->Launch(sustainedDurationUs), "Not able to launch timer thread."); - graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = false; break; case GraphState::RUN_START: @@ -424,14 +426,12 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con } else { status = SetHtpPowerConfigs(config, logger); } - graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = true; break; case GraphState::INIT_DONE: { QnnHtpPerfInfrastructure_PowerConfig_t init_done_htp_performance_cfg{}; SetRelaxedPerfPowerConfig(init_done_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, init_done_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency, logger); - graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = false; break; } @@ -441,7 +441,6 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con } else { status = SetHtpPowerConfigs(config, logger); } - graph_state_ = GraphState::NONE; timer_resource_.caller_busy_ = true; break; case GraphState::TIMEOUT: { @@ -449,7 +448,6 @@ Ort::Status HtpPowerConfigManager::SetSustainedPerformance(GraphState state, con QnnHtpPerfInfrastructure_PowerConfig_t timeout_htp_performance_cfg{}; SetRelaxedPerfPowerConfig(timeout_htp_performance_cfg, config.htp_power_config_client_id, DcvsState::DCVS_DEFAULT); status = SetHtpPowerCustomConfigs(config.htp_power_config_client_id, timeout_htp_performance_cfg, config.rpc_polling_time, config.rpc_control_latency, logger); - graph_state_ = GraphState::NONE; } break; } @@ -493,12 +491,10 @@ Ort::Status HtpPowerConfigManager::SetPerformance(GraphState state, const HtpPer ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Invalid performance mode"); break; } - graph_state_ = GraphState::NONE; break; case GraphState::RUN_START: case GraphState::INIT_START: status = SetHtpPowerConfigs(config, logger); - graph_state_ = GraphState::NONE; break; default: ORT_CXX_LOG(logger, ORT_LOGGING_LEVEL_VERBOSE, "Invalid graph state"); @@ -527,19 +523,28 @@ Ort::Status HtpPowerConfigManager::SetState(GraphState state, const HtpPerfConfi // (inside TimerCallback) calls SetState() which acquires state_mutex_. // Holding state_mutex_ across AbortTimer() would therefore deadlock. // The same pattern is already applied in ReleaseTimerThread(). + Ort::Status status; if (config.perf_mode == qnn::HtpPerformanceMode::kHtpSustainedHighPerformance || config.perf_mode == qnn::HtpPerformanceMode::kHtpBurst) { - return SetSustainedPerformance(state, config, logger); + status = SetSustainedPerformance(state, config, logger); } else if (config.perf_mode == qnn::HtpPerformanceMode::kHtpDefault) { if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); } - return Ort::Status(); + status = Ort::Status(); } else { if (timer_ && timer_->TimerInUse()) { timer_->AbortTimer(); } - return SetPerformance(state, config, logger); + status = SetPerformance(state, config, logger); + } + + // Update graph_state_ to NONE after performance functions complete + { + std::lock_guard lk(state_mutex_); + graph_state_ = GraphState::NONE; } + + return status; } void HtpPowerConfigManager::TimerCallback(void* user_data) { @@ -548,6 +553,9 @@ void HtpPowerConfigManager::TimerCallback(void* user_data) { return; } HtpPowerConfigManager* instance = args->instance_; + if (instance == nullptr) { + return; + } if (instance->timer_resource_.timer_active_) { const Ort::Logger& logger = OrtLoggingManager::GetDefaultLogger(); auto rt = instance->SetState(GraphState::TIMEOUT, {args->power_config_id_, qnn::HtpPerformanceMode::kHtpSustainedHighPerformance, 0, 0}, logger); @@ -568,7 +576,7 @@ bool HtpPowerConfigManager::IsTimerThreadRunning() { } Ort::Status HtpPowerConfigManager::SetHtpPowerConfigs(const HtpPerfConfig_t& config, const Ort::Logger& logger) { - RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized"); + RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized. Call Init() first."); RETURN_IF_ERROR(AddRpcPollingTime(config.rpc_polling_time, logger)); RETURN_IF_ERROR(AddRpcControlLatency(config.rpc_control_latency, logger)); RETURN_IF_ERROR(AddHtpPerformanceMode(config.perf_mode, @@ -584,10 +592,10 @@ Ort::Status HtpPowerConfigManager::SetHtpPowerCustomConfigs(uint32_t htp_power_c uint32_t rpc_polling_time, uint32_t rpc_control_latency, const Ort::Logger& logger) { - RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized"); + RETURN_IF(qnn_interface_ == nullptr, "QNN interface is not initialized. Call Init() first."); RETURN_IF_ERROR(AddRpcPollingTime(rpc_polling_time, logger)); RETURN_IF_ERROR(AddRpcControlLatency(rpc_control_latency, logger)); - RETURN_IF_ERROR(AddHtpPerformanceConfig(std::move(power_config))); + RETURN_IF_ERROR(AddHtpPerformanceConfig(power_config)); RETURN_IF_ERROR(SetPowerConfig(htp_power_config_client_id, *qnn_interface_, logger)); return Ort::Status(); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 29eff50fd4..27885df3ab 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -41,7 +41,12 @@ enum class DcvsState { }; // Manages staging of any new power configurations and -// updates power configurations for the HTP backend +// updates power configurations for the HTP backend. +// +// IMPORTANT: Init() must be called before any other methods that access +// the QNN interface (SetState, SetPowerConfig, etc.), typically during +// backend initialization. Failure to call Init() will result in errors +// when attempting to set power configurations. class HtpPowerConfigManager { public: HtpPowerConfigManager(); From be1e05415880e7106547f9ec05cf4cc0ac67cb01 Mon Sep 17 00:00:00 2001 From: monumeen Date: Tue, 9 Jun 2026 16:30:17 +0530 Subject: [PATCH 34/36] adding changes for review comment --- .../qnn/builder/qnn_htp_power_state_guard.h | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h index 9e427c0083..4d84f57b95 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_state_guard.h @@ -9,8 +9,9 @@ namespace onnxruntime { namespace qnn { // RAII guard for HtpPowerConfigManager::SetState. // -// Calls SetState(start_state, ...) on construction and SetState(done_state, ...) -// on destruction, ensuring the done state is always reached even on early returns. +// Calls SetState(start_state, ...) when SetPreRunHtpPerfStatus() is invoked and +// SetState(done_state, ...) on destruction, ensuring the done state is always reached +// even on early returns. // // Typical usage (INIT_START / INIT_DONE pair): // @@ -19,7 +20,8 @@ namespace qnn { // power::HtpPerfConfig_t config = ...; // configured as needed for the operation // HtpPowerStateGuard power_guard(power_manager, valid_power_config_id, power::GraphState::INIT_START, power::GraphState::INIT_DONE, // config); -// RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); +// // ... optional setup work ... +// RETURN_IF_NOT_OK(power_guard.SetPreRunHtpPerfStatus()); // Sets the pre-run state here // auto status = DoWork(...); // RETURN_IF_NOT_OK(power_guard.SetPostRunHtpPerf()); // optional: capture post-run perf error // return status; @@ -35,23 +37,29 @@ class HtpPowerStateGuard { const Ort::Logger& logger) : power_manager_(power_manager), valid_power_config_id_(valid_power_config_id), + start_state_(start_state), done_state_(done_state), config_(config), logger_(logger), + pre_run_called_(false), finalized_(false) { - if (power_manager_ && valid_power_config_id_) { - start_status_ = power_manager_->SetState(start_state, config_, logger_); - } } ~HtpPowerStateGuard() { - if (!finalized_ && power_manager_ && valid_power_config_id_) { + if (pre_run_called_ && !finalized_ && power_manager_ && valid_power_config_id_) { // Error cannot be propagated from a destructor; silently ignore. power_manager_->SetState(done_state_, config_, logger_); } } - // Returns (by move) the status of setting HTP performance before work begins. - // Should be checked immediately after construction. - Ort::Status SetPreRunHtpPerfStatus() { return std::move(start_status_); } + // Sets HTP performance state before work begins and returns the status. + // Should be called after construction and before the actual work starts. + // This provides flexibility to perform other setup between construction and state setting. + Ort::Status SetPreRunHtpPerfStatus() { + pre_run_called_ = true; + if (power_manager_ && valid_power_config_id_) { + return power_manager_->SetState(start_state_, config_, logger_); + } + return Ort::Status(); + } // Explicitly sets HTP performance after work is done and returns its status. // After this call the destructor will not invoke SetState again. Ort::Status SetPostRunHtpPerf() { @@ -67,10 +75,11 @@ class HtpPowerStateGuard { private: power::HtpPowerConfigManager* power_manager_; bool valid_power_config_id_; + power::GraphState start_state_; power::GraphState done_state_; power::HtpPerfConfig_t config_; const Ort::Logger& logger_; - Ort::Status start_status_; + bool pre_run_called_; bool finalized_; }; } // namespace qnn From a786dd69b92d301f054d9198e3cdcf05ebc68882 Mon Sep 17 00:00:00 2001 From: monumeen Date: Mon, 15 Jun 2026 14:36:17 +0530 Subject: [PATCH 35/36] resolving review comments --- onnxruntime/core/providers/qnn/builder/qnn_def.h | 4 ++-- .../core/providers/qnn/builder/qnn_htp_power_config_manager.h | 3 ++- onnxruntime/core/providers/qnn/builder/timer.h | 2 ++ onnxruntime/core/providers/qnn/qnn_execution_provider.h | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/qnn_def.h b/onnxruntime/core/providers/qnn/builder/qnn_def.h index 259aaa882e..3e372c301b 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_def.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_def.h @@ -151,8 +151,8 @@ constexpr const uint32_t kDisableRpcPolling = 0; constexpr const uint32_t kDisableRpcControlLatency = 0; constexpr const uint32_t kMaxRpcPolling = 9999; -// performance timer timeout value is in microseconds -static constexpr uint64_t kDefaultTimerTimeoutUs = 300000; +// Sustained high performance mode timer timeout duration in microseconds +constexpr const uint64_t kDefaultTimerTimeoutUs = 300000; struct OnnxTensorInfo { ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(OnnxTensorInfo); diff --git a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h index 27885df3ab..26c816013d 100644 --- a/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h +++ b/onnxruntime/core/providers/qnn/builder/qnn_htp_power_config_manager.h @@ -87,6 +87,7 @@ class HtpPowerConfigManager { void Init(const QNN_INTERFACE_VER_TYPE& qnn_interface) { qnn_interface_ = &qnn_interface; } private: + ORT_DISALLOW_COPY_AND_ASSIGNMENT(HtpPowerConfigManager); // Sets voltage corner votes for HTP based on the given performance mode Ort::Status SetHtpPerformancePowerConfig(QnnHtpPerfInfrastructure_PowerConfig_t& power_config, uint32_t htp_power_config_client_id, @@ -137,7 +138,7 @@ class HtpPowerConfigManager { std::atomic timer_active_ = false; }; TimerResource timer_resource_; - std::atomic graph_state_ = GraphState::NONE; + GraphState graph_state_ = GraphState::NONE; struct TimerCallbackArg { uint32_t power_config_id_; HtpPowerConfigManager* instance_; diff --git a/onnxruntime/core/providers/qnn/builder/timer.h b/onnxruntime/core/providers/qnn/builder/timer.h index fd19c20f37..48f1c921ad 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.h +++ b/onnxruntime/core/providers/qnn/builder/timer.h @@ -63,6 +63,8 @@ class Timer { bool TimerInUse(); private: + ORT_DISALLOW_COPY_AND_ASSIGNMENT(Timer); + std::thread bkg_thread_; void BkgTimer(); std::mutex mtx_; diff --git a/onnxruntime/core/providers/qnn/qnn_execution_provider.h b/onnxruntime/core/providers/qnn/qnn_execution_provider.h index 18e35fc128..bcb2734fa4 100644 --- a/onnxruntime/core/providers/qnn/qnn_execution_provider.h +++ b/onnxruntime/core/providers/qnn/qnn_execution_provider.h @@ -158,7 +158,7 @@ class QnnEp : public OrtEp, public ApiPtrs { } GraphFinalizationInfo_t; - // Will return true if any power config options need to be updated + // Retrieves per-thread HTP power configurations from run options void GetPerThreadHtpPowerConfigs(qnn::PerThreadHtpPowerConfigs_t& per_thread_htp_power_configs, const ::OrtRunOptions* run_options); From f0f5fc87e2a1022b7512cf1d6054d532fcdcbcd7 Mon Sep 17 00:00:00 2001 From: monumeen Date: Fri, 19 Jun 2026 16:29:41 +0530 Subject: [PATCH 36/36] addressing reviews --- .../core/providers/qnn/builder/timer.cc | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/onnxruntime/core/providers/qnn/builder/timer.cc b/onnxruntime/core/providers/qnn/builder/timer.cc index 1fcdc4e2b8..c339f6dd55 100644 --- a/onnxruntime/core/providers/qnn/builder/timer.cc +++ b/onnxruntime/core/providers/qnn/builder/timer.cc @@ -22,14 +22,8 @@ Timer::~Timer() { this->DeInitialize(); } void Timer::BkgTimer() { { std::unique_lock lk(mtx_); - try { - thread_status_ = threadState::IDLE; - cv_.notify_all(); - } catch (const std::system_error& e) { - ORT_UNUSED_PARAMETER(e); - thread_status_ = threadState::FAILED; - cv_.notify_all(); - } + thread_status_ = threadState::IDLE; + cv_.notify_all(); } while (true) { std::unique_lock lk(mtx_); @@ -73,11 +67,14 @@ bool Timer::Initialize(std::function callbackFn, void* callbackArg) std::unique_lock lk(mtx_); timeout_arg_ = callbackArg; timeout_fn_ = callbackFn; - bkg_thread_ = std::thread(&Timer::BkgTimer, this); - cv_.wait(lk, [&] { return thread_status_ == threadState::IDLE || thread_status_ == threadState::FAILED; }); - if (thread_status_ == threadState::FAILED) { + try { + bkg_thread_ = std::thread(&Timer::BkgTimer, this); + } catch (const std::system_error& e) { + ORT_UNUSED_PARAMETER(e); + thread_status_ = threadState::FAILED; return false; } + cv_.wait(lk, [&] { return thread_status_ == threadState::IDLE; }); return true; }