Skip to content

[WebGPU] Add transpose-optimizer handlers for Elu and the contrib GELU variants - #32118

Open
Ananya Anand (4n4ny4) wants to merge 3 commits into
microsoft:mainfrom
4n4ny4:webgpu-contrib-gelu-nhwc-reachability
Open

[WebGPU] Add transpose-optimizer handlers for Elu and the contrib GELU variants#32118
Ananya Anand (4n4ny4) wants to merge 3 commits into
microsoft:mainfrom
4n4ny4:webgpu-contrib-gelu-nhwc-reachability

Conversation

@4n4ny4

@4n4ny4 Ananya Anand (4n4ny4) commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Description

OrtExtendedHandlers() had no entries for com.microsoft.Gelu, FastGelu and QuickGelu, and the ONNX map was missing Elu, so layout propagation stopped at the activation and left a transpose wedged between the NHWC Conv and the activation where ConvActivationFusion never saw them adjacent. Results stayed correct, the fusion just silently did not fire. FastGelu needs a bespoke handler rather than a table entry because its optional bias is pinned to the last dimension, and broadcast_node_handler would rank-normalize it into a rank-4 bias that fails bias_gelu_helper::CheckInputs outright, so the handler pushes the transpose only when there is no bias. Elu lives here rather than in #32117 because the transpose optimizer is shared cross-EP infrastructure, not WebGPU code. This also registers FastGeluFusion for WebGPU, which was cpu, cuda and dml only, since GeluFusion and BiasGeluFusion already landed in #32053. Covered by 6 fusion tests and 5 transpose-optimizer tests, including a negative test that FastGelu with a bias is left alone.

Motivation and Context

Models often already contain contrib GELU nodes, since QuickGeluFusion and GeluFusion run before layout transformation, so without these handlers the Conv activation fusion never fires on exactly the models most likely to benefit. #32117 adds the WebGPU side of the same work and needs these handlers to fuse end to end.

Copilot AI balanced review requested due to automatic review settings August 17, 2026 00:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 34 changed files in this pull request and generated no new comments.

Suppressed comments (4)

onnxruntime/core/optimizer/conv_activation_fusion.cc:210

  • QuickGelu's schema default is alpha = 1.702f (core/graph/contrib_ops/contrib_defs.cc:624), but an omitted attribute is encoded here as 1.0. A valid com.microsoft.QuickGelu node without an explicit alpha will therefore be fused as SiLU and produce different results.
      activation_params.push_back(alpha_attr == nullptr ? 1.0f : alpha_attr->f());

onnxruntime/core/optimizer/conv_activation_fusion.cc:108

  • An explicitly empty optional bias input is semantically absent, but it still leaves two entries in InputDefs(). This check rejects FastGelu(X, "") even though the new transpose handler correctly treats that form as elementwise, so layout propagation succeeds but the intended Conv fusion still does not happen. Check Exists() for the optional slot, as other optimizer code does.

This issue also appears on line 210 of the same file.

        return activation_node.InputDefs().size() == 1;

onnxruntime/test/providers/webgpu/activation_snippet_test.cc:6

  • std::setprecision is declared by <iomanip>, which this new test does not include. Depending on transitive includes from gtest makes this translation unit non-portable and can fail to compile when those headers change.
#include <limits>

onnxruntime/core/providers/webgpu/nn/fuse_utils.cc:22

  • std::ostringstream honors the process locale, so after an application installs a locale with a comma decimal separator this can emit WGSL such as 0,707..., causing shader compilation to fail. Shader literals must be locale-independent; imbue the classic locale before formatting.
