Xenova HF Staff commited on
Commit
d1568b9
·
verified ·
1 Parent(s): 7dcaa8d

sync 91d990483a17

Browse files
README.md CHANGED
@@ -12,22 +12,22 @@ tags:
12
 
13
  ## Description
14
 
15
- Adds `bias` to `X`, splits the last dimension in half, then multiplies the left half elementwise by the GELU activation of the right half, producing an output with half the hidden dimension. Used as a fused activation in diffusion models.
16
 
17
  See the [ONNX Runtime `BiasSplitGelu` contrib-operator spec](https://github.com/microsoft/onnxruntime/blob/main/docs/ContribOperators.md#com.microsoft.BiasSplitGelu) for the reference semantics.
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
- | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `X` | `T` | `3` | — | Input tensor of shape `(N, S, D)`, where `N` is the batch size, `S` is the spatial size, and `D` is the hidden dimension. | required |
24
- | `bias` | `bias` | `T` | `1` | — | 1-D bias tensor of length D, matching the hidden dimension of X. | required |
25
 
26
  ## Outputs
27
 
28
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
29
- | --- | --- | --- | --- | --- | --- | --- |
30
- | `Y` | `Y` | `T` | `3` | derived; see description | Output tensor of shape `(N, S, D/2)` after the split-GELU activation. | required |
31
 
32
  ## Type constraints
33
 
@@ -37,7 +37,7 @@ See the [ONNX Runtime `BiasSplitGelu` contrib-operator spec](https://github.com/
37
 
38
  ## Files
39
 
40
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
41
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
42
  - [`test.json`](build/webgpu/test.json) — correctness cases
43
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -45,10 +45,14 @@ See the [ONNX Runtime `BiasSplitGelu` contrib-operator spec](https://github.com/
45
 
46
  ## Use with `@huggingface/kernels`
47
 
48
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
49
- It then allocates the result tensors automatically.
 
 
 
50
 
51
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
52
 
53
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
54
 
 
12
 
13
  ## Description
14
 
15
+ Adds `bias` to `X`, splits the last dimension in half, then multiplies the left half elementwise by the GELU activation of the right half, producing an output with half the hidden dimension.
16
 
17
  See the [ONNX Runtime `BiasSplitGelu` contrib-operator spec](https://github.com/microsoft/onnxruntime/blob/main/docs/ContribOperators.md#com.microsoft.BiasSplitGelu) for the reference semantics.
18
 
19
  ## Inputs
20
 
21
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- |
23
+ | `X` | `T` | `3` | — | Input tensor of shape `(N, S, D_in)`, where `N` is the batch size, `S` is the spatial size, and `D_in` is an even hidden dimension. | required |
24
+ | `bias` | `T` | `1` | — | 1-D bias tensor of length `D_in`, matching the input hidden dimension. | required |
25
 
26
  ## Outputs
27
 
28
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
29
+ | --- | --- | --- | --- | --- | --- |
30
+ | `Y` | `T` | `3` | derived | Output tensor of shape `(N, S, D_out)`, where `D_out = D_in / 2`. | required |
31
 
32
  ## Type constraints
33
 
 
37
 
38
  ## Files
39
 
40
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
41
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
42
  - [`test.json`](build/webgpu/test.json) — correctness cases
43
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
45
 
46
  ## Use with `@huggingface/kernels`
47
 
48
+ ```sh
49
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
50
+ ```
51
+
52
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
53
 
54
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
55
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
56
 
57
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
58
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "com.microsoft.BiasSplitGelu",
3
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
4
  "cases": [
5
  {
 
1
  {
 
2
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
3
  "cases": [
4
  {
build/webgpu/bias-split-gelu.wgsl.jinja CHANGED
@@ -1,56 +1,51 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
34
- {% if usesF16 %}
35
- enable f16;
36
- {% endif %}
37
  {{ env.wgsl.resourceDeclarations }}
38
 
39
  // com.microsoft.BiasSplitGelu GEGLU gate:
40
- // The innermost (hidden) axis of (X + bias) has size 2*D. Split it into
41
- // value = [.., 0:D] and gate = [.., D:2D], then
42
- // Y[.., d] = value[.., d] * gelu_erf(gate[.., d])
43
  // gelu_erf(g) = 0.5 * g * (1 + erf(g / sqrt2)).
44
- // bias is a rank-1 vector [2*D] broadcast over the hidden axis. The output
45
- // hidden axis is D (= HIDDEN below); the input hidden axis is 2*D (TWICE).
46
  // erf_approx uses an overflow-safe rational approximation.
47
  fn erf_approx(x: f32) -> f32 {
48
  let ax = abs(x);
49
  // The polynomial has a small nonzero floor near zero. Use erf(x) ~=
50
  // 2/sqrt(pi)*x below 2^-20 to preserve erf(0) == 0, odd symmetry, and the
51
- // correctly rounded f32 result. The exactly representable threshold keeps
52
- // scalar and vector branching identical. NaN falls through to the polynomial
53
- // and propagates.
54
  if (ax < 9.5367431640625e-7) {
55
  return 1.1283791670955126 * x;
56
  }
@@ -64,14 +59,14 @@ fn gelu_value(v: f32) -> f32 {
64
  return 0.5 * v * (1.0 + erf_approx(v * 0.7071067811865476));
65
  }
66
 
67
- const HIDDEN: u32 = {{ hidden }}u; // D: output hidden axis
68
- const TWICE: u32 = {{ twice }}u; // 2*D: input hidden axis
69
  {% if vec4Tail %}
70
  const GROUPS_PER_ROW: u32 = (HIDDEN + 3u) / 4u;
71
 
72
  {% endif %}
73
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
74
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
75
  {{ flat_index_2d() }}
76
  {% if vec4Tail %}
77
  let row = i / GROUPS_PER_ROW;
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
 
 
 
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  // com.microsoft.BiasSplitGelu GEGLU gate:
37
+ // The innermost axis of (X + bias) has size D_in = 2*D_out. Split it into
38
+ // value = [.., 0:D_out] and gate = [.., D_out:D_in], then
39
+ // Y[.., d] = value[.., d] * gelu_erf(gate[.., d]).
40
  // gelu_erf(g) = 0.5 * g * (1 + erf(g / sqrt2)).
41
+ // `bias` is a rank-1 vector of length D_in broadcast over the hidden axis.
 
42
  // erf_approx uses an overflow-safe rational approximation.
43
  fn erf_approx(x: f32) -> f32 {
44
  let ax = abs(x);
45
  // The polynomial has a small nonzero floor near zero. Use erf(x) ~=
46
  // 2/sqrt(pi)*x below 2^-20 to preserve erf(0) == 0, odd symmetry, and the
47
+ // small-input linear behavior. The threshold is exactly representable in
48
+ // f32. NaN falls through to the polynomial and propagates.
 
49
  if (ax < 9.5367431640625e-7) {
50
  return 1.1283791670955126 * x;
51
  }
 
59
  return 0.5 * v * (1.0 + erf_approx(v * 0.7071067811865476));
60
  }
61
 
62
+ const HIDDEN: u32 = {{ hidden }}u; // D_out
63
+ const TWICE: u32 = {{ twice }}u; // D_in
64
  {% if vec4Tail %}
65
  const GROUPS_PER_ROW: u32 = (HIDDEN + 3u) / 4u;
66
 
67
  {% endif %}
68
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
69
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
70
  {{ flat_index_2d() }}
71
  {% if vec4Tail %}
72
  let row = i / GROUPS_PER_ROW;
build/webgpu/manifest.json CHANGED
@@ -2,45 +2,40 @@
2
  "domain": "com.microsoft",
3
  "name": "BiasSplitGelu",
4
  "sinceVersion": 1,
5
- "description": "Adds `bias` to `X`, splits the last dimension in half, then multiplies the left half elementwise by the GELU activation of the right half, producing an output with half the hidden dimension. Used as a fused activation in diffusion models.",
6
- "inputs": [
7
- {
8
- "role": "X",
9
- "dtype": "T",
10
- "rank": 3,
11
- "description": "Input tensor of shape `(N, S, D)`, where `N` is the batch size, `S` is the spatial size, and `D` is the hidden dimension."
 
 
 
 
 
 
 
 
 
 
 
 
12
  },
13
- {
14
- "role": "bias",
15
- "dtype": "T",
16
- "rank": 1,
17
- "description": "1-D bias tensor of length D, matching the hidden dimension of X."
18
- }
19
- ],
20
- "outputs": [
21
- {
22
- "role": "Y",
23
- "dtype": "T",
24
- "rank": 3,
25
- "shape": "[dim(shapes.X, 0), dim(shapes.X, 1), dim(shapes.X, 2) / 2]",
26
- "description": "Output tensor of shape `(N, S, D/2)` after the split-GELU activation."
27
  }
28
- ],
29
- "typeConstraints": { "T": ["float32", "float16"] },
30
- "args": {
31
- "X": { "kind": "tensor", "semantic": "X", "role": "input" },
32
- "bias": { "kind": "tensor", "semantic": "bias", "role": "input" },
33
- "Y": { "kind": "tensor", "semantic": "Y", "role": "output" }
34
  },
35
- "tunables": { "WORKGROUP_SIZE": 64 },
36
  "variants": [
37
  {
38
  "id": "vec4",
39
  "priority": 30,
40
- "when": ["ranks.X == 3", "ranks.Y == 3", "dim(shapes.Y, 0) == dim(shapes.X, 0)", "dim(shapes.Y, 1) == dim(shapes.X, 1)", "dim(shapes.X, 2) % 2 == 0", "dim(shapes.Y, 2) == dim(shapes.X, 2) / 2", "f16Ok(dtypes.T)", "ranks.bias == 1", "dim(shapes.bias, 0) == dim(shapes.X, 2)", "numel(shapes.Y) > 0", "numel(shapes.Y) % 4 == 0", "dim(shapes.Y, 2) % 4 == 0"],
41
- "constants": {
42
  "scalar": "dtypes.T",
43
- "usesF16": "dtypes.T == \"f16\"",
44
  "vec4": true,
45
  "vec4Tail": false,
46
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
@@ -53,49 +48,25 @@
53
  "name": "BiasSplitGelu.vec4",
54
  "shader": "bias-split-gelu.wgsl.jinja",
55
  "bindings": [
56
- {
57
- "name": "x",
58
- "arg": "X",
59
- "semantic": "X",
60
- "buffer": { "type": "read-only-storage" },
61
- "elementType": "$scalar"
62
- },
63
- {
64
- "name": "bias",
65
- "arg": "bias",
66
- "semantic": "bias",
67
- "buffer": { "type": "read-only-storage" },
68
- "elementType": "$scalar",
69
- "length": "$twice"
70
- },
71
- {
72
- "name": "y",
73
- "arg": "Y",
74
- "semantic": "Y",
75
- "buffer": { "type": "storage" },
76
- "elementType": "$vectorScalar"
77
- },
78
- {
79
- "name": "params",
80
- "semantic": "kernel.params",
81
- "buffer": { "type": "uniform" },
82
- "struct": {
83
- "name": "Params",
84
- "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.Y) / 4" }]
85
- }
86
- }
87
  ],
88
- "dispatch": { "threads": "numel(shapes.Y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
89
  }
90
  ]
91
  },
92
  {
93
  "id": "vec4_tail",
94
  "priority": 20,
95
- "when": ["ranks.X == 3", "ranks.Y == 3", "dim(shapes.Y, 0) == dim(shapes.X, 0)", "dim(shapes.Y, 1) == dim(shapes.X, 1)", "dim(shapes.X, 2) % 2 == 0", "dim(shapes.Y, 2) == dim(shapes.X, 2) / 2", "f16Ok(dtypes.T)", "ranks.bias == 1", "dim(shapes.bias, 0) == dim(shapes.X, 2)", "numel(shapes.Y) > 0"],
96
- "constants": {
97
  "scalar": "dtypes.T",
98
- "usesF16": "dtypes.T == \"f16\"",
99
  "vec4": false,
100
  "vec4Tail": true,
101
  "hidden": "dim(shapes.X, ranks.X - 1) / 2 if dim(shapes.X, ranks.X - 1) > 0 else 1",
@@ -106,42 +77,11 @@
106
  "id": "main",
107
  "name": "BiasSplitGelu.vec4Tail",
108
  "shader": "bias-split-gelu.wgsl.jinja",
109
- "bindings": [
110
- {
111
- "name": "x",
112
- "arg": "X",
113
- "semantic": "X",
114
- "buffer": { "type": "read-only-storage" },
115
- "elementType": "$scalar"
116
- },
117
- {
118
- "name": "bias",
119
- "arg": "bias",
120
- "semantic": "bias",
121
- "buffer": { "type": "read-only-storage" },
122
- "elementType": "$scalar",
123
- "length": "$twice"
124
- },
125
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
126
- {
127
- "name": "params",
128
- "semantic": "kernel.params",
129
- "buffer": { "type": "uniform" },
130
- "struct": {
131
- "name": "Params",
132
- "fields": [
133
- {
134
- "name": "count",
135
- "type": "u32",
136
- "value": "(numel(shapes.Y) / (dim(shapes.X, ranks.X - 1) / 2)) * ceilDiv(dim(shapes.X, ranks.X - 1) / 2, 4)"
137
- }
138
- ]
139
- }
140
- }
141
- ],
142
  "dispatch": {
143
- "threads": "(numel(shapes.Y) / (dim(shapes.X, ranks.X - 1) / 2)) * ceilDiv(dim(shapes.X, ranks.X - 1) / 2, 4)",
144
- "workgroupSize": "tunables.WORKGROUP_SIZE"
 
145
  }
146
  }
147
  ]
@@ -149,10 +89,9 @@
149
  {
150
  "id": "scalar",
151
  "priority": 0,
152
- "when": ["ranks.X == 3", "ranks.Y == 3", "dim(shapes.Y, 0) == dim(shapes.X, 0)", "dim(shapes.Y, 1) == dim(shapes.X, 1)", "dim(shapes.X, 2) % 2 == 0", "dim(shapes.Y, 2) == dim(shapes.X, 2) / 2", "f16Ok(dtypes.T)", "ranks.bias == 1", "dim(shapes.bias, 0) == dim(shapes.X, 2)", "true"],
153
- "constants": {
154
  "scalar": "dtypes.T",
155
- "usesF16": "dtypes.T == \"f16\"",
156
  "vec4": false,
157
  "vec4Tail": false,
158
  "hidden": "dim(shapes.X, ranks.X - 1) / 2 if dim(shapes.X, ranks.X - 1) > 0 else 1",
@@ -163,30 +102,12 @@
163
  "id": "main",
164
  "name": "BiasSplitGelu.scalar",
165
  "shader": "bias-split-gelu.wgsl.jinja",
166
- "bindings": [
167
- {
168
- "name": "x",
169
- "arg": "X",
170
- "semantic": "X",
171
- "buffer": { "type": "read-only-storage" },
172
- "elementType": "$scalar"
173
- },
174
- {
175
- "name": "bias",
176
- "arg": "bias",
177
- "semantic": "bias",
178
- "buffer": { "type": "read-only-storage" },
179
- "elementType": "$scalar"
180
- },
181
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
182
- {
183
- "name": "params",
184
- "semantic": "kernel.params",
185
- "buffer": { "type": "uniform" },
186
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.Y)" }] }
187
- }
188
- ],
189
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
190
  }
191
  ]
192
  }
 
2
  "domain": "com.microsoft",
3
  "name": "BiasSplitGelu",
4
  "sinceVersion": 1,
5
+ "inputs": { "X": { "dtype": "T", "rank": 3 }, "bias": { "dtype": "T", "rank": 1 } },
6
+ "outputs": { "Y": { "dtype": "T", "rank": 3, "shape": "[dim(shapes.X, 0), dim(shapes.X, 1), dim(shapes.X, 2) / 2]" } },
7
+ "typeConstraints": { "T": ["float32", "float16"] },
8
+ "tunables": { "WORKGROUP_SIZE": { "default": 64 } },
9
+ "when": ["ranks.X == 3", "ranks.Y == 3", "dim(shapes.Y, 0) == dim(shapes.X, 0)", "dim(shapes.Y, 1) == dim(shapes.X, 1)", "dim(shapes.X, 2) % 2 == 0", "dim(shapes.Y, 2) == dim(shapes.X, 2) / 2", "f16Ok(dtypes.T)", "ranks.bias == 1", "dim(shapes.bias, 0) == dim(shapes.X, 2)"],
10
+ "bindings": {
11
+ "x": { "arg": "X", "buffer": "read-only-storage", "elementType": "$scalar" },
12
+ "bias": { "buffer": "read-only-storage", "elementType": "$scalar", "length": "$twice" },
13
+ "y_2": { "arg": "Y", "name": "y", "buffer": "storage", "elementType": "$scalar" },
14
+ "params_2": {
15
+ "name": "params",
16
+ "buffer": "uniform",
17
+ "struct": [
18
+ {
19
+ "name": "count",
20
+ "type": "u32",
21
+ "value": "(numel(shapes.Y) / (dim(shapes.X, ranks.X - 1) / 2)) * ceilDiv(dim(shapes.X, ranks.X - 1) / 2, 4)"
22
+ }
23
+ ]
24
  },
25
+ "bias_2": { "name": "bias", "buffer": "read-only-storage", "elementType": "$scalar" },
26
+ "params_3": {
27
+ "name": "params",
28
+ "buffer": "uniform",
29
+ "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.Y)" }]
 
 
 
 
 
 
 
 
 
30
  }
 
 
 
 
 
 
31
  },
 
32
  "variants": [
33
  {
34
  "id": "vec4",
35
  "priority": 30,
36
+ "when": ["numel(shapes.Y) > 0", "numel(shapes.Y) % 4 == 0", "dim(shapes.Y, 2) % 4 == 0"],
37
+ "derive": {
38
  "scalar": "dtypes.T",
 
39
  "vec4": true,
40
  "vec4Tail": false,
41
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
 
48
  "name": "BiasSplitGelu.vec4",
49
  "shader": "bias-split-gelu.wgsl.jinja",
50
  "bindings": [
51
+ "x",
52
+ "bias",
53
+ { "arg": "Y", "name": "y", "elementType": "$vectorScalar" },
54
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.Y) / 4" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  ],
56
+ "dispatch": {
57
+ "x": "min(ceilDiv((numel(shapes.Y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
58
+ "y": "ceilDiv(ceilDiv((numel(shapes.Y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
59
+ "z": 1
60
+ }
61
  }
62
  ]
63
  },
64
  {
65
  "id": "vec4_tail",
66
  "priority": 20,
67
+ "when": ["numel(shapes.Y) > 0"],
68
+ "derive": {
69
  "scalar": "dtypes.T",
 
70
  "vec4": false,
71
  "vec4Tail": true,
72
  "hidden": "dim(shapes.X, ranks.X - 1) / 2 if dim(shapes.X, ranks.X - 1) > 0 else 1",
 
77
  "id": "main",
78
  "name": "BiasSplitGelu.vec4Tail",
79
  "shader": "bias-split-gelu.wgsl.jinja",
80
+ "bindings": ["x", "bias", "y_2", "params_2"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  "dispatch": {
82
+ "x": "min(ceilDiv(((numel(shapes.Y) / (dim(shapes.X, ranks.X - 1) / 2)) * ceilDiv(dim(shapes.X, ranks.X - 1) / 2, 4)), (tunables.WORKGROUP_SIZE)), 65535)",
83
+ "y": "ceilDiv(ceilDiv(((numel(shapes.Y) / (dim(shapes.X, ranks.X - 1) / 2)) * ceilDiv(dim(shapes.X, ranks.X - 1) / 2, 4)), (tunables.WORKGROUP_SIZE)), 65535)",
84
+ "z": 1
85
  }
86
  }
87
  ]
 
89
  {
90
  "id": "scalar",
91
  "priority": 0,
92
+ "when": ["true"],
93
+ "derive": {
94
  "scalar": "dtypes.T",
 
95
  "vec4": false,
96
  "vec4Tail": false,
97
  "hidden": "dim(shapes.X, ranks.X - 1) / 2 if dim(shapes.X, ranks.X - 1) > 0 else 1",
 
102
  "id": "main",
103
  "name": "BiasSplitGelu.scalar",
104
  "shader": "bias-split-gelu.wgsl.jinja",
105
+ "bindings": ["x", "bias_2", "y_2", "params_3"],
106
+ "dispatch": {
107
+ "x": "min(ceilDiv((numel(shapes.Y)), (tunables.WORKGROUP_SIZE)), 65535)",
108
+ "y": "ceilDiv(ceilDiv((numel(shapes.Y)), (tunables.WORKGROUP_SIZE)), 65535)",
109
+ "z": 1
110
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  }
112
  ]
113
  }
build/webgpu/metadata.json CHANGED
@@ -1,18 +1,25 @@
1
  {
2
  "name": "com.microsoft.BiasSplitGelu",
3
- "id": "_com_microsoft_biassplitgelu_webgpu_e56edec",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "oYxkYz1Ukonqiav8OJSz76L0uidTkGg049tpmfnC0Ms=",
11
- "bias-split-gelu.wgsl.jinja": "YGnskVbzCTa0y/SN+O6Wq0Yp8SWpUav6Q930/EHEvsw=",
12
- "manifest.json": "i40t5ZCHmqMoQrSWSWZlC3X/ERSmyoa5z4PkrzsWP/w=",
13
- "test.json": "JVElcOkuNl49j2nfKWBgotONhDTNVT7ZbWTDW+5Of/Q="
14
  }
15
  },
16
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
17
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.BiasSplitGelu" }
 
 
 
 
 
 
 
18
  }
 
1
  {
2
  "name": "com.microsoft.BiasSplitGelu",
3
+ "id": "_com_microsoft_biassplitgelu_webgpu_c7117c6",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "9D5pXr6GQoiKWOaIZonasPZV750s7EBoeCc18Fjh6LE=",
11
+ "bias-split-gelu.wgsl.jinja": "NdmUm+O7ZCH0nHgT9xuUVfsMcX0TKQLoz2edprOP4Eg=",
12
+ "manifest.json": "RhauSBIjgU0p1ri0W74BHIABvKxAMnSg9MfBB++0c0E=",
13
+ "test.json": "jeqt2QA6oOrulPiXyZ1nXe7lvFKPg1me9DSnpzLx7lk="
14
  }
15
  },
16
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
17
+ "webgpu": {
18
+ "manifestSpec": "2.0",
19
+ "variants": {
20
+ "vec4": ["bias-split-gelu.wgsl.jinja"],
21
+ "vec4_tail": ["bias-split-gelu.wgsl.jinja"],
22
+ "scalar": ["bias-split-gelu.wgsl.jinja"]
23
+ }
24
+ }
25
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "com.microsoft.BiasSplitGelu",
3
  "cases": [
4
  {
5
  "name": "dispatch_cliff_vec4_over_16M",
@@ -47,7 +46,7 @@
47
  "provenance": {
48
  "source": "onnxruntime/test/contrib_ops/bias_split_gelu_op_test.cc",
49
  "test": "BiasSplitGeluTest.BiasSplitGeluTest_HiddenSize_5120",
50
- "notes": "Compact deterministic projection of ORT's batch=2, sequence_length=1 shape coverage."
51
  },
52
  "inputs": {
53
  "X": {
@@ -79,7 +78,7 @@
79
  "provenance": {
80
  "source": "onnxruntime/test/contrib_ops/bias_split_gelu_op_test.cc",
81
  "test": "BiasSplitGeluTest.BiasSplitGeluTest_HiddenSize_10240",
82
- "notes": "Compact deterministic projection of ORT's batch=1, sequence_length=2 shape coverage."
83
  },
84
  "inputs": {
85
  "X": {
@@ -111,7 +110,7 @@
111
  "provenance": {
112
  "source": "onnxruntime/test/contrib_ops/bias_split_gelu_op_test.cc",
113
  "test": "BiasSplitGeluTest.BiasSplitGeluTest_HiddenSize_6144",
114
- "notes": "Compact deterministic projection of ORT's batch=2, sequence_length=3 shape coverage with output hidden divisible by 4."
115
  },
116
  "inputs": {
117
  "X": {
@@ -228,7 +227,7 @@
228
  {
229
  "name": "scalar_path_dispatch_fold_over_16M",
230
  "provenance": {
231
- "notes": "D=6 (twice=12, 6%4!=0) forces the scalar variant; numel(Y)=16776966 > 65535*256 forces the scalar-branch 2D dispatch fold (gid.y high bits), an untested path (dispatch_cliff_vec4_over_16M covers only the vec4 branch). Validates the i=gid.x+gid.y*nwg.x*WG reconstruction and i>=count tail guard on the last rows."
232
  },
233
  "inputs": {
234
  "X": {
@@ -243,7 +242,7 @@
243
  {
244
  "name": "scalar_path_odd_hidden_d6",
245
  "provenance": {
246
- "notes": "twice=12, D=6 (6%4!=0) routes to the scalar variant; small case verifying scalar split-index math (value half [0:6], gate half [6:12]) at a hidden size not covered by existing D=3 scalar cases. Reference supplies truth."
247
  },
248
  "inputs": {
249
  "X": {
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "dispatch_cliff_vec4_over_16M",
 
46
  "provenance": {
47
  "source": "onnxruntime/test/contrib_ops/bias_split_gelu_op_test.cc",
48
  "test": "BiasSplitGeluTest.BiasSplitGeluTest_HiddenSize_5120",
49
+ "notes": "A compact deterministic ORT case with batch 2 and sequence length 1."
50
  },
51
  "inputs": {
52
  "X": {
 
78
  "provenance": {
79
  "source": "onnxruntime/test/contrib_ops/bias_split_gelu_op_test.cc",
80
  "test": "BiasSplitGeluTest.BiasSplitGeluTest_HiddenSize_10240",
81
+ "notes": "A compact deterministic ORT case with batch 1 and sequence length 2."
82
  },
83
  "inputs": {
84
  "X": {
 
110
  "provenance": {
111
  "source": "onnxruntime/test/contrib_ops/bias_split_gelu_op_test.cc",
112
  "test": "BiasSplitGeluTest.BiasSplitGeluTest_HiddenSize_6144",
113
+ "notes": "A compact deterministic ORT case with batch 2, sequence length 3, and output width divisible by four."
114
  },
115
  "inputs": {
116
  "X": {
 
227
  {
228
  "name": "scalar_path_dispatch_fold_over_16M",
229
  "provenance": {
230
+ "notes": "Output width 6 is not divisible by four, selecting the scalar variant. With 16,776,966 output elements, the two-dimensional dispatch fold must reconstruct the linear index from both grid axes and guard the final partial row."
231
  },
232
  "inputs": {
233
  "X": {
 
242
  {
243
  "name": "scalar_path_odd_hidden_d6",
244
  "provenance": {
245
+ "notes": "With input width 12 and output width 6, the unaligned output uses scalar indexing. Values [0:6] form the value half and [6:12] form the gate half."
246
  },
247
  "inputs": {
248
  "X": {