Xenova HF Staff commited on
Commit
4eec196
·
verified ·
1 Parent(s): a5f52d3

sync 6fdf6301e2bb

Browse files
README.md CHANGED
@@ -24,7 +24,7 @@ See the [ONNX Runtime `VarlenCausalConvWithState` contrib-operator spec](https:/
24
  | `weightT` | `weight` | `T` | same as logical dtype | `3` | — | Depthwise kernel with shape `(channels, 1, kernel_size)`. | required |
25
  | `cumulativeSequenceLengthT` | `cumulative_sequence_length` | `M` | `int32` | `1` | — | Exclusive prefix sums with shape `(batch_size + 1)`, starting at 0, ending at `total_tokens`, and strictly increasing so every sequence is non-empty. Sequence `i` owns tokens `[cum[i], cum[i + 1])`. Outputs are unspecified for a malformed schedule. | required |
26
  | `biasT` | `bias` | `T` | same as logical dtype | `1` | — | Optional per-channel bias with shape `(channels,)`. In an ONNX graph an omitted bias must still occupy input index 3 as an empty name so `initial_state` stays at index 4. | optional |
27
- | `initialStateT` | `initial_state` | `T` | same as logical dtype | `3` | — | Required committed carry state with shape `(batch_size, channels, kernel_size - 1)`, holding the raw samples immediately preceding this call. | required |
28
  | `captureCountT` | `capture_count` | `M` | `int32` | `1` | — | Optional int32 vector with shape `(batch_size)`. Required exactly when `state_update_capacity` is positive; each value is clamped to `[0, min(state_update_capacity, sequence_length)]`. | optional |
29
 
30
  ## Outputs
@@ -32,7 +32,7 @@ See the [ONNX Runtime `VarlenCausalConvWithState` contrib-operator spec](https:/
32
  | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
33
  | --- | --- | --- | --- | --- | --- | --- |
34
  | `outputT` | `output` | `T` | same as `inputT` | same as `inputT` | Convolution output with the same shape as `input`. | required |
35
- | `finalStateT` | `final_state` | `T` | `3` | derived | State after each sequence's final token, shape `(batch_size, channels, kernel_size - 1)`. Always fully written. | required |
36
  | `stateUpdateT` | `state_update` | `T` | `3` | derived | Optional compact transition values with shape `(batch_size, state_update_capacity, channels)`. Active slots contain the original local input tokens and all other slots are zero. | optional |
37
 
38
  ## Attributes
@@ -42,6 +42,7 @@ Default values (overridable per request):
42
  | Attribute | Default | Description |
43
  | --- | --- | --- |
44
  | `activation` | `"none"` | Fused activation applied after convolution and bias. One of `none`, `silu`, or `swish`; the standard default is `none`. |
 
45
  | `state_update_capacity` | `0` | Static number of compact per-request prefix transition values to expose, in `[0, 8]`. The standard default is 0. |
46
 
47
  ## Type constraints
@@ -51,20 +52,34 @@ Default values (overridable per request):
51
  | `T` | `float32`, `float16` |
52
  | `M` | `int32` |
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  ## Files
55
 
56
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
57
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
58
  - [`test.json`](build/webgpu/test.json) — correctness cases
59
- - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
60
  - [`varlen-causal-conv-stream.wgsl.jinja`](build/webgpu/varlen-causal-conv-stream.wgsl.jinja)
61
  - [`varlen-causal-conv.wgsl.jinja`](build/webgpu/varlen-causal-conv.wgsl.jinja)
62
  - [`varlen-state-update.wgsl.jinja`](build/webgpu/varlen-state-update.wgsl.jinja)
 
63
 
64
  ## Use with `@huggingface/kernels`
65
 
66
  ```sh
67
- npm install --save-exact @huggingface/kernels@0.0.1-preview.2
68
  ```
69
 
70
  Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
@@ -79,9 +94,9 @@ import { getKernel } from "@huggingface/kernels";
79
 
80
  const kernel = await getKernel("webgpu-kernels/com.microsoft.VarlenCausalConvWithState", { version: 1 });
81
  const { outputT, finalStateT } = await kernel({
82
- inputT: { data: inputTData, shape: [5, 6] },
83
- weightT: { data: weightTData, shape: [6, 1, 4] },
84
- cumulativeSequenceLengthT: { data: cumulativeSequenceLengthTData, shape: [3] },
85
- initialStateT: { data: initialStateTData, shape: [2, 6, 3] },
86
  });
87
  ```
 
24
  | `weightT` | `weight` | `T` | same as logical dtype | `3` | — | Depthwise kernel with shape `(channels, 1, kernel_size)`. | required |
25
  | `cumulativeSequenceLengthT` | `cumulative_sequence_length` | `M` | `int32` | `1` | — | Exclusive prefix sums with shape `(batch_size + 1)`, starting at 0, ending at `total_tokens`, and strictly increasing so every sequence is non-empty. Sequence `i` owns tokens `[cum[i], cum[i + 1])`. Outputs are unspecified for a malformed schedule. | required |
26
  | `biasT` | `bias` | `T` | same as logical dtype | `1` | — | Optional per-channel bias with shape `(channels,)`. In an ONNX graph an omitted bias must still occupy input index 3 as an empty name so `initial_state` stays at index 4. | optional |
27
+ | `initialStateT` | `initial_state` | `T` | same as logical dtype | `3` | — | Required committed carry state with shape `(batch_size, channels, (kernel_size - 1) * dilation)`, holding the raw samples immediately preceding this call. | required |
28
  | `captureCountT` | `capture_count` | `M` | `int32` | `1` | — | Optional int32 vector with shape `(batch_size)`. Required exactly when `state_update_capacity` is positive; each value is clamped to `[0, min(state_update_capacity, sequence_length)]`. | optional |
29
 
30
  ## Outputs
 
32
  | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
33
  | --- | --- | --- | --- | --- | --- | --- |
34
  | `outputT` | `output` | `T` | same as `inputT` | same as `inputT` | Convolution output with the same shape as `input`. | required |
35
+ | `finalStateT` | `final_state` | `T` | `3` | derived | State after each sequence's final token, shape `(batch_size, channels, (kernel_size - 1) * dilation)`. Always fully written. | required |
36
  | `stateUpdateT` | `state_update` | `T` | `3` | derived | Optional compact transition values with shape `(batch_size, state_update_capacity, channels)`. Active slots contain the original local input tokens and all other slots are zero. | optional |
37
 
38
  ## Attributes
 
42
  | Attribute | Default | Description |
43
  | --- | --- | --- |
44
  | `activation` | `"none"` | Fused activation applied after convolution and bias. One of `none`, `silu`, or `swish`; the standard default is `none`. |
45
+ | `dilation` | `1` | Positive integer spacing between taps; the initial and final states hold (`kernel_size` - 1) * dilation raw samples. |
46
  | `state_update_capacity` | `0` | Static number of compact per-request prefix transition values to expose, in `[0, 8]`. The standard default is 0. |
47
 
48
  ## Type constraints
 
52
  | `T` | `float32`, `float16` |
53
  | `M` | `int32` |
54
 
55
+ ## Implementation variants
56
+
57
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
58
+
59
+ - `unit_plain` — Direct single-token sequence convolution with contiguous vector state traffic and fused capture writes. Strictly increasing schedules with `total_tokens == batch_size` imply one token per sequence. Channel divisibility selects the vector width; the workgroup respects device limits. Dilated and f16 requests retain their existing paths.
60
+ - `unit_bias` — Direct single-token sequence convolution with contiguous vector state traffic and fused capture writes. Strictly increasing schedules with `total_tokens == batch_size` imply one token per sequence. Channel divisibility selects the vector width; the workgroup respects device limits. Dilated and f16 requests retain their existing paths.
61
+ - `unit_state_update` — Direct single-token sequence convolution with contiguous vector state traffic and fused capture writes. Strictly increasing schedules with `total_tokens == batch_size` imply one token per sequence. Channel divisibility selects the vector width; the workgroup respects device limits. Dilated and f16 requests retain their existing paths.
62
+ - `unit_bias_state_update` — Direct single-token sequence convolution with contiguous vector state traffic and fused capture writes. Strictly increasing schedules with `total_tokens == batch_size` imply one token per sequence. Channel divisibility selects the vector width; the workgroup respects device limits. Dilated and f16 requests retain their existing paths.
63
+ - `capture_copy_state_update_stream` — Copy the capture prefix in contiguous channel vectors while preserving the convolution path and exact zero-fill semantics. The existing copy template operates on vector groups; no extra storage bindings or optional GPU features are needed.
64
+ - `capture_copy_state_update` — Copy the capture prefix in contiguous channel vectors while preserving the convolution path and exact zero-fill semantics. The existing copy template operates on vector groups; no extra storage bindings or optional GPU features are needed.
65
+ - `capture_copy_bias_state_update_stream` — Copy the capture prefix in contiguous channel vectors while preserving the convolution path and exact zero-fill semantics. The existing copy template operates on vector groups; no extra storage bindings or optional GPU features are needed.
66
+ - `capture_copy_bias_state_update` — Copy the capture prefix in contiguous channel vectors while preserving the convolution path and exact zero-fill semantics. The existing copy template operates on vector groups; no extra storage bindings or optional GPU features are needed.
67
+
68
  ## Files
69
 
70
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
71
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
72
  - [`test.json`](build/webgpu/test.json) — correctness cases
73
+ - [`bench.json`](build/webgpu/bench.json) — benchmark cases
74
  - [`varlen-causal-conv-stream.wgsl.jinja`](build/webgpu/varlen-causal-conv-stream.wgsl.jinja)
75
  - [`varlen-causal-conv.wgsl.jinja`](build/webgpu/varlen-causal-conv.wgsl.jinja)
76
  - [`varlen-state-update.wgsl.jinja`](build/webgpu/varlen-state-update.wgsl.jinja)
77
+ - [`varlen-unit-sequence.wgsl.jinja`](build/webgpu/varlen-unit-sequence.wgsl.jinja)
78
 
79
  ## Use with `@huggingface/kernels`
80
 
81
  ```sh
82
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.3
83
  ```
84
 
85
  Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
 
94
 
95
  const kernel = await getKernel("webgpu-kernels/com.microsoft.VarlenCausalConvWithState", { version: 1 });
96
  const { outputT, finalStateT } = await kernel({
97
+ inputT: { data: inputTData, shape: [1, 4] },
98
+ weightT: { data: weightTData, shape: [4, 1, 4] },
99
+ cumulativeSequenceLengthT: { data: cumulativeSequenceLengthTData, shape: [2] },
100
+ initialStateT: { data: initialStateTData, shape: [1, 4, 3] },
101
  });
102
  ```
build/webgpu/bench.json CHANGED
@@ -4,6 +4,9 @@
4
  "streamWorkgroupSize": [64, 128, 256],
5
  "streamChunk": [16, 32, 64, 128, 256]
6
  },
 
 
 
7
  "cases": [
8
  {
9
  "name": "varlenconv-decode-b32-c4096-k4",
@@ -27,7 +30,16 @@
27
  "outputT": { "shape": [32, 4096], "dtype": "float32" },
28
  "finalStateT": { "shape": [32, 4096, 3], "dtype": "float32" }
29
  },
30
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 32 * 4096 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
31
  "attrs": { "activation": "silu" }
32
  },
33
  {
@@ -49,7 +61,16 @@
49
  "outputT": { "shape": [2048, 4096], "dtype": "float32" },
50
  "finalStateT": { "shape": [1, 4096, 3], "dtype": "float32" }
51
  },
52
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 2048 * 4096 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
53
  "attrs": { "activation": "silu" }
54
  },
