From 81d34099c67a3e2300346e39389ca17df1fc2d3c Mon Sep 17 00:00:00 2001 From: Aaditya Srinivasan Date: Mon, 8 Jun 2026 02:17:17 +0530 Subject: [PATCH] [WebGPU] Make max_num_pending_dispatches configurable --- .../core/providers/webgpu/webgpu_context.cc | 12 +++++++++ .../core/providers/webgpu/webgpu_context.h | 3 ++- .../webgpu/webgpu_provider_factory.cc | 25 +++++++++++++++++++ .../webgpu/webgpu_provider_options.h | 3 +++ .../test/providers/cpu/tensor/expand_test.cc | 21 ++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.cc b/onnxruntime/core/providers/webgpu/webgpu_context.cc index b0241915a4ff4..3b2411d7b8ca3 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.cc +++ b/onnxruntime/core/providers/webgpu/webgpu_context.cc @@ -41,6 +41,8 @@ namespace webgpu { void WebGpuContext::Initialize(const WebGpuContextConfig& config) { std::call_once(init_flag_, [this, &config]() { + max_num_pending_dispatches_ = config.max_num_pending_dispatches; + if (device_ == nullptr) { // Create wgpu::Adapter wgpu::RequestAdapterOptions req_adapter_options = {}; @@ -174,6 +176,16 @@ void WebGpuContext::Initialize(const WebGpuContextConfig& config) { query_type_ = TimestampQueryType::None; } }); + + if (max_num_pending_dispatches_ != config.max_num_pending_dispatches) { + LOGS_DEFAULT(WARNING) + << "WebGPU context is already initialized with " + << "maxNumPendingDispatches=" + << max_num_pending_dispatches_ + << ". Requested value " + << config.max_num_pending_dispatches + << " will be ignored."; + } } Status WebGpuContext::Wait(wgpu::Future f) { diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.h b/onnxruntime/core/providers/webgpu/webgpu_context.h index e2e9d33fe3834..d6bf6166e0084 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.h +++ b/onnxruntime/core/providers/webgpu/webgpu_context.h @@ -97,6 +97,7 @@ struct WebGpuContextConfig { }; bool validation_mode_explicitly_set{false}; bool preserve_device{false}; + uint32_t max_num_pending_dispatches{16}; uint64_t max_storage_buffer_binding_size{0}; WebGpuBufferCacheConfig buffer_cache_config{}; int power_preference{static_cast(WGPUPowerPreference_HighPerformance)}; @@ -348,7 +349,7 @@ class WebGpuContext final { std::unique_ptr program_mgr_; uint32_t num_pending_dispatches_ = 0; - const uint32_t max_num_pending_dispatches_ = 16; + uint32_t max_num_pending_dispatches_ = 16; std::unique_ptr split_k_config_; diff --git a/onnxruntime/core/providers/webgpu/webgpu_provider_factory.cc b/onnxruntime/core/providers/webgpu/webgpu_provider_factory.cc index 61042ff5996f2..3b6d0f65e458b 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_provider_factory.cc +++ b/onnxruntime/core/providers/webgpu/webgpu_provider_factory.cc @@ -199,6 +199,30 @@ WebGpuContextConfig ParseWebGpuContextConfig(const ConfigOptions& config_options "Invalid maxStorageBufferBindingSize value: ", max_storage_buffer_binding_size_str); } + std::string max_num_pending_dispatches_str; + if (config_options.TryGetConfigEntry( + kMaxNumPendingDispatches, + max_num_pending_dispatches_str)) { + ORT_ENFORCE( + std::errc{} == + std::from_chars( + max_num_pending_dispatches_str.data(), + max_num_pending_dispatches_str.data() + + max_num_pending_dispatches_str.size(), + config.max_num_pending_dispatches) + .ec, + "Invalid maxNumPendingDispatches value: ", + max_num_pending_dispatches_str); + ORT_ENFORCE( + config.max_num_pending_dispatches > 0, + "maxNumPendingDispatches must be greater than 0"); + // Practical cap to avoid excessive query buffer sizing and + // unpredictable memory/performance behavior. + ORT_ENFORCE( + config.max_num_pending_dispatches <= 4096, + "maxNumPendingDispatches must be less than or equal to 4096"); + } + LOGS_DEFAULT(VERBOSE) << "WebGPU EP Device ID: " << config.context_id; LOGS_DEFAULT(VERBOSE) << "WebGPU EP WGPUInstance: " << reinterpret_cast(config.instance); LOGS_DEFAULT(VERBOSE) << "WebGPU EP WGPUDevice: " << reinterpret_cast(config.device); @@ -206,6 +230,7 @@ WebGpuContextConfig ParseWebGpuContextConfig(const ConfigOptions& config_options LOGS_DEFAULT(VERBOSE) << "WebGPU EP ValidationMode: " << config.validation_mode; LOGS_DEFAULT(VERBOSE) << "WebGPU EP PreserveDevice: " << config.preserve_device; LOGS_DEFAULT(VERBOSE) << "WebGPU EP max storage buffer binding size: " << config.max_storage_buffer_binding_size; + LOGS_DEFAULT(VERBOSE) << "WebGPU EP max pending dispatches: " << config.max_num_pending_dispatches; // buffer cache modes auto parse_buffer_cache_mode = [&config_options](const std::string& config_entry_str, diff --git a/onnxruntime/core/providers/webgpu/webgpu_provider_options.h b/onnxruntime/core/providers/webgpu/webgpu_provider_options.h index d2faccdb8c4a5..1a287afebcb8b 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_provider_options.h +++ b/onnxruntime/core/providers/webgpu/webgpu_provider_options.h @@ -36,6 +36,9 @@ constexpr const char* kEnablePIXCapture = "ep.webgpuexecutionprovider.enablePIXC constexpr const char* kPreserveDevice = "ep.webgpuexecutionprovider.preserveDevice"; constexpr const char* kMaxStorageBufferBindingSize = "ep.webgpuexecutionprovider.maxStorageBufferBindingSize"; +// Valid range: 1-4096. Larger values are rejected to avoid excessive +// query buffer sizing and unpredictable memory/performance behavior. +constexpr const char* kMaxNumPendingDispatches = "ep.webgpuexecutionprovider.maxNumPendingDispatches"; // The following are the possible values for the provider options. diff --git a/onnxruntime/test/providers/cpu/tensor/expand_test.cc b/onnxruntime/test/providers/cpu/tensor/expand_test.cc index cb2774a22f99c..d2ee12689392b 100644 --- a/onnxruntime/test/providers/cpu/tensor/expand_test.cc +++ b/onnxruntime/test/providers/cpu/tensor/expand_test.cc @@ -186,6 +186,27 @@ TEST(ExpandOpTest, Expand_3x1x3x1_int64_webgpu) { test.ConfigEp(std::move(provider)) .RunWithConfig(); } + +TEST(ExpandOpTest, Expand_3x3_int64_webgpu_max_num_pending_dispatches) { + OpTester test("Expand", 8); + + test.AddInput("data_0", {1}, {1}); + test.AddInput("data_1", {2}, {3, 3}); + + test.AddOutput("result", {3, 3}, + {1, 1, 1, + 1, 1, 1, + 1, 1, 1}); + + ConfigOptions config_options{}; + ASSERT_STATUS_OK(config_options.AddConfigEntry(webgpu::options::kEnableInt64, "1")); + ASSERT_STATUS_OK(config_options.AddConfigEntry(webgpu::options::kMaxNumPendingDispatches, "32")); + + auto provider = WebGpuExecutionProviderWithOptions(config_options); + + test.ConfigEp(std::move(provider)) + .RunWithConfig(); +} #endif TEST(ExpandOpTest, Expand_3x3_float16) {