-
Notifications
You must be signed in to change notification settings - Fork 4.1k
webgpu: optimize Gemm and MatMul using subgroup feature #26433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
guschmue
merged 17 commits into
microsoft:main
from
xhcao:optimize_matmul_gemm_use_subgroup
Jan 27, 2026
Merged
Changes from 7 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
fe71863
webgpu: optimize Gemm and MatMul using subgroup feature
xhcao 28429df
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao f8166e5
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao 9afbc36
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao 452cb18
Address comments
xhcao eca1ce1
Merge remote-tracking branch 'upstream/main' into optimize_matmul_gem…
xhcao b5ec0ee
Fix code conflict
xhcao a185253
Address Jiajia's comments
xhcao 267f8af
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao 2182b8e
Tune parameters
xhcao 21d9ee7
support subgroup feature check
xhcao 6ff2b4e
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao 87ae8ed
Expand devices usage
xhcao aceb7c9
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao 13cb4a4
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao 79ff973
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao 57fa663
Merge branch 'main' into optimize_matmul_gemm_use_subgroup
xhcao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
onnxruntime/core/providers/webgpu/vendor/intel/math/gemm.cc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #include "core/providers/webgpu/vendor/intel/math/gemm.h" | ||
| #include "core/providers/webgpu/vendor/intel/math/gemm_subgroup.h" | ||
| #include "core/providers/webgpu/math/gemm_utils.h" | ||
|
|
||
| namespace onnxruntime { | ||
| namespace webgpu { | ||
| namespace intel { | ||
|
|
||
| Status GemmSubgroupProgram::GenerateShaderCode(ShaderHelper& shader) const { | ||
| const ShaderVariableHelper& output = shader.AddOutput("output", ShaderUsage::UseUniform | | ||
| ShaderUsage::UseValueTypeAlias | | ||
| ShaderUsage::UseElementTypeAlias); | ||
|
|
||
| if (need_handle_matmul_) { | ||
| const auto& a = shader.AddInput("a", ShaderUsage::UseUniform | ShaderUsage::UseIndicesTypeAlias | | ||
| ShaderUsage::UseValueTypeAlias | ShaderUsage::UseElementTypeAlias); | ||
| const auto& b = shader.AddInput("b", ShaderUsage::UseUniform | ShaderUsage::UseIndicesTypeAlias | | ||
| ShaderUsage::UseValueTypeAlias | ShaderUsage::UseElementTypeAlias); | ||
|
|
||
| MatMulReadFnSource(shader, a, b, nullptr, transA_, transB_, is_vec4_, true); | ||
| } | ||
|
|
||
| ORT_RETURN_IF_ERROR(MakeMatMulSubgroupSource(shader, elements_per_thread_, nullptr, is_vec4_, transA_, transB_, | ||
| alpha_, need_handle_matmul_)); | ||
| const ShaderVariableHelper* c = nullptr; | ||
| if (need_handle_bias_) { | ||
| c = &shader.AddInput("c", ShaderUsage::UseUniform); | ||
| } | ||
| MatMulWriteFnSource(shader, output, c, true, c_components_, output_components_, c_is_scalar_); | ||
|
|
||
| return Status::OK(); | ||
| } | ||
|
|
||
| bool CanApplyGemmIntel(const ComputeContext& context, int64_t M, int64_t N, int64_t K, bool transA, bool transB) { | ||
| return CanApplySubgroup(context, M, N, K, transA, transB); | ||
| } | ||
|
|
||
| Status ApplyGemmIntel(const Tensor* a, | ||
| const Tensor* b, | ||
| const Tensor* c, | ||
| bool transA, | ||
| bool transB, | ||
| float alpha, | ||
| float beta, | ||
| ComputeContext& context) { | ||
| const auto& a_shape = a->Shape(); | ||
| const auto& b_shape = b->Shape(); | ||
|
|
||
| uint32_t M = onnxruntime::narrow<uint32_t>(transA ? a_shape[1] : a_shape[0]); | ||
| uint32_t K = onnxruntime::narrow<uint32_t>(transA ? a_shape[0] : a_shape[1]); | ||
| uint32_t N = onnxruntime::narrow<uint32_t>(transB ? b_shape[0] : b_shape[1]); | ||
|
|
||
| std::vector<int64_t> output_dims{M, N}; | ||
| auto* y = context.Output(0, output_dims); | ||
| int64_t output_size = y->Shape().Size(); | ||
|
|
||
| if (output_size == 0) { | ||
| return Status::OK(); | ||
| } | ||
|
|
||
| // WebGPU doesn't support binding a zero-sized buffer, so we need to check if A or B is empty. | ||
| bool need_handle_matmul = a_shape.Size() > 0 && b_shape.Size() > 0; | ||
| bool need_handle_bias = c && beta; | ||
|
|
||
| const bool is_vec4 = b_shape[1] % 4 == 0; | ||
| // Components for A, B | ||
| int a_components = 1; | ||
| int b_components = is_vec4 ? 4 : 1; | ||
| // Components for Y | ||
| int output_components = (is_vec4 && N % 4 == 0) ? 4 : 1; | ||
| // Components for C. | ||
| int c_components = 1; | ||
|
|
||
| bool c_is_scalar = false; | ||
| if (need_handle_bias) { | ||
| const auto& c_shape = c->Shape(); | ||
| int64_t c_last_dim = c_shape[c_shape.NumDimensions() - 1]; | ||
| // `C` in GEMM might be broadcast to the output, and broadcasting requires the components to be consistent. | ||
| // So we use vec4 for C when its last dimension is N, and the output is also a vec4. | ||
| c_components = (c_last_dim == N && output_components == 4) ? 4 : 1; | ||
| c_is_scalar = c_shape.Size() == 1; | ||
| } | ||
|
|
||
| InlinedVector<int64_t> elements_per_thread = InlinedVector<int64_t>({4, intel::ElementsPerThreadY(is_vec4, M), 1}); | ||
| const uint32_t dispatch_x = narrow<uint32_t>((N + kSubgroupLogicalWorkGroupSizeX * elements_per_thread[0] - 1) / | ||
| (kSubgroupLogicalWorkGroupSizeX * elements_per_thread[0])); | ||
| const uint32_t dispatch_y = narrow<uint32_t>((M + kSubgroupLogicalWorkGroupSizeY * elements_per_thread[1] - 1) / | ||
| (kSubgroupLogicalWorkGroupSizeY * elements_per_thread[1])); | ||
|
|
||
| GemmSubgroupProgram program{transA, transB, alpha, need_handle_bias, need_handle_matmul, c_components, c_is_scalar, | ||
| output_components, is_vec4, elements_per_thread}; | ||
|
|
||
| if (need_handle_matmul) { | ||
| program.AddInputs({{a, ProgramTensorMetadataDependency::TypeAndRank, a_components}, | ||
| {b, ProgramTensorMetadataDependency::TypeAndRank, b_components}}); | ||
| } | ||
|
|
||
| if (need_handle_bias) { | ||
| program.AddInput({c, ProgramTensorMetadataDependency::TypeAndRank, c_components}); | ||
| } | ||
|
|
||
| program.CacheHint(alpha, transA, transB, c_is_scalar, is_vec4, absl::StrJoin(elements_per_thread, "-")) | ||
|
guschmue marked this conversation as resolved.
Outdated
|
||
| .AddOutputs({{y, ProgramTensorMetadataDependency::TypeAndRank, output_components}}) | ||
| .SetDispatchGroupSize(dispatch_x, dispatch_y, 1) | ||
| .SetWorkgroupSize(kSubgroupLogicalWorkGroupSizeX * kSubgroupLogicalWorkGroupSizeY, 1, 1) | ||
| .AddUniformVariables({{alpha}, | ||
| {beta}, | ||
| {M}, /* dim_a_outer */ | ||
| {N}, /* dim_b_outer */ | ||
| {K}} /*dim_inner */ | ||
| ); | ||
|
|
||
| return context.RunProgram(program); | ||
| } | ||
|
|
||
| } // namespace intel | ||
| } // namespace webgpu | ||
| } // namespace onnxruntime | ||
66 changes: 66 additions & 0 deletions
66
onnxruntime/core/providers/webgpu/vendor/intel/math/gemm.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "core/providers/webgpu/webgpu_kernel.h" | ||
| #include "core/providers/webgpu/shader_helper.h" | ||
| #include "core/providers/webgpu/program.h" | ||
|
|
||
| namespace onnxruntime { | ||
| namespace webgpu { | ||
| namespace intel { | ||
|
|
||
| class GemmSubgroupProgram final : public Program<GemmSubgroupProgram> { | ||
| public: | ||
| GemmSubgroupProgram(bool transA, bool transB, float alpha, bool need_handle_bias, bool need_handle_matmul, | ||
| int c_components, bool c_is_scalar, int output_components, bool is_vec4, | ||
| const gsl::span<int64_t>& elements_per_thread) | ||
| : Program{"GemmSubgroup"}, | ||
| transA_{transA}, | ||
| transB_{transB}, | ||
| alpha_{alpha}, | ||
| need_handle_bias_{need_handle_bias}, | ||
| need_handle_matmul_{need_handle_matmul}, | ||
| c_components_(c_components), | ||
| c_is_scalar_(c_is_scalar), | ||
| output_components_(output_components), | ||
| is_vec4_(is_vec4), | ||
| elements_per_thread_(elements_per_thread.begin(), elements_per_thread.end()) {} | ||
|
|
||
| Status GenerateShaderCode(ShaderHelper& sh) const override; | ||
|
|
||
| WEBGPU_PROGRAM_DEFINE_UNIFORM_VARIABLES( | ||
| {"alpha", ProgramUniformVariableDataType::Float32}, | ||
| {"beta", ProgramUniformVariableDataType::Float32}, | ||
| {"dim_a_outer", ProgramUniformVariableDataType::Uint32}, | ||
| {"dim_b_outer", ProgramUniformVariableDataType::Uint32}, | ||
| {"dim_inner", ProgramUniformVariableDataType::Uint32}); | ||
|
|
||
| private: | ||
| bool transA_; | ||
| bool transB_; | ||
| float alpha_; | ||
| bool need_handle_bias_; | ||
| bool need_handle_matmul_; | ||
| int c_components_; | ||
| bool c_is_scalar_ = false; | ||
| int output_components_; | ||
| bool is_vec4_ = false; | ||
| const InlinedVector<int64_t> elements_per_thread_; | ||
| }; | ||
|
|
||
| bool CanApplyGemmIntel(const ComputeContext& context, int64_t M, int64_t N, int64_t K, bool transA, bool transB); | ||
|
|
||
| Status ApplyGemmIntel(const Tensor* a, | ||
| const Tensor* b, | ||
| const Tensor* c, | ||
| bool transA, | ||
| bool transB, | ||
| float alpha, | ||
| float beta, | ||
| ComputeContext& context); | ||
|
|
||
| } // namespace intel | ||
| } // namespace webgpu | ||
| } // namespace onnxruntime |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.