55
  {
@@ -71,7 +92,16 @@
71
  "outputT": { "shape": [512, 4096], "dtype": "float32" },
72
  "finalStateT": { "shape": [8, 4096, 3], "dtype": "float32" }
73
  },
74
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 512 * 4096 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
75
  "attrs": { "activation": "silu" }
76
  },
77
  {
@@ -95,10 +125,19 @@
95
  "finalStateT": { "shape": [6, 2048, 3], "dtype": "float32" },
96
  "stateUpdateT": { "dtype": "float32", "shape": [6, 4, 2048] }
97
  },
98
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 378 * 2048 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
99
  "attrs": { "activation": "silu", "state_update_capacity": 4 },
100
  "provenance": {
101
- "notes": "Benchmarks the schema-defined compact state_update alongside convolution; every request captures up to the configured contiguous-prefix capacity."
102
  }
103
  },
104
  {
@@ -122,10 +161,19 @@
122
  "finalStateT": { "shape": [6, 2048, 3], "dtype": "float32" },
123
  "stateUpdateT": { "dtype": "float32", "shape": [6, 4, 2048] }
124
  },
125
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 378 * 2048 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
126
  "attrs": { "activation": "silu", "state_update_capacity": 4 },
127
  "provenance": {
128
- "notes": "Benchmarks the schema-defined compact state_update alongside convolution; every request captures up to the configured contiguous-prefix capacity."
129
  }
130
  },
131
  {
@@ -147,7 +195,16 @@
147
  "outputT": { "shape": [512, 4096], "dtype": "float32" },
148
  "finalStateT": { "shape": [4, 4096, 3], "dtype": "float32" }
149
  },
150
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 512 * 4096 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
151
  "attrs": { "activation": "silu" }
152
  },
153
  {
@@ -169,7 +226,16 @@
169
  "outputT": { "shape": [512, 4096], "dtype": "float16" },
170
  "finalStateT": { "shape": [8, 4096, 3], "dtype": "float16" }
171
  },
172
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 512 * 4096 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
173
  "attrs": { "activation": "silu" }
174
  },
175
  {
@@ -191,8 +257,261 @@
191
  "outputT": { "shape": [512, 4096], "dtype": "float32" },
192
  "finalStateT": { "shape": [8, 4096, 2], "dtype": "float32" }
193
  },
194
- "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 512 * 4096 * dtypeBytes(args.dtype)" }] },
 
 
 
 
 
 
 
 
 
195
  "attrs": { "activation": "silu" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  }
197
  ]
198
  }
 
4
  "streamWorkgroupSize": [64, 128, 256],
5
  "streamChunk": [16, 32, 64, 128, 256]
6
  },
7
+ "fixtureArrays": {
8
+ "varlenconv-unit-capture-b64-c4096-k4-cap4_input_captureCountT": [2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99, 2, 0, -3, 99]
9
+ },
10
  "cases": [
11
  {
12
  "name": "varlenconv-decode-b32-c4096-k4",
 
30
  "outputT": { "shape": [32, 4096], "dtype": "float32" },
31
  "finalStateT": { "shape": [32, 4096, 3], "dtype": "float32" }
32
  },
33
+ "bench": {
34
+ "metrics": [
35
+ {
36
+ "type": "bandwidth",
37
+ "name": "Logical tensor traffic",
38
+ "provenance": "logical",
39
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
40
+ }
41
+ ]
42
+ },
43
  "attrs": { "activation": "silu" }
44
  },
45
  {
 
61
  "outputT": { "shape": [2048, 4096], "dtype": "float32" },
62
  "finalStateT": { "shape": [1, 4096, 3], "dtype": "float32" }
63
  },
64
+ "bench": {
65
+ "metrics": [
66
+ {
67
+ "type": "bandwidth",
68
+ "name": "Logical tensor traffic",
69
+ "provenance": "logical",
70
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
71
+ }
72
+ ]
73
+ },
74
  "attrs": { "activation": "silu" }
75
  },
76
  {
 
92
  "outputT": { "shape": [512, 4096], "dtype": "float32" },
93
  "finalStateT": { "shape": [8, 4096, 3], "dtype": "float32" }
94
  },
95
+ "bench": {
96
+ "metrics": [
97
+ {
98
+ "type": "bandwidth",
99
+ "name": "Logical tensor traffic",
100
+ "provenance": "logical",
101
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
102
+ }
103
+ ]
104
+ },
105
  "attrs": { "activation": "silu" }
106
  },
107
  {
 
125
  "finalStateT": { "shape": [6, 2048, 3], "dtype": "float32" },
126
  "stateUpdateT": { "dtype": "float32", "shape": [6, 4, 2048] }
127
  },
128
+ "bench": {
129
+ "metrics": [
130
+ {
131
+ "type": "bandwidth",
132
+ "name": "Logical tensor traffic",
133
+ "provenance": "logical",
134
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
135
+ }
136
+ ]
137
+ },
138
  "attrs": { "activation": "silu", "state_update_capacity": 4 },
139
  "provenance": {
140
+ "notes": "The compact state_update accompanies convolution and captures up to the configured contiguous-prefix capacity."
141
  }
142
  },
143
  {
 
161
  "finalStateT": { "shape": [6, 2048, 3], "dtype": "float32" },
162
  "stateUpdateT": { "dtype": "float32", "shape": [6, 4, 2048] }
163
  },
164
+ "bench": {
165
+ "metrics": [
166
+ {
167
+ "type": "bandwidth",
168
+ "name": "Logical tensor traffic",
169
+ "provenance": "logical",
170
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
171
+ }
172
+ ]
173
+ },
174
  "attrs": { "activation": "silu", "state_update_capacity": 4 },
175
  "provenance": {
176
+ "notes": "The compact state_update accompanies convolution and captures up to the configured contiguous-prefix capacity."
177
  }
178
  },
179
  {
 
195
  "outputT": { "shape": [512, 4096], "dtype": "float32" },
196
  "finalStateT": { "shape": [4, 4096, 3], "dtype": "float32" }
197
  },
198
+ "bench": {
199
+ "metrics": [
200
+ {
201
+ "type": "bandwidth",
202
+ "name": "Logical tensor traffic",
203
+ "provenance": "logical",
204
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
205
+ }
206
+ ]
207
+ },
208
  "attrs": { "activation": "silu" }
209
  },
210
  {
 
226
  "outputT": { "shape": [512, 4096], "dtype": "float16" },
227
  "finalStateT": { "shape": [8, 4096, 3], "dtype": "float16" }
228
  },
229
+ "bench": {
230
+ "metrics": [
231
+ {
232
+ "type": "bandwidth",
233
+ "name": "Logical tensor traffic",
234
+ "provenance": "logical",
235
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
236
+ }
237
+ ]
238
+ },
239
  "attrs": { "activation": "silu" }
240
  },
241
  {
 
257
  "outputT": { "shape": [512, 4096], "dtype": "float32" },
258
  "finalStateT": { "shape": [8, 4096, 2], "dtype": "float32" }
259
  },
260
+ "bench": {
261
+ "metrics": [
262
+ {
263
+ "type": "bandwidth",
264
+ "name": "Logical tensor traffic",
265
+ "provenance": "logical",
266
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
267
+ }
268
+ ]
269
+ },
270
  "attrs": { "activation": "silu" }
271
+ },
272
+ {
273
+ "name": "varlenconv-unit-capture-b1-c4096-k4-cap4",
274
+ "preset": "smoke",
275
+ "vars": { "dtype": "float32" },
276
+ "inputs": {
277
+ "inputT": { "shape": [1, 4096], "dtype": "float32", "dist": "normal", "seed": 4100, "scale": 1 },
278
+ "weightT": { "shape": [4096, 1, 4], "dtype": "float32", "dist": "normal", "seed": 4101, "scale": 1 },
279
+ "cumulativeSequenceLengthT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [0, 1] } },
280
+ "biasT": { "shape": [4096], "dtype": "float32", "dist": "normal", "seed": 4102, "scale": 1 },
281
+ "initialStateT": { "shape": [1, 4096, 3], "dtype": "float32", "dist": "normal", "seed": 4103, "scale": 1 },
282
+ "captureCountT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [2] } }
283
+ },
284
+ "outputs": {
285
+ "outputT": { "shape": [1, 4096], "dtype": "float32" },
286
+ "finalStateT": { "shape": [1, 4096, 3], "dtype": "float32" },
287
+ "stateUpdateT": { "dtype": "float32", "shape": [1, 4, 4096] }
288
+ },
289
+ "bench": {
290
+ "metrics": [
291
+ {
292
+ "type": "bandwidth",
293
+ "name": "Logical tensor traffic",
294
+ "provenance": "logical",
295
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
296
+ }
297
+ ]
298
+ },
299
+ "attrs": { "activation": "silu", "state_update_capacity": 4 }
300
+ },
301
+ {
302
+ "name": "varlenconv-unit-capture-b64-c4096-k4-cap4",
303
+ "preset": "smoke",
304
+ "vars": { "dtype": "float32" },
305
+ "inputs": {
306
+ "inputT": { "shape": [64, 4096], "dtype": "float32", "dist": "normal", "seed": 4100, "scale": 1 },
307
+ "weightT": { "shape": [4096, 1, 4], "dtype": "float32", "dist": "normal", "seed": 4101, "scale": 1 },
308
+ "cumulativeSequenceLengthT": {
309
+ "dtype": "int32",
310
+ "shape": [65],
311
+ "data": {
312
+ "kind": "values",
313
+ "values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]
314
+ }
315
+ },
316
+ "biasT": { "shape": [4096], "dtype": "float32", "dist": "normal", "seed": 4102, "scale": 1 },
317
+ "initialStateT": { "shape": [64, 4096, 3], "dtype": "float32", "dist": "normal", "seed": 4103, "scale": 1 },
318
+ "captureCountT": {
319
+ "dtype": "int32",
320
+ "shape": [64],
321
+ "data": {
322
+ "kind": "values",
323
+ "values": { "$ref": "#/fixtureArrays/varlenconv-unit-capture-b64-c4096-k4-cap4_input_captureCountT" }
324
+ }
325
+ }
326
+ },
327
+ "outputs": {
328
+ "outputT": { "shape": [64, 4096], "dtype": "float32" },
329
+ "finalStateT": { "shape": [64, 4096, 3], "dtype": "float32" },
330
+ "stateUpdateT": { "dtype": "float32", "shape": [64, 4, 4096] }
331
+ },
332
+ "bench": {
333
+ "metrics": [
334
+ {
335
+ "type": "bandwidth",
336
+ "name": "Logical tensor traffic",
337
+ "provenance": "logical",
338
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
339
+ }
340
+ ]
341
+ },
342
+ "attrs": { "activation": "silu", "state_update_capacity": 4 }
343
+ },
344
+ {
345
+ "name": "varlenconv-unit-alignment-b64-c4098-k3-cap4",
346
+ "preset": "smoke",
347
+ "vars": { "dtype": "float32" },
348
+ "inputs": {
349
+ "inputT": { "shape": [64, 4098], "dtype": "float32", "dist": "normal", "seed": 4100, "scale": 1 },
350
+ "weightT": { "shape": [4098, 1, 3], "dtype": "float32", "dist": "normal", "seed": 4101, "scale": 1 },
351
+ "cumulativeSequenceLengthT": {
352
+ "dtype": "int32",
353
+ "shape": [65],
354
+ "dist": "linearMod",
355
+ "mod": 65,
356
+ "step": 1,
357
+ "offset": 0
358
+ },
359
+ "biasT": { "shape": [4098], "dtype": "float32", "dist": "normal", "seed": 4102, "scale": 1 },
360
+ "initialStateT": { "shape": [64, 4098, 2], "dtype": "float32", "dist": "normal", "seed": 4103, "scale": 1 },
361
+ "captureCountT": {
362
+ "dtype": "int32",
363
+ "shape": [64],
364
+ "data": {
365
+ "kind": "values",
366
+ "values": { "$ref": "#/fixtureArrays/varlenconv-unit-capture-b64-c4096-k4-cap4_input_captureCountT" }
367
+ }
368
+ }
369
+ },
370
+ "outputs": {
371
+ "outputT": { "shape": [64, 4098], "dtype": "float32" },
372
+ "finalStateT": { "shape": [64, 4098, 2], "dtype": "float32" },
373
+ "stateUpdateT": { "dtype": "float32", "shape": [64, 4, 4098] }
374
+ },
375
+ "bench": {
376
+ "metrics": [
377
+ {
378
+ "type": "bandwidth",
379
+ "name": "Logical tensor traffic",
380
+ "provenance": "logical",
381
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
382
+ }
383
+ ]
384
+ },
385
+ "attrs": { "activation": "silu", "state_update_capacity": 4 }
386
+ },
387
+ {
388
+ "name": "varlenconv-unit-alignment-b64-c4097-k8-cap8",
389
+ "preset": "smoke",
390
+ "vars": { "dtype": "float32" },
391
+ "inputs": {
392
+ "inputT": { "shape": [64, 4097], "dtype": "float32", "dist": "normal", "seed": 4100, "scale": 1 },
393
+ "weightT": { "shape": [4097, 1, 8], "dtype": "float32", "dist": "normal", "seed": 4101, "scale": 1 },
394
+ "cumulativeSequenceLengthT": {
395
+ "dtype": "int32",
396
+ "shape": [65],
397
+ "dist": "linearMod",
398
+ "mod": 65,
399
+ "step": 1,
400
+ "offset": 0
401
+ },
402
+ "biasT": { "shape": [4097], "dtype": "float32", "dist": "normal", "seed": 4102, "scale": 1 },
403
+ "initialStateT": { "shape": [64, 4097, 7], "dtype": "float32", "dist": "normal", "seed": 4103, "scale": 1 },
404
+ "captureCountT": {
405
+ "dtype": "int32",
406
+ "shape": [64],
407
+ "data": {
408
+ "kind": "values",
409
+ "values": { "$ref": "#/fixtureArrays/varlenconv-unit-capture-b64-c4096-k4-cap4_input_captureCountT" }
410
+ }
411
+ }
412
+ },
413
+ "outputs": {
414
+ "outputT": { "shape": [64, 4097], "dtype": "float32" },
415
+ "finalStateT": { "shape": [64, 4097, 7], "dtype": "float32" },
416
+ "stateUpdateT": { "dtype": "float32", "shape": [64, 8, 4097] }
417
+ },
418
+ "bench": {
419
+ "metrics": [
420
+ {
421
+ "type": "bandwidth",
422
+ "name": "Logical tensor traffic",
423
+ "provenance": "logical",
424
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
425
+ }
426
+ ]
427
+ },
428
+ "attrs": { "activation": "silu", "state_update_capacity": 8 }
429
+ },
430
+ {
431
+ "name": "varlenconv-capture-b64-t128-c4096-k4-d1",
432
+ "preset": "smoke",
433
+ "vars": { "dtype": "float32" },
434
+ "inputs": {
435
+ "inputT": { "shape": [128, 4096], "dtype": "float32", "dist": "normal", "seed": 4100, "scale": 1 },
436
+ "weightT": { "shape": [4096, 1, 4], "dtype": "float32", "dist": "normal", "seed": 4101, "scale": 1 },
437
+ "cumulativeSequenceLengthT": {
438
+ "dtype": "int32",
439
+ "shape": [65],
440
+ "dist": "linearMod",
441
+ "mod": 130,
442
+ "step": 2,
443
+ "offset": 0
444
+ },
445
+ "biasT": { "shape": [4096], "dtype": "float32", "dist": "normal", "seed": 4102, "scale": 1 },
446
+ "initialStateT": { "shape": [64, 4096, 3], "dtype": "float32", "dist": "normal", "seed": 4103, "scale": 1 },
447
+ "captureCountT": {
448
+ "dtype": "int32",
449
+ "shape": [64],
450
+ "data": {
451
+ "kind": "values",
452
+ "values": { "$ref": "#/fixtureArrays/varlenconv-unit-capture-b64-c4096-k4-cap4_input_captureCountT" }
453
+ }
454
+ }
455
+ },
456
+ "outputs": {
457
+ "outputT": { "shape": [128, 4096], "dtype": "float32" },
458
+ "finalStateT": { "shape": [64, 4096, 3], "dtype": "float32" },
459
+ "stateUpdateT": { "dtype": "float32", "shape": [64, 4, 4096] }
460
+ },
461
+ "bench": {
462
+ "metrics": [
463
+ {
464
+ "type": "bandwidth",
465
+ "name": "Logical tensor traffic",
466
+ "provenance": "logical",
467
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
468
+ }
469
+ ]
470
+ },
471
+ "attrs": { "activation": "silu", "state_update_capacity": 4, "dilation": 1 }
472
+ },
473
+ {
474
+ "name": "varlenconv-capture-b64-t128-c4096-k4-d2",
475
+ "preset": "smoke",
476
+ "vars": { "dtype": "float32" },
477
+ "inputs": {
478
+ "inputT": { "shape": [128, 4096], "dtype": "float32", "dist": "normal", "seed": 4100, "scale": 1 },
479
+ "weightT": { "shape": [4096, 1, 4], "dtype": "float32", "dist": "normal", "seed": 4101, "scale": 1 },
480
+ "cumulativeSequenceLengthT": {
481
+ "dtype": "int32",
482
+ "shape": [65],
483
+ "dist": "linearMod",
484
+ "mod": 130,
485
+ "step": 2,
486
+ "offset": 0
487
+ },
488
+ "biasT": { "shape": [4096], "dtype": "float32", "dist": "normal", "seed": 4102, "scale": 1 },
489
+ "initialStateT": { "shape": [64, 4096, 6], "dtype": "float32", "dist": "normal", "seed": 4103, "scale": 1 },
490
+ "captureCountT": {
491
+ "dtype": "int32",
492
+ "shape": [64],
493
+ "data": {
494
+ "kind": "values",
495
+ "values": { "$ref": "#/fixtureArrays/varlenconv-unit-capture-b64-c4096-k4-cap4_input_captureCountT" }
496
+ }
497
+ }
498
+ },
499
+ "outputs": {
500
+ "outputT": { "shape": [128, 4096], "dtype": "float32" },
501
+ "finalStateT": { "shape": [64, 4096, 6], "dtype": "float32" },
502
+ "stateUpdateT": { "dtype": "float32", "shape": [64, 4, 4096] }
503
+ },
504
+ "bench": {
505
+ "metrics": [
506
+ {
507
+ "type": "bandwidth",
508
+ "name": "Logical tensor traffic",
509
+ "provenance": "logical",
510
+ "value": "(numel(shapes.inputT) + numel(shapes.weightT) + numel(shapes.initialStateT) + numel(shapes.outputT) + numel(shapes.finalStateT) + (numel(shapes.biasT) if has(shapes, \"biasT\") else 0) + (numel(shapes.stateUpdateT) if has(shapes, \"stateUpdateT\") else 0)) * dtypeBytes(args.dtype) + 4 * (numel(shapes.cumulativeSequenceLengthT) + (numel(shapes.captureCountT) if has(shapes, \"captureCountT\") else 0))"
511
+ }
512
+ ]
513
+ },
514
+ "attrs": { "activation": "silu", "state_update_capacity": 4, "dilation": 2 }
515
  }
516
  ]
517
  }
build/webgpu/manifest.json CHANGED
@@ -21,7 +21,11 @@
21
  "shape": "[batchSize, stateUpdateCapacity, channels]"
22
  }
23
  },
24
- "attributes": { "activation": { "default": "none" }, "state_update_capacity": { "default": 0 } },
 
 
 
 
25
  "attributeConstraints": { "activation": { "values": ["none", "silu", "swish"] } },
26
  "typeConstraints": { "T": ["float32", "float16"], "M": ["int32"] },
27
  "tunables": {
@@ -35,33 +39,31 @@
35
  "batchSize": "dim(shapes.cumulativeSequenceLengthT, 0) - 1",
36
  "weightRankOk": "ranks.weightT == 3 and dim(shapes.weightT, 1) == 1",
37
  "kernelSize": "dim(shapes.weightT, 2)",
38
- "stateLength": "kernelSize - 1",
39
  "stateUpdateCapacity": "attrs.state_update_capacity",
40
  "capturePairOk": "present.captureCountT == (stateUpdateCapacity > 0)",
41
  "stateShape": "ranks.initialStateT == 3 and dim(shapes.initialStateT, 0) == batchSize and dim(shapes.initialStateT, 1) == channels and dim(shapes.initialStateT, 2) == stateLength",
42
  "finalStateShape": "ranks.finalStateT == 3 and dim(shapes.finalStateT, 0) == batchSize and dim(shapes.finalStateT, 1) == channels and dim(shapes.finalStateT, 2) == stateLength",
43
  "captureShape": "not present.captureCountT or (ranks.captureCountT == 1 and dim(shapes.captureCountT, 0) == batchSize and tensorDtypes.captureCountT == \"int32\")",
44
  "stateUpdateShape": "not present.stateUpdateT or (ranks.stateUpdateT == 3 and dim(shapes.stateUpdateT, 0) == batchSize and dim(shapes.stateUpdateT, 1) == stateUpdateCapacity and dim(shapes.stateUpdateT, 2) == channels and tensorDtypes.stateUpdateT == tensorDtypes.inputT)",
45
- "commonContract": "ranks.inputT == 2 and weightRankOk and ranks.outputT == 2 and ranks.cumulativeSequenceLengthT == 1 and channels >= 1 and kernelSize >= 1 and batchSize >= 1 and totalTokens >= batchSize and stateUpdateCapacity >= 0 and stateUpdateCapacity <= 8 and floor(stateUpdateCapacity) == stateUpdateCapacity and capturePairOk and captureShape and stateUpdateShape and (tensorDtypes.inputT == \"float32\" or tensorDtypes.inputT == \"float16\") and tensorDtypes.weightT == tensorDtypes.inputT and tensorDtypes.outputT == tensorDtypes.inputT and tensorDtypes.initialStateT == tensorDtypes.inputT and tensorDtypes.finalStateT == tensorDtypes.inputT and tensorDtypes.cumulativeSequenceLengthT == \"int32\" and f16Ok(dtypes.T) and dim(shapes.weightT, 0) == channels and dim(shapes.outputT, 0) == totalTokens and dim(shapes.outputT, 1) == channels and stateShape and finalStateShape",
46
  "biasOk": "ranks.biasT == 1 and tensorDtypes.biasT == tensorDtypes.inputT and dim(shapes.biasT, 0) == channels",
47
  "plainContract": "commonContract and not present.biasT and (not present.stateUpdateT or stateUpdateCapacity == 0)",
48
  "biasContract": "commonContract and present.biasT and biasOk and (not present.stateUpdateT or stateUpdateCapacity == 0)",
49
  "stateUpdateContract": "commonContract and not present.biasT and present.stateUpdateT and stateUpdateCapacity > 0",
50
- "biasStateUpdateContract": "commonContract and present.biasT and biasOk and present.stateUpdateT and stateUpdateCapacity > 0"
 
 
 
51
  },
52
  "bindings": {
53
- "input": { "arg": "inputT", "buffer": "read-only-storage", "elementType": "$inputScalar" },
54
- "weight": { "arg": "weightT", "buffer": "read-only-storage", "elementType": "$inputScalar" },
55
- "cumulative_sequence_length": {
56
- "arg": "cumulativeSequenceLengthT",
57
- "buffer": "read-only-storage",
58
- "elementType": "i32"
59
- },
60
- "initial_state": { "arg": "initialStateT", "buffer": "read-only-storage", "elementType": "$inputScalar" },
61
- "output": { "arg": "outputT", "buffer": "storage", "elementType": "$outputScalar" },
62
- "final_state": { "arg": "finalStateT", "buffer": "storage", "elementType": "$outputScalar" },
63
  "params": {
64
- "buffer": "uniform",
65
  "struct": [
66
  { "name": "batchSize", "type": "u32", "value": "batchSize" },
67
  { "name": "channels", "type": "u32", "value": "channels" },
@@ -69,34 +71,173 @@
69
  { "name": "kernelSize", "type": "u32", "value": "kernelSize" }
70
  ]
71
  },
72
- "bias": { "arg": "biasT", "buffer": "read-only-storage", "elementType": "$inputScalar" },
73
- "capture_count": { "arg": "captureCountT", "buffer": "read-only-storage", "elementType": "i32" },
74
- "state_update": { "arg": "stateUpdateT", "buffer": "storage", "elementType": "$outputScalar" },
75
- "params_2": {
76
  "name": "params",
77
- "buffer": "uniform",
78
  "struct": [
79
  { "name": "batchSize", "type": "u32", "value": "batchSize" },
80
  { "name": "channels", "type": "u32", "value": "channels" },
81
  { "name": "totalTokens", "type": "u32", "value": "totalTokens" },
82
  { "name": "stateUpdateCapacity", "type": "u32", "value": "stateUpdateCapacity" }
83
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
  },
86
  "variants": [
87
  {
88
- "id": "plain_stream",
89
- "priority": 20,
90
- "when": ["plainContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  "derive": {
92
  "hasBias": false,
93
- "useSilu": "attrs.activation != \"none\"",
94
  "inputScalar": "dtypes.T",
95
  "outputScalar": "dtypes.T",
96
  "workgroupSize": "tunables.streamWorkgroupSize",
97
  "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
98
  },
99
  "passes": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  {
101
  "id": "main",
102
  "name": "VarlenCausalConvWithState.Stream",
@@ -111,38 +252,51 @@
111
  ]
112
  },
113
  {
114
- "id": "bias_stream",
115
- "priority": 20,
116
- "when": ["biasContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
117
  "derive": {
118
- "hasBias": true,
119
- "useSilu": "attrs.activation != \"none\"",
120
  "inputScalar": "dtypes.T",
121
  "outputScalar": "dtypes.T",
122
- "workgroupSize": "tunables.streamWorkgroupSize",
123
- "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
124
  },
125
  "passes": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  {
127
  "id": "main",
128
- "name": "VarlenCausalConvWithState.Stream",
129
- "shader": "varlen-causal-conv-stream.wgsl.jinja",
130
- "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "bias", "output", "final_state", "params"],
131
  "dispatch": {
132
- "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
133
- "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
134
  "z": 1
135
  }
136
  }
137
  ]
138
  },
139
  {
140
- "id": "state_update_stream",
141
- "priority": 20,
142
- "when": ["stateUpdateContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
143
  "derive": {
144
- "hasBias": false,
145
- "useSilu": "attrs.activation != \"none\"",
146
  "inputScalar": "dtypes.T",
147
  "outputScalar": "dtypes.T",
148
  "workgroupSize": "tunables.streamWorkgroupSize",
@@ -153,18 +307,23 @@
153
  "id": "state_update",
154
  "name": "VarlenCausalConvWithState.StateUpdate",
155
  "shader": "varlen-state-update.wgsl.jinja",
156
- "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params_2"],
157
  "dispatch": {
158
- "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
159
- "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
160
  "z": 1
 
 
 
 
 
161
  }
162
  },
163
  {
164
  "id": "main",
165
  "name": "VarlenCausalConvWithState.Stream",
166
  "shader": "varlen-causal-conv-stream.wgsl.jinja",
167
- "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "output", "final_state", "params"],
168
  "dispatch": {
169
  "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
170
  "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
@@ -174,34 +333,62 @@
174
  ]
175
  },
176
  {
177
- "id": "bias_state_update_stream",
178
- "priority": 20,
179
- "when": ["biasStateUpdateContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
180
  "derive": {
181
  "hasBias": true,
182
- "useSilu": "attrs.activation != \"none\"",
183
  "inputScalar": "dtypes.T",
184
  "outputScalar": "dtypes.T",
185
- "workgroupSize": "tunables.streamWorkgroupSize",
186
- "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
187
  },
188
  "passes": [
189
  {
190
  "id": "state_update",
191
  "name": "VarlenCausalConvWithState.StateUpdate",
192
  "shader": "varlen-state-update.wgsl.jinja",
193
- "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params_2"],
194
  "dispatch": {
195
- "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
196
- "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
197
  "z": 1
 
 
 
 
 
198
  }
199
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  {
201
  "id": "main",
202
  "name": "VarlenCausalConvWithState.Stream",
203
  "shader": "varlen-causal-conv-stream.wgsl.jinja",
204
- "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "bias", "output", "final_state", "params"],
205
  "dispatch": {
206
  "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
207
  "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
@@ -215,8 +402,7 @@
215
  "priority": 0,
216
  "when": ["plainContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
217
  "derive": {
218
- "hasBias": false,
219
- "useSilu": "attrs.activation != \"none\"",
220
  "inputScalar": "dtypes.T",
221
  "outputScalar": "dtypes.T",
222
  "workgroupSize": "tunables.workgroupSize"
@@ -235,13 +421,37 @@
235
  }
236
  ]
237
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  {
239
  "id": "bias",
240
  "priority": 0,
241
  "when": ["biasContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
242
  "derive": {
243
- "hasBias": true,
244
- "useSilu": "attrs.activation != \"none\"",
245
  "inputScalar": "dtypes.T",
246
  "outputScalar": "dtypes.T",
247
  "workgroupSize": "tunables.workgroupSize"
@@ -260,13 +470,48 @@
260
  }
261
  ]
262
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  {
264
  "id": "state_update",
265
  "priority": 0,
266
  "when": ["stateUpdateContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
267
  "derive": {
268
- "hasBias": false,
269
- "useSilu": "attrs.activation != \"none\"",
270
  "inputScalar": "dtypes.T",
271
  "outputScalar": "dtypes.T",
272
  "workgroupSize": "tunables.workgroupSize"
@@ -276,7 +521,7 @@
276
  "id": "state_update",
277
  "name": "VarlenCausalConvWithState.StateUpdate",
278
  "shader": "varlen-state-update.wgsl.jinja",
279
- "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params_2"],
280
  "dispatch": {
281
  "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
282
  "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
@@ -296,13 +541,48 @@
296
  }
297
  ]
298
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  {
300
  "id": "bias_state_update",
301
  "priority": 0,
302
  "when": ["biasStateUpdateContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
303
  "derive": {
304
- "hasBias": true,
305
- "useSilu": "attrs.activation != \"none\"",
306
  "inputScalar": "dtypes.T",
307
  "outputScalar": "dtypes.T",
308
  "workgroupSize": "tunables.workgroupSize"
@@ -312,7 +592,7 @@
312
  "id": "state_update",
313
  "name": "VarlenCausalConvWithState.StateUpdate",
314
  "shader": "varlen-state-update.wgsl.jinja",
315
- "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params_2"],
316
  "dispatch": {
317
  "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
318
  "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
 
21
  "shape": "[batchSize, stateUpdateCapacity, channels]"
22
  }
23
  },
24
+ "attributes": {
25
+ "activation": { "default": "none" },
26
+ "dilation": { "default": 1 },
27
+ "state_update_capacity": { "default": 0 }
28
+ },
29
  "attributeConstraints": { "activation": { "values": ["none", "silu", "swish"] } },
30
  "typeConstraints": { "T": ["float32", "float16"], "M": ["int32"] },
31
  "tunables": {
 
39
  "batchSize": "dim(shapes.cumulativeSequenceLengthT, 0) - 1",
40
  "weightRankOk": "ranks.weightT == 3 and dim(shapes.weightT, 1) == 1",
41
  "kernelSize": "dim(shapes.weightT, 2)",
42
+ "stateLength": "(kernelSize - 1) * attrs.dilation",
43
  "stateUpdateCapacity": "attrs.state_update_capacity",
44
  "capturePairOk": "present.captureCountT == (stateUpdateCapacity > 0)",
45
  "stateShape": "ranks.initialStateT == 3 and dim(shapes.initialStateT, 0) == batchSize and dim(shapes.initialStateT, 1) == channels and dim(shapes.initialStateT, 2) == stateLength",
46
  "finalStateShape": "ranks.finalStateT == 3 and dim(shapes.finalStateT, 0) == batchSize and dim(shapes.finalStateT, 1) == channels and dim(shapes.finalStateT, 2) == stateLength",
47
  "captureShape": "not present.captureCountT or (ranks.captureCountT == 1 and dim(shapes.captureCountT, 0) == batchSize and tensorDtypes.captureCountT == \"int32\")",
48
  "stateUpdateShape": "not present.stateUpdateT or (ranks.stateUpdateT == 3 and dim(shapes.stateUpdateT, 0) == batchSize and dim(shapes.stateUpdateT, 1) == stateUpdateCapacity and dim(shapes.stateUpdateT, 2) == channels and tensorDtypes.stateUpdateT == tensorDtypes.inputT)",
49
+ "commonContract": "ranks.inputT == 2 and weightRankOk and ranks.outputT == 2 and ranks.cumulativeSequenceLengthT == 1 and channels >= 1 and kernelSize >= 1 and batchSize >= 1 and totalTokens >= batchSize and stateUpdateCapacity >= 0 and stateUpdateCapacity <= 8 and floor(stateUpdateCapacity) == stateUpdateCapacity and capturePairOk and captureShape and stateUpdateShape and (tensorDtypes.inputT == \"float32\" or tensorDtypes.inputT == \"float16\") and tensorDtypes.weightT == tensorDtypes.inputT and tensorDtypes.outputT == tensorDtypes.inputT and tensorDtypes.initialStateT == tensorDtypes.inputT and tensorDtypes.finalStateT == tensorDtypes.inputT and tensorDtypes.cumulativeSequenceLengthT == \"int32\" and f16Ok(dtypes.T) and dim(shapes.weightT, 0) == channels and dim(shapes.outputT, 0) == totalTokens and dim(shapes.outputT, 1) == channels and stateShape and finalStateShape and attrs.dilation >= 1 and floor(attrs.dilation) == attrs.dilation",
50
  "biasOk": "ranks.biasT == 1 and tensorDtypes.biasT == tensorDtypes.inputT and dim(shapes.biasT, 0) == channels",
51
  "plainContract": "commonContract and not present.biasT and (not present.stateUpdateT or stateUpdateCapacity == 0)",
52
  "biasContract": "commonContract and present.biasT and biasOk and (not present.stateUpdateT or stateUpdateCapacity == 0)",
53
  "stateUpdateContract": "commonContract and not present.biasT and present.stateUpdateT and stateUpdateCapacity > 0",
54
+ "biasStateUpdateContract": "commonContract and present.biasT and biasOk and present.stateUpdateT and stateUpdateCapacity > 0",
55
+ "causalDilation": "attrs.dilation",
56
+ "unitWg": "min(tunables.streamWorkgroupSize, device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
57
+ "useSilu": "attrs.activation != \"none\""
58
  },
59
  "bindings": {
60
+ "input": { "arg": "inputT", "elementType": "$inputScalar" },
61
+ "weight": { "arg": "weightT", "elementType": "$inputScalar" },
62
+ "cumulative_sequence_length": { "arg": "cumulativeSequenceLengthT", "elementType": "i32" },
63
+ "initial_state": { "arg": "initialStateT", "elementType": "$inputScalar" },
64
+ "output": { "arg": "outputT", "elementType": "$outputScalar" },
65
+ "final_state": { "arg": "finalStateT", "elementType": "$outputScalar" },
 
 
 
 
66
  "params": {
 
67
  "struct": [
68
  { "name": "batchSize", "type": "u32", "value": "batchSize" },
69
  { "name": "channels", "type": "u32", "value": "channels" },
 
71
  { "name": "kernelSize", "type": "u32", "value": "kernelSize" }
72
  ]
73
  },
74
+ "bias": { "arg": "biasT", "elementType": "$inputScalar" },
75
+ "capture_count": { "arg": "captureCountT", "elementType": "i32" },
76
+ "state_update": { "arg": "stateUpdateT", "elementType": "$outputScalar" },
77
+ "params__uniform": {
78
  "name": "params",
 
79
  "struct": [
80
  { "name": "batchSize", "type": "u32", "value": "batchSize" },
81
  { "name": "channels", "type": "u32", "value": "channels" },
82
  { "name": "totalTokens", "type": "u32", "value": "totalTokens" },
83
  { "name": "stateUpdateCapacity", "type": "u32", "value": "stateUpdateCapacity" }
84
  ]
85
+ },
86
+ "input_unit": { "arg": "inputT", "name": "input", "elementType": "$unitStorageType" },
87
+ "bias_unit": { "arg": "biasT", "name": "bias", "elementType": "$unitStorageType" },
88
+ "output_unit": { "arg": "outputT", "name": "output", "elementType": "$unitStorageType" },
89
+ "state_update_unit": { "arg": "stateUpdateT", "name": "state_update", "elementType": "$unitStorageType" },
90
+ "params_unit": {
91
+ "name": "params",
92
+ "struct": [
93
+ { "name": "channels", "type": "u32", "value": "channels" },
94
+ { "name": "totalTokens", "type": "u32", "value": "totalTokens" }
95
+ ]
96
+ },
97
+ "weight_unit": { "arg": "weightT", "elementType": "$unitStorageType", "name": "weight" },
98
+ "initial_state_unit": { "arg": "initialStateT", "elementType": "$unitStorageType", "name": "initial_state" },
99
+ "final_state_unit": { "arg": "finalStateT", "elementType": "$unitStorageType", "name": "final_state" },
100
+ "input_capture": { "arg": "inputT", "name": "input", "elementType": "$captureStorageType" },
101
+ "state_update_capture": { "arg": "stateUpdateT", "name": "state_update", "elementType": "$captureStorageType" },
102
+ "params_capture": {
103
+ "name": "params",
104
+ "struct": [
105
+ { "name": "batchSize", "type": "u32", "value": "batchSize" },
106
+ { "name": "channels", "type": "u32", "value": "channels / captureVectorWidth" },
107
+ { "name": "totalTokens", "type": "u32", "value": "totalTokens" },
108
+ { "name": "stateUpdateCapacity", "type": "u32", "value": "stateUpdateCapacity" }
109
+ ]
110
  }
111
  },
112
  "variants": [
113
  {
114
+ "id": "unit_plain",
115
+ "priority": 50,
116
+ "when": ["plainContract", "tensorDtypes.inputT == \"float32\"", "totalTokens == batchSize", "attrs.dilation == 1", "kernelSize >= 2", "kernelSize <= 8", "unitWg >= 1", "floor(unitWg) == unitWg"],
117
+ "derive": {
118
+ "hasBias": false,
119
+ "hasCapture": false,
120
+ "unitVectorWidth": "4 if channels % 4 == 0 else (2 if channels % 2 == 0 else 1)",
121
+ "unitStorageType": "dtypes.T if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
122
+ "unitValueType": "\"f32\" if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<f32>\""
123
+ },
124
+ "passes": [
125
+ {
126
+ "id": "main",
127
+ "name": "VarlenCausalConvWithState.UnitSequence",
128
+ "shader": "varlen-unit-sequence.wgsl.jinja",
129
+ "bindings": ["input_unit", "weight_unit", "initial_state_unit", "output_unit", "final_state_unit", "params_unit"],
130
+ "dispatch": {
131
+ "x": "min(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
132
+ "y": "ceilDiv(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
133
+ "z": 1
134
+ }
135
+ }
136
+ ]
137
+ },
138
+ {
139
+ "id": "unit_bias",
140
+ "priority": 50,
141
+ "when": ["biasContract", "tensorDtypes.inputT == \"float32\"", "totalTokens == batchSize", "attrs.dilation == 1", "kernelSize >= 2", "kernelSize <= 8", "unitWg >= 1", "floor(unitWg) == unitWg"],
142
+ "derive": {
143
+ "hasBias": true,
144
+ "hasCapture": false,
145
+ "unitVectorWidth": "4 if channels % 4 == 0 else (2 if channels % 2 == 0 else 1)",
146
+ "unitStorageType": "dtypes.T if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
147
+ "unitValueType": "\"f32\" if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<f32>\""
148
+ },
149
+ "passes": [
150
+ {
151
+ "id": "main",
152
+ "name": "VarlenCausalConvWithState.UnitSequence",
153
+ "shader": "varlen-unit-sequence.wgsl.jinja",
154
+ "bindings": ["input_unit", "weight_unit", "initial_state_unit", "bias_unit", "output_unit", "final_state_unit", "params_unit"],
155
+ "dispatch": {
156
+ "x": "min(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
157
+ "y": "ceilDiv(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
158
+ "z": 1
159
+ }
160
+ }
161
+ ]
162
+ },
163
+ {
164
+ "id": "unit_state_update",
165
+ "priority": 50,
166
+ "when": ["stateUpdateContract", "tensorDtypes.inputT == \"float32\"", "totalTokens == batchSize", "attrs.dilation == 1", "kernelSize >= 2", "kernelSize <= 8", "unitWg >= 1", "floor(unitWg) == unitWg"],
167
+ "derive": {
168
+ "hasBias": false,
169
+ "hasCapture": true,
170
+ "unitVectorWidth": "4 if channels % 4 == 0 else (2 if channels % 2 == 0 else 1)",
171
+ "unitStorageType": "dtypes.T if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
172
+ "unitValueType": "\"f32\" if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<f32>\""
173
+ },
174
+ "passes": [
175
+ {
176
+ "id": "main",
177
+ "name": "VarlenCausalConvWithState.UnitSequence",
178
+ "shader": "varlen-unit-sequence.wgsl.jinja",
179
+ "bindings": ["input_unit", "weight_unit", "initial_state_unit", "output_unit", "final_state_unit", "params_unit", "capture_count", "state_update_unit"],
180
+ "dispatch": {
181
+ "x": "min(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
182
+ "y": "ceilDiv(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
183
+ "z": 1
184
+ }
185
+ }
186
+ ]
187
+ },
188
+ {
189
+ "id": "unit_bias_state_update",
190
+ "priority": 50,
191
+ "when": ["biasStateUpdateContract", "tensorDtypes.inputT == \"float32\"", "totalTokens == batchSize", "attrs.dilation == 1", "kernelSize >= 2", "kernelSize <= 8", "unitWg >= 1", "floor(unitWg) == unitWg"],
192
+ "derive": {
193
+ "hasBias": true,
194
+ "hasCapture": true,
195
+ "unitVectorWidth": "4 if channels % 4 == 0 else (2 if channels % 2 == 0 else 1)",
196
+ "unitStorageType": "dtypes.T if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
197
+ "unitValueType": "\"f32\" if unitVectorWidth == 1 else \"vec\" ~ unitVectorWidth ~ \"<f32>\""
198
+ },
199
+ "passes": [
200
+ {
201
+ "id": "main",
202
+ "name": "VarlenCausalConvWithState.UnitSequence",
203
+ "shader": "varlen-unit-sequence.wgsl.jinja",
204
+ "bindings": ["input_unit", "weight_unit", "initial_state_unit", "bias_unit", "output_unit", "final_state_unit", "params_unit", "capture_count", "state_update_unit"],
205
+ "dispatch": {
206
+ "x": "min(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
207
+ "y": "ceilDiv(ceilDiv((totalTokens * channels / unitVectorWidth), (unitWg)), 65535)",
208
+ "z": 1
209
+ }
210
+ }
211
+ ]
212
+ },
213
+ {
214
+ "id": "capture_copy_state_update_stream",
215
+ "priority": 25,
216
+ "when": ["stateUpdateContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX", "attrs.dilation == 1", "tensorDtypes.inputT == \"float32\"", "channels % 2 == 0"],
217
  "derive": {
218
  "hasBias": false,
 
219
  "inputScalar": "dtypes.T",
220
  "outputScalar": "dtypes.T",
221
  "workgroupSize": "tunables.streamWorkgroupSize",
222
  "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
223
  },
224
  "passes": [
225
+ {
226
+ "id": "state_update",
227
+ "name": "VarlenCausalConvWithState.StateUpdate",
228
+ "shader": "varlen-state-update.wgsl.jinja",
229
+ "bindings": ["input_capture", "cumulative_sequence_length", "capture_count", "state_update_capture", "params_capture"],
230
+ "dispatch": {
231
+ "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
232
+ "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
233
+ "z": 1
234
+ },
235
+ "derive": {
236
+ "captureVectorWidth": "4 if channels % 4 == 0 else 2",
237
+ "captureStorageType": "\"vec\" ~ captureVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
238
+ "outputScalar": "captureStorageType"
239
+ }
240
+ },
241
  {
242
  "id": "main",
243
  "name": "VarlenCausalConvWithState.Stream",
 
252
  ]
253
  },
254
  {
255
+ "id": "capture_copy_state_update",
256
+ "priority": 5,
257
+ "when": ["stateUpdateContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX", "tensorDtypes.inputT == \"float32\"", "channels % 2 == 0"],
258
  "derive": {
259
+ "hasBias": false,
 
260
  "inputScalar": "dtypes.T",
261
  "outputScalar": "dtypes.T",
262
+ "workgroupSize": "tunables.workgroupSize"
 
263
  },
264
  "passes": [
265
+ {
266
+ "id": "state_update",
267
+ "name": "VarlenCausalConvWithState.StateUpdate",
268
+ "shader": "varlen-state-update.wgsl.jinja",
269
+ "bindings": ["input_capture", "cumulative_sequence_length", "capture_count", "state_update_capture", "params_capture"],
270
+ "dispatch": {
271
+ "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
272
+ "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
273
+ "z": 1
274
+ },
275
+ "derive": {
276
+ "captureVectorWidth": "4 if channels % 4 == 0 else 2",
277
+ "captureStorageType": "\"vec\" ~ captureVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
278
+ "outputScalar": "captureStorageType"
279
+ }
280
+ },
281
  {
282
  "id": "main",
283
+ "name": "VarlenCausalConvWithState",
284
+ "shader": "varlen-causal-conv.wgsl.jinja",
285
+ "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "output", "final_state", "params"],
286
  "dispatch": {
287
+ "x": "min(ceilDiv((totalTokens * channels), (workgroupSize)), 65535)",
288
+ "y": "ceilDiv(ceilDiv((totalTokens * channels), (workgroupSize)), 65535)",
289
  "z": 1
290
  }
291
  }
292
  ]
293
  },
294
  {
295
+ "id": "capture_copy_bias_state_update_stream",
296
+ "priority": 25,
297
+ "when": ["biasStateUpdateContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX", "attrs.dilation == 1", "tensorDtypes.inputT == \"float32\"", "channels % 2 == 0"],
298
  "derive": {
299
+ "hasBias": true,
 
300
  "inputScalar": "dtypes.T",
301
  "outputScalar": "dtypes.T",
302
  "workgroupSize": "tunables.streamWorkgroupSize",
 
307
  "id": "state_update",
308
  "name": "VarlenCausalConvWithState.StateUpdate",
309
  "shader": "varlen-state-update.wgsl.jinja",
310
+ "bindings": ["input_capture", "cumulative_sequence_length", "capture_count", "state_update_capture", "params_capture"],
311
  "dispatch": {
312
+ "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
313
+ "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
314
  "z": 1
315
+ },
316
+ "derive": {
317
+ "captureVectorWidth": "4 if channels % 4 == 0 else 2",
318
+ "captureStorageType": "\"vec\" ~ captureVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
319
+ "outputScalar": "captureStorageType"
320
  }
321
  },
322
  {
323
  "id": "main",
324
  "name": "VarlenCausalConvWithState.Stream",
325
  "shader": "varlen-causal-conv-stream.wgsl.jinja",
326
+ "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "bias", "output", "final_state", "params"],
327
  "dispatch": {
328
  "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
329
  "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
 
333
  ]
334
  },
335
  {
336
+ "id": "capture_copy_bias_state_update",
337
+ "priority": 5,
338
+ "when": ["biasStateUpdateContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX", "tensorDtypes.inputT == \"float32\"", "channels % 2 == 0"],
339
  "derive": {
340
  "hasBias": true,
 
341
  "inputScalar": "dtypes.T",
342
  "outputScalar": "dtypes.T",
343
+ "workgroupSize": "tunables.workgroupSize"
 
344
  },
345
  "passes": [
346
  {
347
  "id": "state_update",
348
  "name": "VarlenCausalConvWithState.StateUpdate",
349
  "shader": "varlen-state-update.wgsl.jinja",
350
+ "bindings": ["input_capture", "cumulative_sequence_length", "capture_count", "state_update_capture", "params_capture"],
351
  "dispatch": {
352
+ "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
353
+ "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels / captureVectorWidth), (workgroupSize)), 65535)",
354
  "z": 1
355
+ },
356
+ "derive": {
357
+ "captureVectorWidth": "4 if channels % 4 == 0 else 2",
358
+ "captureStorageType": "\"vec\" ~ captureVectorWidth ~ \"<\" ~ dtypes.T ~ \">\"",
359
+ "outputScalar": "captureStorageType"
360
  }
361
  },
362
+ {
363
+ "id": "main",
364
+ "name": "VarlenCausalConvWithState",
365
+ "shader": "varlen-causal-conv.wgsl.jinja",
366
+ "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "bias", "output", "final_state", "params"],
367
+ "dispatch": {
368
+ "x": "min(ceilDiv((totalTokens * channels), (workgroupSize)), 65535)",
369
+ "y": "ceilDiv(ceilDiv((totalTokens * channels), (workgroupSize)), 65535)",
370
+ "z": 1
371
+ }
372
+ }
373
+ ]
374
+ },
375
+ {
376
+ "id": "plain_stream",
377
+ "priority": 20,
378
+ "when": ["plainContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX", "attrs.dilation == 1"],
379
+ "derive": {
380
+ "hasBias": "present.biasT",
381
+ "inputScalar": "dtypes.T",
382
+ "outputScalar": "dtypes.T",
383
+ "workgroupSize": "tunables.streamWorkgroupSize",
384
+ "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
385
+ },
386
+ "passes": [
387
  {
388
  "id": "main",
389
  "name": "VarlenCausalConvWithState.Stream",
390
  "shader": "varlen-causal-conv-stream.wgsl.jinja",
391
+ "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "output", "final_state", "params"],
392
  "dispatch": {
393
  "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
394
  "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
 
402
  "priority": 0,
403
  "when": ["plainContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
404
  "derive": {
405
+ "hasBias": "present.biasT",
 
406
  "inputScalar": "dtypes.T",
407
  "outputScalar": "dtypes.T",
408
  "workgroupSize": "tunables.workgroupSize"
 
421
  }
422
  ]
423
  },
424
+ {
425
+ "id": "bias_stream",
426
+ "priority": 20,
427
+ "when": ["biasContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX", "attrs.dilation == 1"],
428
+ "derive": {
429
+ "hasBias": "present.biasT",
430
+ "inputScalar": "dtypes.T",
431
+ "outputScalar": "dtypes.T",
432
+ "workgroupSize": "tunables.streamWorkgroupSize",
433
+ "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
434
+ },
435
+ "passes": [
436
+ {
437
+ "id": "main",
438
+ "name": "VarlenCausalConvWithState.Stream",
439
+ "shader": "varlen-causal-conv-stream.wgsl.jinja",
440
+ "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "bias", "output", "final_state", "params"],
441
+ "dispatch": {
442
+ "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
443
+ "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
444
+ "z": 1
445
+ }
446
+ }
447
+ ]
448
+ },
449
  {
450
  "id": "bias",
451
  "priority": 0,
452
  "when": ["biasContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
453
  "derive": {
454
+ "hasBias": "present.biasT",
 
455
  "inputScalar": "dtypes.T",
456
  "outputScalar": "dtypes.T",
457
  "workgroupSize": "tunables.workgroupSize"
 
470
  }
471
  ]
472
  },
473
+ {
474
+ "id": "state_update_stream",
475
+ "priority": 20,
476
+ "when": ["stateUpdateContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX", "attrs.dilation == 1"],
477
+ "derive": {
478
+ "hasBias": "present.biasT",
479
+ "inputScalar": "dtypes.T",
480
+ "outputScalar": "dtypes.T",
481
+ "workgroupSize": "tunables.streamWorkgroupSize",
482
+ "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
483
+ },
484
+ "passes": [
485
+ {
486
+ "id": "state_update",
487
+ "name": "VarlenCausalConvWithState.StateUpdate",
488
+ "shader": "varlen-state-update.wgsl.jinja",
489
+ "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params__uniform"],
490
+ "dispatch": {
491
+ "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
492
+ "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
493
+ "z": 1
494
+ }
495
+ },
496
+ {
497
+ "id": "main",
498
+ "name": "VarlenCausalConvWithState.Stream",
499
+ "shader": "varlen-causal-conv-stream.wgsl.jinja",
500
+ "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "output", "final_state", "params"],
501
+ "dispatch": {
502
+ "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
503
+ "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
504
+ "z": 1
505
+ }
506
+ }
507
+ ]
508
+ },
509
  {
510
  "id": "state_update",
511
  "priority": 0,
512
  "when": ["stateUpdateContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
513
  "derive": {
514
+ "hasBias": "present.biasT",
 
515
  "inputScalar": "dtypes.T",
516
  "outputScalar": "dtypes.T",
517
  "workgroupSize": "tunables.workgroupSize"
 
521
  "id": "state_update",
522
  "name": "VarlenCausalConvWithState.StateUpdate",
523
  "shader": "varlen-state-update.wgsl.jinja",
524
+ "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params__uniform"],
525
  "dispatch": {
526
  "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
527
  "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
 
541
  }
542
  ]
543
  },
544
+ {
545
+ "id": "bias_state_update_stream",
546
+ "priority": 20,
547
+ "when": ["biasStateUpdateContract", "kernelSize >= 2", "kernelSize <= 8", "tunables.streamChunk >= 1", "floor(tunables.streamChunk) == tunables.streamChunk", "tunables.streamWorkgroupSize >= 1", "floor(tunables.streamWorkgroupSize) == tunables.streamWorkgroupSize", "tunables.streamWorkgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.streamWorkgroupSize <= device.limits.maxComputeWorkgroupSizeX", "attrs.dilation == 1"],
548
+ "derive": {
549
+ "hasBias": "present.biasT",
550
+ "inputScalar": "dtypes.T",
551
+ "outputScalar": "dtypes.T",
552
+ "workgroupSize": "tunables.streamWorkgroupSize",
553
+ "chunkSize": "min(tunables.streamChunk, ceilDiv(totalTokens, batchSize))"
554
+ },
555
+ "passes": [
556
+ {
557
+ "id": "state_update",
558
+ "name": "VarlenCausalConvWithState.StateUpdate",
559
+ "shader": "varlen-state-update.wgsl.jinja",
560
+ "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params__uniform"],
561
+ "dispatch": {
562
+ "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
563
+ "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
564
+ "z": 1
565
+ }
566
+ },
567
+ {
568
+ "id": "main",
569
+ "name": "VarlenCausalConvWithState.Stream",
570
+ "shader": "varlen-causal-conv-stream.wgsl.jinja",
571
+ "bindings": ["input", "weight", "cumulative_sequence_length", "initial_state", "bias", "output", "final_state", "params"],
572
+ "dispatch": {
573
+ "x": "min(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
574
+ "y": "ceilDiv(ceilDiv((ceilDiv(totalTokens, chunkSize) * channels), (workgroupSize)), 65535)",
575
+ "z": 1
576
+ }
577
+ }
578
+ ]
579
+ },
580
  {
581
  "id": "bias_state_update",
582
  "priority": 0,
583
  "when": ["biasStateUpdateContract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
584
  "derive": {
585
+ "hasBias": "present.biasT",
 
586
  "inputScalar": "dtypes.T",
587
  "outputScalar": "dtypes.T",
588
  "workgroupSize": "tunables.workgroupSize"
 
592
  "id": "state_update",
593
  "name": "VarlenCausalConvWithState.StateUpdate",
594
  "shader": "varlen-state-update.wgsl.jinja",
595
+ "bindings": ["input", "cumulative_sequence_length", "capture_count", "state_update", "params__uniform"],
596
  "dispatch": {
597
  "x": "min(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
598
  "y": "ceilDiv(ceilDiv((batchSize * stateUpdateCapacity * channels), (workgroupSize)), 65535)",
build/webgpu/metadata.json CHANGED
@@ -1,31 +1,40 @@
1
  {
2
  "name": "com.microsoft.VarlenCausalConvWithState",
3
- "id": "_com_microsoft_varlencausalconvwithstate_webgpu_7c6635e",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "y4qlCvMEobqusx4BHjuqJFrTxWGJfwFiE3ck+AejJQw=",
11
- "manifest.json": "lcISl3PiMOgRa2gUETuiKNwm2RcbERhUNsPKM4yAfbc=",
12
- "test.json": "66AJhdzvoC4y/XYc3VfVW0rgbaNmqskI834QnP591ng=",
13
- "varlen-causal-conv-stream.wgsl.jinja": "0FWRyGR35U5ZB7Fb4Q1MFQHx90PALdOSNk1JoyeQCdQ=",
14
- "varlen-causal-conv.wgsl.jinja": "OhLAVTlZId7AFw0Hfp8DWRcNZsgoMZgxpWdKxGdp4Ks=",
15
- "varlen-state-update.wgsl.jinja": "aTGir5h/LQkg5ami41pKaH4EG8ybaltnnM0oJSJ6Neg="
 
16
  }
17
  },
18
- "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
19
  "webgpu": {
20
- "manifestSpec": "2.0",
21
  "variants": {
 
 
 
 
 
 
 
 
22
  "plain_stream": ["varlen-causal-conv-stream.wgsl.jinja"],
23
- "bias_stream": ["varlen-causal-conv-stream.wgsl.jinja"],
24
- "state_update_stream": ["varlen-causal-conv-stream.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
25
- "bias_state_update_stream": ["varlen-causal-conv-stream.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
26
  "plain": ["varlen-causal-conv.wgsl.jinja"],
 
27
  "bias": ["varlen-causal-conv.wgsl.jinja"],
 
28
  "state_update": ["varlen-causal-conv.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
 
29
  "bias_state_update": ["varlen-causal-conv.wgsl.jinja", "varlen-state-update.wgsl.jinja"]
30
  }
31
  }
 
1
  {
2
  "name": "com.microsoft.VarlenCausalConvWithState",
3
+ "id": "_com_microsoft_varlencausalconvwithstate_webgpu_a11f8c1",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "0VIH7EDZLjXt1ZgOmAA4oDeWfhadYrY8VJOkvCIbDlg=",
11
+ "manifest.json": "uEm8UogYuIyHJRhVKAebGdlv7yxdEIRJs7rLubPctf0=",
12
+ "test.json": "o32EnBOjkjEdcFA/wkWFt1ywXTCCN20uEndKoBIoYB8=",
13
+ "varlen-causal-conv-stream.wgsl.jinja": "th2IihEJq0gHEpdRm2PNN8MleOvgSZlAstRE0W6wXmo=",
14
+ "varlen-causal-conv.wgsl.jinja": "t9K8tccFYN5I9uDeOAUs+gtNaHrQS6iy2S0IbQtp4ik=",
15
+ "varlen-state-update.wgsl.jinja": "aTGir5h/LQkg5ami41pKaH4EG8ybaltnnM0oJSJ6Neg=",
16
+ "varlen-unit-sequence.wgsl.jinja": "g2tYe2vdYUlDWySCvrljSsqUAuodUmjYnOW/6tr51jw="
17
  }
18
  },
19
+ "provenance": { "kernel": { "sha": "6fdf6301e2bbcc2f03bf1eaf493b7ad55ef33afc", "dirty": false } },
20
  "webgpu": {
21
+ "manifestSpec": "2.1",
22
  "variants": {
23
+ "unit_plain": ["varlen-unit-sequence.wgsl.jinja"],
24
+ "unit_bias": ["varlen-unit-sequence.wgsl.jinja"],
25
+ "unit_state_update": ["varlen-unit-sequence.wgsl.jinja"],
26
+ "unit_bias_state_update": ["varlen-unit-sequence.wgsl.jinja"],
27
+ "capture_copy_state_update_stream": ["varlen-causal-conv-stream.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
28
+ "capture_copy_state_update": ["varlen-causal-conv.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
29
+ "capture_copy_bias_state_update_stream": ["varlen-causal-conv-stream.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
30
+ "capture_copy_bias_state_update": ["varlen-causal-conv.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
31
  "plain_stream": ["varlen-causal-conv-stream.wgsl.jinja"],
 
 
 
32
  "plain": ["varlen-causal-conv.wgsl.jinja"],
33
+ "bias_stream": ["varlen-causal-conv-stream.wgsl.jinja"],
34
  "bias": ["varlen-causal-conv.wgsl.jinja"],
35
+ "state_update_stream": ["varlen-causal-conv-stream.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
36
  "state_update": ["varlen-causal-conv.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
37
+ "bias_state_update_stream": ["varlen-causal-conv-stream.wgsl.jinja", "varlen-state-update.wgsl.jinja"],
38
  "bias_state_update": ["varlen-causal-conv.wgsl.jinja", "varlen-state-update.wgsl.jinja"]
39
  }
40
  }
build/webgpu/test.json CHANGED
The diff for this file is too large to render. See raw diff
 
build/webgpu/varlen-causal-conv-stream.wgsl.jinja CHANGED
@@ -6,9 +6,7 @@ fn sequence_of_token(token: u32, batch_size: u32) -> u32 {
6
  }
7
  }
8
  return batch_size - 1u;
9
- }
10
- {%- endmacro %}
11
-
12
  {% macro varlen_conv_helpers(useSilu) %}
13
  // virtual[pos] is one sequence's raw sample stream: the kernelSize-1 carried samples
14
  // from initial_state, then the sequence's own tokens. Reading the window through this
@@ -21,15 +19,22 @@ fn virtual_sample(start: u32, state_base: u32, channel: u32, state_length: u32,
21
  return f32(initial_state[state_base + pos]);
22
  }
23
 
24
- fn activate(value: f32) -> f32 {
 
 
 
25
  {% if useSilu %}
26
  return value / (1.0 + exp(-value));
27
  {% else %}
28
  return value;
29
  {% endif %}
30
- }
31
- {%- endmacro %}
32
  {% set stateLength = kernelSize - 1 %}
 
 
 
 
 
33
  {{ env.wgsl.resourceDeclarations }}
34
 
35
  // com.microsoft.VarlenCausalConvWithState, streaming pass.
@@ -49,9 +54,7 @@ const STATE_LENGTH: u32 = {{ stateLength }}u;
49
 
50
  @compute @workgroup_size(WG, 1, 1)
51
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
52
- // 2D-folded flat index: gid.y carries the high bits past the
53
- // per-axis dispatch fold width. Reduces to gid.x when the dispatch does not fold.
54
- let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
55
  let blocks = (params.totalTokens + CHUNK - 1u) / CHUNK;
56
  if (index >= blocks * params.channels) {
57
  return;
 
6
  }
7
  }
8
  return batch_size - 1u;
9
+ }{% endmacro %}
 
 
10
  {% macro varlen_conv_helpers(useSilu) %}
11
  // virtual[pos] is one sequence's raw sample stream: the kernelSize-1 carried samples
12
  // from initial_state, then the sequence's own tokens. Reading the window through this
 
19
  return f32(initial_state[state_base + pos]);
20
  }
21
 
22
+ {{ varlen_conv_activation(useSilu) }}{% endmacro %}
23
+ {% macro varlen_conv_activation(useSilu, width=1) %}
24
+ {% set valueType = "f32" if width == 1 else "vec" ~ width ~ "<f32>" %}
25
+ fn activate(value: {{ valueType }}) -> {{ valueType }} {
26
  {% if useSilu %}
27
  return value / (1.0 + exp(-value));
28
  {% else %}
29
  return value;
30
  {% endif %}
31
+ }{% endmacro %}
 
32
  {% set stateLength = kernelSize - 1 %}
33
+ {% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
34
+ {% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
35
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
36
+ // per-axis workgroup fold width.
37
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};{% endmacro %}
38
  {{ env.wgsl.resourceDeclarations }}
39
 
40
  // com.microsoft.VarlenCausalConvWithState, streaming pass.
 
54
 
55
  @compute @workgroup_size(WG, 1, 1)
56
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
57
+ {{ flat_index_2d("WG", "index", "") }}
 
 
58
  let blocks = (params.totalTokens + CHUNK - 1u) / CHUNK;
59
  if (index >= blocks * params.channels) {
60
  return;
build/webgpu/varlen-causal-conv.wgsl.jinja CHANGED
@@ -6,9 +6,7 @@ fn sequence_of_token(token: u32, batch_size: u32) -> u32 {
6
  }
7
  }
8
  return batch_size - 1u;
9
- }
10
- {%- endmacro %}
11
-
12
  {% macro varlen_conv_helpers(useSilu) %}
13
  // virtual[pos] is one sequence's raw sample stream: the kernelSize-1 carried samples
14
  // from initial_state, then the sequence's own tokens. Reading the window through this
@@ -21,14 +19,24 @@ fn virtual_sample(start: u32, state_base: u32, channel: u32, state_length: u32,
21
  return f32(initial_state[state_base + pos]);
22
  }
23
 
24
- fn activate(value: f32) -> f32 {
 
 
 
25
  {% if useSilu %}
26
  return value / (1.0 + exp(-value));
27
  {% else %}
28
  return value;
29
  {% endif %}
30
- }
31
- {%- endmacro %}
 
 
 
 
 
 
 
32
  {{ env.wgsl.resourceDeclarations }}
33
 
34
  // com.microsoft.VarlenCausalConvWithState, portable pass.
@@ -45,15 +53,10 @@ const WG: u32 = {{ workgroupSize }}u;
45
 
46
  @compute @workgroup_size(WG, 1, 1)
47
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
48
- // 2D-folded flat index: gid.y carries the high bits past the
49
- // per-axis dispatch fold width. Reduces to gid.x when the dispatch does not fold.
50
- let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
51
- if (index >= params.totalTokens * params.channels) {
52
- return;
53
- }
54
  let token = index / params.channels;
55
  let channel = index % params.channels;
56
- let state_length = params.kernelSize - 1u;
57
 
58
  // A malformed schedule returns before any buffer access. This is memory-safety
59
  // containment, not validation; outputs are unspecified for such a request.
@@ -75,7 +78,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
75
  acc = f32(bias[channel]);
76
  {% endif %}
77
  for (var k: u32 = 0u; k < params.kernelSize; k = k + 1u) {
78
- let value = virtual_sample(start, state_base, channel, state_length, local + k);
79
  acc = acc + value * f32(weight[channel * params.kernelSize + k]);
80
  }
81
  output[index] = {{ outputScalar }}(activate(acc));
 
6
  }
7
  }
8
  return batch_size - 1u;
9
+ }{% endmacro %}
 
 
10
  {% macro varlen_conv_helpers(useSilu) %}
11
  // virtual[pos] is one sequence's raw sample stream: the kernelSize-1 carried samples
12
  // from initial_state, then the sequence's own tokens. Reading the window through this
 
19
  return f32(initial_state[state_base + pos]);
20
  }
21
 
22
+ {{ varlen_conv_activation(useSilu) }}{% endmacro %}
23
+ {% macro varlen_conv_activation(useSilu, width=1) %}
24
+ {% set valueType = "f32" if width == 1 else "vec" ~ width ~ "<f32>" %}
25
+ fn activate(value: {{ valueType }}) -> {{ valueType }} {
26
  {% if useSilu %}
27
  return value / (1.0 + exp(-value));
28
  {% else %}
29
  return value;
30
  {% endif %}
31
+ }{% endmacro %}
32
+ {% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
33
+ {% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
34
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
35
+ // per-axis workgroup fold width.
36
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};
37
+ if ({{ name }} >= {{ bound }}) {
38
+ return;
39
+ }{% endmacro %}
40
  {{ env.wgsl.resourceDeclarations }}
41
 
42
  // com.microsoft.VarlenCausalConvWithState, portable pass.
 
53
 
54
  @compute @workgroup_size(WG, 1, 1)
55
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
56
+ {{ flat_index_2d("WG", "index", "params.totalTokens * params.channels") }}
 
 
 
 
 
57
  let token = index / params.channels;
58
  let channel = index % params.channels;
59
+ let state_length = {% if causalDilation != 1 %}(params.kernelSize - 1u) * {{ causalDilation }}u{% else %}params.kernelSize - 1u{% endif %};
60
 
61
  // A malformed schedule returns before any buffer access. This is memory-safety
62
  // containment, not validation; outputs are unspecified for such a request.
 
78
  acc = f32(bias[channel]);
79
  {% endif %}
80
  for (var k: u32 = 0u; k < params.kernelSize; k = k + 1u) {
81
+ let value = virtual_sample(start, state_base, channel, state_length, local + k{% if causalDilation != 1 %} * {{ causalDilation }}u{% endif %});
82
  acc = acc + value * f32(weight[channel * params.kernelSize + k]);
83
  }
84
  output[index] = {{ outputScalar }}(activate(acc));
build/webgpu/varlen-unit-sequence.wgsl.jinja ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% macro varlen_conv_activation(useSilu, width=1) %}
2
+ {% set valueType = "f32" if width == 1 else "vec" ~ width ~ "<f32>" %}
3
+ fn activate(value: {{ valueType }}) -> {{ valueType }} {
4
+ {% if useSilu %}
5
+ return value / ({% if width == 1 %}1.0{% else %}{{ valueType }}(1.0){% endif %} + exp(-value));
6
+ {% else %}
7
+ return value;
8
+ {% endif %}
9
+ }{% endmacro %}
10
+ {% set stateLength = kernelSize - 1 %}
11
+ {{ env.wgsl.resourceDeclarations }}
12
+ {{ varlen_conv_activation(useSilu, unitVectorWidth) }}
13
+ const WG: u32 = {{ unitWg }}u;
14
+ @compute @workgroup_size(WG)
15
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
16
+ let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
17
+ let channelGroups = params.channels / {{ unitVectorWidth }}u;
18
+ if (index >= params.totalTokens * channelGroups) { return; }
19
+ // Strictly increasing integer prefix sums over T == batch imply one token per sequence.
20
+ let sequence = index / channelGroups;
21
+ let channel = index % channelGroups;
22
+ // Loads follow the contiguous channel-major storage layout. Their components
23
+ // are rearranged into channel vectors at compile time for the original
24
+ // ordered tap sum.
25
+ let weightBase = channel * {{ kernelSize }}u;
26
+ let stateBase = (sequence * channelGroups + channel) * {{ stateLength }}u;
27
+ {% for i in range(kernelSize) %}
28
+ let packedW{{ i }} = weight[weightBase + {{ i }}u];
29
+ {% endfor %}
30
+ {% for i in range(stateLength) %}
31
+ let packedS{{ i }} = initial_state[stateBase + {{ i }}u];
32
+ {% endfor %}
33
+ {% for i in range(kernelSize) %}
34
+ let w{{ i }} = {{ unitValueType }}({% for c in range(unitVectorWidth) %}packedW{{ ((c * kernelSize + i) / unitVectorWidth)|int }}{% if unitVectorWidth > 1 %}[{{ (c * kernelSize + i) % unitVectorWidth }}]{% endif %}{% if not loop.last %}, {% endif %}{% endfor %});
35
+ {% endfor %}
36
+ {% for i in range(stateLength) %}
37
+ let s{{ i }} = {{ unitValueType }}({% for c in range(unitVectorWidth) %}packedS{{ ((c * stateLength + i) / unitVectorWidth)|int }}{% if unitVectorWidth > 1 %}[{{ (c * stateLength + i) % unitVectorWidth }}]{% endif %}{% if not loop.last %}, {% endif %}{% endfor %});
38
+ {% endfor %}
39
+ {% if hasBias %}
40
+ let biasValue = {{ unitValueType }}(bias[channel]);
41
+ {% else %}
42
+ let biasValue = {{ unitValueType }}(0.0);
43
+ {% endif %}
44
+ let x = {{ unitValueType }}(input[index]);
45
+ output[index] = {{ unitStorageType }}(activate(biasValue{% for i in range(stateLength) %} + s{{ i }} * w{{ i }}{% endfor %} + x * w{{ kernelSize - 1 }}));
46
+ // Repack the shifted state without scalar scatter stores.
47
+ {% for i in range(stateLength) %}
48
+ final_state[stateBase + {{ i }}u] = {{ unitStorageType }}({% for c in range(unitVectorWidth) %}{% set scalarOffset = i * unitVectorWidth + c %}{% set tap = scalarOffset % stateLength %}{% if tap == stateLength - 1 %}x{% else %}s{{ tap + 1 }}{% endif %}{% if unitVectorWidth > 1 %}[{{ (scalarOffset / stateLength)|int }}]{% endif %}{% if not loop.last %}, {% endif %}{% endfor %});
49
+ {% endfor %}
50
+ {% if hasCapture %}
51
+ var captured = {{ unitStorageType }}(0.0);
52
+ if (capture_count[sequence] > 0) { captured = {{ unitStorageType }}(x); }
53
+ let updateBase = (sequence * {{ stateUpdateCapacity }}u) * channelGroups + channel;
54
+ state_update[updateBase] = captured;
55
+ {% for slot in range(1, stateUpdateCapacity) %}
56
+ state_update[updateBase + {{ slot }}u * channelGroups] = {{ unitStorageType }}(0.0);
57
+ {% endfor %}
58
+ {% endif %}
59
+ }