std::string FloatLiteral(float value) {
  std::ostringstream oss;
  oss << std::setprecision(std::numeric_limits<float>::max_digits10) << value;
  return oss.str();

@4n4ny4
Ananya Anand (4n4ny4) force-pushed the webgpu-contrib-gelu-nhwc-reachability branch from 8c5c5ec to 78c472e Compare August 20, 2026 21:23
@4n4ny4 Ananya Anand (4n4ny4) changed the title [WebGPU] Keep contrib GELU variants reachable for NHWC Conv fusion [WebGPU] Add transpose-optimizer handlers for Elu and the contrib GELU variants Aug 20, 2026
@4n4ny4
Ananya Anand (4n4ny4) force-pushed the webgpu-contrib-gelu-nhwc-reachability branch 2 times, most recently from 365dbc6 to b4ee98c Compare August 23, 2026 06:25
@hariharans29

Copy link
Copy Markdown
Member

Can you please rebase with main ? For some reason, the 2 CI checks are not queueing. Hopefully a rebase fixes that.

Hariharan Seshadri (hariharans29) pushed a commit that referenced this pull request Aug 30, 2026
### Description

Extends the WebGPU Conv+activation fusion allowlist by eight kinds:
QuickGelu, HardSwish, Elu, Gelu, Gelu(tanh), Softplus, ThresholdedRelu
and Erf, each with a WGSL snippet for the generated-shader path and a
matching branch in the im2col template. QuickGelu at alpha == 1 is a
distinct shader because the multiply drops out entirely, so it carries
its own `QuickGeluUnitAlpha` cache-key term to stop the two variants
colliding in the pipeline cache. This also fixes the QuickGelu alpha
fallback, which was `1.0f` rather than the schema default `1.702f`:
attributes are materialized onto nodes during `Graph::Resolve()`, so the
fallback was unreachable and could not change model output, but it was
still wrong on paper and inconsistent with the standalone WebGPU
QuickGelu kernel. Four of the eight (`Elu` and the three contrib GELU
variants) also need a transpose-optimizer handler to fuse end to end,
and since that map is shared cross-EP infrastructure rather than WebGPU
code it lives in #32118; until that lands those four still execute
correctly, just unfused. Covered by 35 new tests, including negative
controls for Selu and the CPU EP, execution parity against unfused
results, and one that strips QuickGelu's alpha after `Resolve()` so it
actually fails without the fix.

### Motivation and Context

These activations are common after convolutions. QuickGelu is how
SiLU/Swish reaches the graph and HardSwish appears throughout
MobileNet-class models, but each one previously forced a separate
dispatch and a round trip through global memory.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Ananya Anand <t-anaanand@microsoft.com>
Co-authored-by: Ananya Anand <4n4ny4@users.noreply.github.com>
Ananya Anand (4n4ny4) and others added 3 commits August 30, 2026 17:26
Models that already contain com.microsoft.Gelu, FastGelu or QuickGelu after a Conv never fused: layout propagation stopped at the activation, leaving a transpose wedged between the internal-NHWC Conv and the GELU, so ConvActivationFusion never saw them adjacent. Add layout handlers for the three contrib ops, plus ONNX Elu, which has the same gap.

The Elu handler previously arrived here by inheritance from the WebGPU Conv activation PR this branch stacks on. That PR no longer carries it, because the transpose optimizer is shared cross-EP infrastructure rather than WebGPU code, so this commit owns the line outright. Inheritance was fragile: rebasing this branch after that PR dropped the line would have silently removed the handler with no conflict and no error.

FastGeluFusion was also registered for cpu+cuda+dml only, so the tanh-GELU decomposition emitted by pre-opset-20 exporters never became com.microsoft.FastGelu on WebGPU. Register it for WebGPU too. WebGPU implements all three contrib kernels, so nothing is stranded on CPU.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit ac4e398)
The handlers added here had no coverage at the transpose-optimizer layer. These build Transpose -> activation -> Transpose and assert the transposes cancel, which only happens if the optimizer actually pushed one through the activation. TransformerTester also runs each model and compares against the un-optimized baseline, so a handler that moved the Transpose but changed the maths fails too.

Includes the case the FastGelu handler exists for: with a bias present the Transpose must NOT move, because bias_gelu_helper::CheckInputs requires a rank-1 bias whose length equals the last dimension of input 0, and a layout permutation moves a different axis into that position.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit efcb879)
WebGpuConvEluFusionMatchesUnfusedResults needs the Elu transpose handler
that this PR adds (onnx_transpose_optimization.cc), so it belongs here
rather than in the PR below, where it can only fail. This re-adds it
after the parent PR dropped it, matching the rule already used on this
stack: a test that needs two layers lives at the top layer.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit b4ee98c)
@4n4ny4

Ananya Anand (4n4ny4) commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main. #32117 merged in the meantime, so the eight commits this was stacked on are gone. The push dismissed your approval, could you re-approve? Thank you!

@4n4ny4
Ananya Anand (4n4ny4) enabled auto-merge (squash) August 31, 2026 04:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants