Repository navigation
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Extract the subgroup-matrix MatMul into a reusable SubgroupMatrixMatMulContext (device capability check + vendor tiling policy) dispatched via the shared ComputeMatMul(). The 1x1 Conv path now caches this context and runs the subgroup-matrix kernel, threading w_is_constant/b_is_constant so odd-N constant weights are still padded once and cached.
e462a32 to
7f5aba8
Compare
| std::call_once(impl_init_flag_, [&]() { | ||
| impl_ = CreateSubgroupMatrixConv1x1Impl(context); | ||
| }); | ||
| if (impl_ != nullptr && activation_.activation_kind_ == ActivationKind::None) { |
There was a problem hiding this comment.
Let impl_ decide whether to reject activation_.activation_kind_ == ActivationKind::None
| #if !defined(__wasm__) | ||
| // Lazily create the subgroup-matrix 1x1-Conv impl once so the device | ||
| // capability query is not repeated on every inference step. It computes a | ||
| // plain (non-activation-fused) matmul, so only attempt it when no activation | ||
| // is fused; on the channels-last path B is the (possibly constant) weight, so | ||
| // an odd-N weight can be padded/cached, whereas channels-first B is the | ||
| // activation input and is never constant. | ||
| std::call_once(impl_init_flag_, [&]() { | ||
| impl_ = CreateSubgroupMatrixConv1x1Impl(context); | ||
| }); | ||
| if (impl_ != nullptr && activation_.activation_kind_ == ActivationKind::None) { | ||
| bool handled = false; | ||
| ORT_RETURN_IF_ERROR(impl_->Compute(context, matmul_inputs, output, | ||
| matmul_input_reshapes[0], matmul_input_reshapes[1], | ||
| is_channels_last && w_is_constant_, handled)); | ||
| if (handled) { | ||
| return Status::OK(); | ||
| } | ||
| } | ||
| #endif |
There was a problem hiding this comment.
Move this at the beginning of Compute(). Check the 1x1 condition by impl itself.
| // When B is a shared 2D weight (its batch folds to 1), fold A's leading dims | ||
| // into M so the problem runs as a single 2D-weight matmul (e.g. the 1x1 Conv | ||
| // [batch,H*W,C] @ [1,C,N] -> [batch*H*W, C] @ [C, N]). | ||
| const int64_t batch_a = a_shape.SizeToDimension(a_shape.NumDimensions() - 2); | ||
| const int64_t batch_b = b_shape.SizeToDimension(b_shape.NumDimensions() - 2); | ||
| if (batch_a != 1 && batch_b == 1) { | ||
| const int64_t k = a_shape[a_shape.NumDimensions() - 1]; | ||
| const int64_t n = b_shape[b_shape.NumDimensions() - 1]; | ||
| const int64_t batch_and_m = a_shape.SizeToDimension(a_shape.NumDimensions() - 1); | ||
| a_shape = TensorShape({batch_and_m, k}); | ||
| b_shape = TensorShape({k, n}); | ||
| } |
There was a problem hiding this comment.
It seems we are duplicating most Compute() code lines with SubgroupMatrixMatMul except this block here and input a, b reshape, right? Maybe we can refactor a bit.
8380a1b to
438b814
Compare
438b814 to
56ca415
Compare
49166ec to
abbc93f
Compare
| }); | ||
| if (impl_ != nullptr) { | ||
| bool handled = false; | ||
| ORT_RETURN_IF_ERROR(impl_->Compute(context, conv_attrs_, activation_, is_channels_last, |
There was a problem hiding this comment.
Define an abstract class ConvOptImpl as MatMulOptImpl does, so that we needn't pass conv_attrs_, activation_, ... to impl here. Instead impl can access them via parent.
| if (!is_channels_last || prepacked_kernel != nullptr || !w_is_constant || | ||
| activation.activation_kind_ != ActivationKind::None || conv_attrs.group != 1) { | ||
| return Status::OK(); | ||
| } |
There was a problem hiding this comment.
prepacked_kernel != nullptr is hard to understand, as it's logically complicated which implies quite a few conditions. As for conv(matmul) here, the major conditions are:
// 3. MatMul optimization (same_size || is_1x1_conv):
// - is_channels_last: transposes
// - !is_channels_last: does NOT transpose (uses kernel directly)
As you only serve channels-last, should we suppose or just assert prepacked_kernel != nullptr?
| const auto kernel_width = kernel_shape[3]; | ||
|
|
||
| const bool same_size = input_height == kernel_height && input_width == kernel_width && pads[0] == 0 && pads[1] == 0; | ||
| const bool is_conv1x1_matmul = same_size || (kernel_height == 1 && kernel_width == 1 && pads[0] == 0 && pads[1] == 0 && strides[0] == 1 && strides[1] == 1); |
There was a problem hiding this comment.
As we also support same size, conv1x1 is not accurate to be used as the file/class name.
| virtual Status Compute(ComputeContext& context, /*out*/ bool& handled) = 0; | ||
|
|
||
| protected: | ||
| const ConvOptImplParent& parent_; |
There was a problem hiding this comment.
No need to define a new class ConvOptImplParent. Simply use Conv class instead.
There was a problem hiding this comment.
is_conv_matmul instead of is_conv1x1_matmul
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Routes eligible WebGPU Conv2D operations through the shared subgroup-matrix MatMul kernel.
Changes:
- Adds subgroup-matrix Conv implementation and shared configuration utilities.
- Refactors MatMul/Gemm to reuse common tiling and padding logic.
- Adds disabled large Conv test cases.
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
onnxruntime/test/providers/webgpu/conv_large_test.cc |
Adds large Conv correctness cases. |
onnxruntime/core/providers/webgpu/vendor/intel/math/subgroup_matrix_tiling_selector.h |
Uses shared subgroup definitions. |
onnxruntime/core/providers/webgpu/vendor/intel/math/subgroup_matrix_tiling_selector.cc |
Updates shared-header dependency. |
onnxruntime/core/providers/webgpu/subgroup_matrix_common.h |
Declares common configuration APIs. |
onnxruntime/core/providers/webgpu/subgroup_matrix_common.cc |
Implements device and tiling selection. |
onnxruntime/core/providers/webgpu/nn/subgroup_matrix_conv.h |
Declares optimized Conv factory. |
onnxruntime/core/providers/webgpu/nn/subgroup_matrix_conv.cc |
Implements Conv-to-MatMul routing. |
onnxruntime/core/providers/webgpu/nn/conv.h |
Adds optimized implementation state. |
onnxruntime/core/providers/webgpu/nn/conv.cc |
Invokes the subgroup path first. |
onnxruntime/core/providers/webgpu/math/subgroup_matrix_matmul.h |
Exposes shared dispatch and padding. |
onnxruntime/core/providers/webgpu/math/subgroup_matrix_matmul.cc |
Refactors reusable MatMul execution. |
onnxruntime/core/providers/webgpu/math/subgroup_matrix_gemm.cc |
Reuses common device selection. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| return [](const ComputeContext&, uint32_t /*M*/, uint32_t /*N*/, | ||
| uint32_t /*K*/, uint32_t /*batch*/) -> std::optional<SubgroupMatrixTiling> { | ||
| return SubgroupMatrixTiling{32, 32, 1}; | ||
| }; |
| const bool same_size = input_height == kernel_height && input_width == kernel_width && pads[0] == 0 && pads[1] == 0; | ||
| const bool is_conv_matmul = same_size || (kernel_height == 1 && kernel_width == 1 && pads[0] == 0 && pads[1] == 0 && strides[0] == 1 && strides[1] == 1); |
| RunConvTest<MLFloat16, version>(p); | ||
| } | ||
|
|
||
| TEST(Conv_Large, DISABLED_Conv1x1) { |
| // the MatMul, Gemm and Conv 1x1 factories. On a supported device sets config_index / | ||
| // tiling_selector and returns true; returns false (leaving the outputs untouched) when | ||
| // the device or vendor is unsupported, so the caller yields no implementation. |
| // (handled=false, allocating nothing) otherwise and we fall through to the normal | ||
| // path order below. | ||
| std::call_once(impl_init_flag_, [&]() { | ||
| impl_ = CreateSubgroupMatrixConvImpl(*this, context); |
There was a problem hiding this comment.
If subgroupMatrix is only used for 1x1 Conv, I would like to integrate SubgroupMatrix support into [a] ComputeMatMul instead of here. That's an unified entrance both for Conv and MatMul ops.
There was a problem hiding this comment.
Jiajia Qin (@qjia7), I will apply SubgroupMatrix to general Conv, #32252, will let these two PRs share some code. Furthermore, it would be inconvenient in many ways if integrate these code into ComputeMatMul, take the cached transposed tensor for an example.
| .AddUniformVariables({{output_size}, {static_cast<uint32_t>(matmul_output_shape[1])}, {static_cast<uint32_t>(matmul_output_shape[2])}, {static_cast<uint32_t>(K)}}); | ||
| return context.RunProgram(program); | ||
| } else { | ||
| return ComputeMatMul(&context, activation_, matmul_inputs, output, is_channels_last, matmul_input_reshapes[0], matmul_input_reshapes[1]); |
There was a problem hiding this comment.
[a] Please also integrate MatMulNaiveProgram into ComputeMatMul so that all point-wise conv just go to ComputeMatMul path. And in ComputeMatMul path, do all necessary optimizations. Conv itself doesn't need to take care what kind of algorithms it uses underlying.
|
#32304 has routed conv2d 1x1 to subgroup-matrix MatMul kernel. Close it. |
Description
Reuse the existing subgroup-matrix MatMul kernel for Conv2D on WebGPU.
Motivation and Context
While MatMul/Gemm already had a much faster subgroup-matrix (cooperative matrix) kernel with vendor-tuned tiling, so routing some convolution computation to the same kernel closes an easy performance gap without touching the general Conv path.