sync 91d990483a17
Browse files- README.md +19 -9
- build/webgpu/bench.json +50 -1
- build/webgpu/datamove-flat-copy.wgsl.jinja +1 -4
- build/webgpu/datamove-transpose-2d-tiled-scalar.wgsl.jinja +9 -12
- build/webgpu/datamove-transpose-2d-tiled.wgsl.jinja +9 -11
- build/webgpu/datamove-transpose-vec4.wgsl.jinja +7 -10
- build/webgpu/manifest.json +95 -161
- build/webgpu/metadata.json +20 -11
- build/webgpu/test.json +41 -10
- build/webgpu/transpose.wgsl.jinja +15 -17
README.md
CHANGED
|
@@ -18,15 +18,15 @@ See the [ONNX `Transpose` spec](https://onnx.ai/onnx/operators/onnx__Transpose.h
|
|
| 18 |
|
| 19 |
## Inputs
|
| 20 |
|
| 21 |
-
| Name |
|
| 22 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
-
| `
|
| 24 |
|
| 25 |
## Outputs
|
| 26 |
|
| 27 |
-
| Name |
|
| 28 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 29 |
-
| `
|
| 30 |
|
| 31 |
## Attributes
|
| 32 |
|
|
@@ -42,9 +42,16 @@ Attributes and default values (overridable per request):
|
|
| 42 |
| --- | --- |
|
| 43 |
| `T` | `float32`, `float16`, `int32`, `int16`, `uint32`, `uint8`, `int8`, `bool` |
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
## Files
|
| 46 |
|
| 47 |
-
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
|
| 48 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 49 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 50 |
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
|
@@ -56,15 +63,18 @@ Attributes and default values (overridable per request):
|
|
| 56 |
|
| 57 |
## Use with `@huggingface/kernels`
|
| 58 |
|
| 59 |
-
|
|
|
|
|
|
|
| 60 |
|
| 61 |
-
|
| 62 |
|
| 63 |
-
|
| 64 |
|
| 65 |
-
|
| 66 |
|
| 67 |
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
|
|
|
| 68 |
|
| 69 |
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 70 |
|
|
|
|
| 18 |
|
| 19 |
## Inputs
|
| 20 |
|
| 21 |
+
| Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
|
| 22 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
+
| `x` | `data` | `T` | — | — | The input tensor to transpose. | required |
|
| 24 |
|
| 25 |
## Outputs
|
| 26 |
|
| 27 |
+
| Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
|
| 28 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 29 |
+
| `y` | `transposed` | `T` | same as `x` | — | The transposed output tensor with permuted axes. | required |
|
| 30 |
|
| 31 |
## Attributes
|
| 32 |
|
|
|
|
| 42 |
| --- | --- |
|
| 43 |
| `T` | `float32`, `float16`, `int32`, `int16`, `uint32`, `uint8`, `int8`, `bool` |
|
| 44 |
|
| 45 |
+
## Implementation variants
|
| 46 |
+
|
| 47 |
+
One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
|
| 48 |
+
|
| 49 |
+
- `tiled_scalar` — Stages ragged two-dimensional transpose planes through a padded workgroup tile so reads and writes remain coalesced when vec4 alignment is unavailable.
|
| 50 |
+
- `inner_vec4` — Vectorizes the contiguous innermost dimension for permutations that leave that axis in place. It assigns multiple vectors per invocation only when enough invocations remain to keep the dispatch populated.
|
| 51 |
+
|
| 52 |
## Files
|
| 53 |
|
| 54 |
+
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
|
| 55 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 56 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 57 |
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
|
|
|
| 63 |
|
| 64 |
## Use with `@huggingface/kernels`
|
| 65 |
|
| 66 |
+
```sh
|
| 67 |
+
npm install --save-exact @huggingface/kernels@0.0.1-preview.2
|
| 68 |
+
```
|
| 69 |
|
| 70 |
+
Outputs with inferable metadata are allocated automatically. Explicit `outputs` entries request optional results or provide metadata that cannot be inferred from the supplied inputs and attributes.
|
| 71 |
|
| 72 |
+
This example supplies explicit metadata for:
|
| 73 |
|
| 74 |
+
- `y`
|
| 75 |
|
| 76 |
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
| 77 |
+
It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
|
| 78 |
|
| 79 |
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 80 |
|
build/webgpu/bench.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
{
|
| 2 |
-
"op": "ai.onnx.Transpose",
|
| 3 |
"tunableSpace": {
|
| 4 |
"WORKGROUP_SIZE": [64, 128, 256],
|
| 5 |
"INNER_VEC4_ITEMS_PER_THREAD": [1, 2, 4, 8],
|
|
@@ -216,6 +215,56 @@
|
|
| 216 |
"inputs": { "x": { "shape": [1812, 384], "dtype": "float32", "dist": "normal", "seed": 9451, "scale": 0.2 } },
|
| 217 |
"outputs": { "y": { "shape": [384, 1812], "dtype": "float32" } },
|
| 218 |
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
}
|
| 220 |
]
|
| 221 |
}
|
|
|
|
| 1 |
{
|
|
|
|
| 2 |
"tunableSpace": {
|
| 3 |
"WORKGROUP_SIZE": [64, 128, 256],
|
| 4 |
"INNER_VEC4_ITEMS_PER_THREAD": [1, 2, 4, 8],
|
|
|
|
| 215 |
"inputs": { "x": { "shape": [1812, 384], "dtype": "float32", "dist": "normal", "seed": 9451, "scale": 0.2 } },
|
| 216 |
"outputs": { "y": { "shape": [384, 1812], "dtype": "float32" } },
|
| 217 |
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
|
| 218 |
+
},
|
| 219 |
+
{
|
| 220 |
+
"name": "transpose-f32-rank6-swin-window-partition-perm013245-pathology",
|
| 221 |
+
"preset": "edge",
|
| 222 |
+
"provenance": {
|
| 223 |
+
"notes": "Rank-6 window partition (swap of the two window-grid axes with a contiguous 96-wide last axis) at a batch-8 Swin 56x56 stage. Rank 6 is outside every tiled and inner-vec4 pattern, so the generic strided kernel is the selected route; the permutation is its own inverse, so one case covers both directions."
|
| 224 |
+
},
|
| 225 |
+
"vars": { "count": 2408448, "dtype": "float32" },
|
| 226 |
+
"attrs": { "perm": [0, 1, 3, 2, 4, 5] },
|
| 227 |
+
"inputs": {
|
| 228 |
+
"x": { "shape": [8, 8, 7, 8, 7, 96], "dtype": "float32", "dist": "normal", "seed": 154, "scale": 0.2 }
|
| 229 |
+
},
|
| 230 |
+
"outputs": { "y": { "shape": [8, 8, 8, 7, 7, 96], "dtype": "float32" } },
|
| 231 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"name": "transpose-f32-rank6-swin-window-partition-rank4-tiled-control",
|
| 235 |
+
"preset": "edge",
|
| 236 |
+
"provenance": {
|
| 237 |
+
"notes": "Equal-byte tiled control for the rank-6 window-partition case: the same 8x56x56x96 elements as an NHWC-to-NCHW permutation, which takes the vec4 staged transpose tile."
|
| 238 |
+
},
|
| 239 |
+
"vars": { "count": 2408448, "dtype": "float32" },
|
| 240 |
+
"attrs": { "perm": [0, 3, 1, 2] },
|
| 241 |
+
"inputs": { "x": { "shape": [8, 56, 56, 96], "dtype": "float32", "dist": "normal", "seed": 155, "scale": 0.2 } },
|
| 242 |
+
"outputs": { "y": { "shape": [8, 96, 56, 56], "dtype": "float32" } },
|
| 243 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"name": "transpose-f32-rank4-perm0321-8x64x197x64-pathology",
|
| 247 |
+
"preset": "edge",
|
| 248 |
+
"provenance": {
|
| 249 |
+
"notes": "Rank-4 permutation [0,3,2,1] at a 197-token, 64-head-dim attention shape: axis 3 leaves the last position and the permutation is none of the three tiled rank-4 patterns, so the generic strided kernel is the selected route."
|
| 250 |
+
},
|
| 251 |
+
"vars": { "count": 6455296, "dtype": "float32" },
|
| 252 |
+
"attrs": { "perm": [0, 3, 2, 1] },
|
| 253 |
+
"inputs": { "x": { "shape": [8, 64, 197, 64], "dtype": "float32", "dist": "normal", "seed": 156, "scale": 0.2 } },
|
| 254 |
+
"outputs": { "y": { "shape": [8, 64, 197, 64], "dtype": "float32" } },
|
| 255 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
|
| 256 |
+
},
|
| 257 |
+
{
|
| 258 |
+
"name": "transpose-f32-rank4-perm0213-8x64x197x64-control",
|
| 259 |
+
"preset": "edge",
|
| 260 |
+
"provenance": {
|
| 261 |
+
"notes": "Control for the [0,3,2,1] case: the same tensor under the attention-layout permutation [0,2,1,3] keeps the innermost axis in place and takes the inner-vec4 route."
|
| 262 |
+
},
|
| 263 |
+
"vars": { "count": 6455296, "dtype": "float32" },
|
| 264 |
+
"attrs": { "perm": [0, 2, 1, 3] },
|
| 265 |
+
"inputs": { "x": { "shape": [8, 64, 197, 64], "dtype": "float32", "dist": "normal", "seed": 157, "scale": 0.2 } },
|
| 266 |
+
"outputs": { "y": { "shape": [8, 197, 64, 64], "dtype": "float32" } },
|
| 267 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
|
| 268 |
}
|
| 269 |
]
|
| 270 |
}
|
build/webgpu/datamove-flat-copy.wgsl.jinja
CHANGED
|
@@ -1,9 +1,6 @@
|
|
| 1 |
-
{% if usesF16 %}
|
| 2 |
-
enable f16;
|
| 3 |
-
{% endif %}
|
| 4 |
{{ env.wgsl.resourceDeclarations }}
|
| 5 |
|
| 6 |
-
const COUNT: u32 = {{
|
| 7 |
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 8 |
|
| 9 |
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
{{ env.wgsl.resourceDeclarations }}
|
| 2 |
|
| 3 |
+
const COUNT: u32 = {{ count }}u;
|
| 4 |
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 5 |
|
| 6 |
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
build/webgpu/datamove-transpose-2d-tiled-scalar.wgsl.jinja
CHANGED
|
@@ -1,24 +1,21 @@
|
|
| 1 |
// Tiled, optionally batched 2-D transpose over scalar elements.
|
| 2 |
//
|
| 3 |
-
// A 32x32 tile enters workgroup memory through
|
| 4 |
-
// through
|
| 5 |
-
//
|
| 6 |
-
// fall back to a generic copy whose reads and writes cannot both be coalesced.
|
| 7 |
//
|
| 8 |
// Layout contract:
|
| 9 |
// - src is row-major [batch, ROWS, COLS]
|
| 10 |
// - dst is row-major [batch, COLS, ROWS]
|
| 11 |
// - dispatch is x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
|
| 12 |
//
|
| 13 |
-
// The extra column
|
| 14 |
-
|
| 15 |
-
enable f16;
|
| 16 |
-
{% endif %}
|
| 17 |
{{ env.wgsl.resourceDeclarations }}
|
| 18 |
|
| 19 |
-
const ROWS: u32 = {{
|
| 20 |
-
const COLS: u32 = {{
|
| 21 |
-
const MAT: u32 = {{
|
| 22 |
const TILE: u32 = 32u;
|
| 23 |
const ROW_STEP: u32 = 8u;
|
| 24 |
|
|
@@ -46,7 +43,7 @@ fn main(
|
|
| 46 |
|
| 47 |
// Write: lid.x runs along the destination row, which is the source column
|
| 48 |
// direction — the transpose happens in the tile index, not in the memory
|
| 49 |
-
// access, so this side
|
| 50 |
for (var i = 0u; i < TILE; i = i + ROW_STEP) {
|
| 51 |
let out_row = col_base + lid.y + i;
|
| 52 |
let out_col = row_base + lid.x;
|
|
|
|
| 1 |
// Tiled, optionally batched 2-D transpose over scalar elements.
|
| 2 |
//
|
| 3 |
+
// A 32x32 tile enters workgroup memory through consecutive row reads and leaves
|
| 4 |
+
// through consecutive transposed-row writes. Guarded edges make this valid for
|
| 5 |
+
// any extent.
|
|
|
|
| 6 |
//
|
| 7 |
// Layout contract:
|
| 8 |
// - src is row-major [batch, ROWS, COLS]
|
| 9 |
// - dst is row-major [batch, COLS, ROWS]
|
| 10 |
// - dispatch is x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
|
| 11 |
//
|
| 12 |
+
// The extra column changes the transposed workgroup-memory stride and reduces
|
| 13 |
+
// bank-conflict risk on banked implementations.
|
|
|
|
|
|
|
| 14 |
{{ env.wgsl.resourceDeclarations }}
|
| 15 |
|
| 16 |
+
const ROWS: u32 = {{ rowCount }}u;
|
| 17 |
+
const COLS: u32 = {{ cols }}u;
|
| 18 |
+
const MAT: u32 = {{ rowCount * cols }}u;
|
| 19 |
const TILE: u32 = 32u;
|
| 20 |
const ROW_STEP: u32 = 8u;
|
| 21 |
|
|
|
|
| 43 |
|
| 44 |
// Write: lid.x runs along the destination row, which is the source column
|
| 45 |
// direction — the transpose happens in the tile index, not in the memory
|
| 46 |
+
// access, so this side also traverses consecutive addresses.
|
| 47 |
for (var i = 0u; i < TILE; i = i + ROW_STEP) {
|
| 48 |
let out_row = col_base + lid.y + i;
|
| 49 |
let out_col = row_base + lid.x;
|
build/webgpu/datamove-transpose-2d-tiled.wgsl.jinja
CHANGED
|
@@ -5,19 +5,17 @@
|
|
| 5 |
// - ROWS % 4 == 0 and COLS % 4 == 0, so both buffers bind as vec4<T>
|
| 6 |
// - dispatch: x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
|
| 7 |
//
|
| 8 |
-
// Each 8x32 workgroup moves one 32x32 scalar tile through workgroup memory
|
| 9 |
-
//
|
| 10 |
-
// column
|
| 11 |
-
|
| 12 |
-
enable f16;
|
| 13 |
-
{% endif %}
|
| 14 |
{{ env.wgsl.resourceDeclarations }}
|
| 15 |
|
| 16 |
-
const ROWS: u32 = {{
|
| 17 |
-
const COLS: u32 = {{
|
| 18 |
-
const ROWS_V: u32 = {{ ((
|
| 19 |
-
const COLS_V: u32 = {{ ((
|
| 20 |
-
const MAT_V: u32 = {{ (((
|
| 21 |
const TILE: u32 = 32u;
|
| 22 |
|
| 23 |
var<workgroup> tile: array<array<{{ scalar }}, 33>, 32>;
|
|
|
|
| 5 |
// - ROWS % 4 == 0 and COLS % 4 == 0, so both buffers bind as vec4<T>
|
| 6 |
// - dispatch: x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
|
| 7 |
//
|
| 8 |
+
// Each 8x32 workgroup moves one 32x32 scalar tile through workgroup memory.
|
| 9 |
+
// Global loads and stores each traverse consecutive vec4 addresses. The extra
|
| 10 |
+
// workgroup-memory column changes the transposed stride and reduces
|
| 11 |
+
// bank-conflict risk on banked implementations.
|
|
|
|
|
|
|
| 12 |
{{ env.wgsl.resourceDeclarations }}
|
| 13 |
|
| 14 |
+
const ROWS: u32 = {{ rowCount }}u;
|
| 15 |
+
const COLS: u32 = {{ cols }}u;
|
| 16 |
+
const ROWS_V: u32 = {{ ((rowCount / 4) | int) }}u;
|
| 17 |
+
const COLS_V: u32 = {{ ((cols / 4) | int) }}u;
|
| 18 |
+
const MAT_V: u32 = {{ (((rowCount * cols) / 4) | int) }}u;
|
| 19 |
const TILE: u32 = 32u;
|
| 20 |
|
| 21 |
var<workgroup> tile: array<array<{{ scalar }}, 33>, 32>;
|
build/webgpu/datamove-transpose-vec4.wgsl.jinja
CHANGED
|
@@ -2,27 +2,24 @@
|
|
| 2 |
// axis in place. Its dimension is a multiple of four, so both buffers bind as
|
| 3 |
// vec4<T>. The index fold uses effective shapes whose last dimension is divided
|
| 4 |
// by four, enabling 128-bit loads and stores.
|
| 5 |
-
{% if usesF16 %}
|
| 6 |
-
enable f16;
|
| 7 |
-
{% endif %}
|
| 8 |
{{ env.wgsl.resourceDeclarations }}
|
| 9 |
|
| 10 |
-
const COUNT: u32 = {{
|
| 11 |
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 12 |
|
| 13 |
fn input_offset(out_index: u32) -> u32 {
|
| 14 |
var rem = out_index;
|
| 15 |
var offset = 0u;
|
| 16 |
-
{% for out_axis in range(
|
| 17 |
{% set out_stride = namespace(value=1) %}
|
| 18 |
-
{% for j in range(out_axis + 1,
|
| 19 |
-
{% set dj = ((
|
| 20 |
{% set out_stride.value = out_stride.value * dj %}
|
| 21 |
{% endfor %}
|
| 22 |
-
{% set in_axis =
|
| 23 |
{% set in_stride = namespace(value=1) %}
|
| 24 |
-
{% for j in range(in_axis + 1,
|
| 25 |
-
{% set dj = ((
|
| 26 |
{% set in_stride.value = in_stride.value * dj %}
|
| 27 |
{% endfor %}
|
| 28 |
let coord{{ out_axis }} = rem / {{ out_stride.value }}u;
|
|
|
|
| 2 |
// axis in place. Its dimension is a multiple of four, so both buffers bind as
|
| 3 |
// vec4<T>. The index fold uses effective shapes whose last dimension is divided
|
| 4 |
// by four, enabling 128-bit loads and stores.
|
|
|
|
|
|
|
|
|
|
| 5 |
{{ env.wgsl.resourceDeclarations }}
|
| 6 |
|
| 7 |
+
const COUNT: u32 = {{ count }}u;
|
| 8 |
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 9 |
|
| 10 |
fn input_offset(out_index: u32) -> u32 {
|
| 11 |
var rem = out_index;
|
| 12 |
var offset = 0u;
|
| 13 |
+
{% for out_axis in range(rank) %}
|
| 14 |
{% set out_stride = namespace(value=1) %}
|
| 15 |
+
{% for j in range(out_axis + 1, rank) %}
|
| 16 |
+
{% set dj = ((outputShape[j] / 4) | int) if j == rank - 1 else outputShape[j] %}
|
| 17 |
{% set out_stride.value = out_stride.value * dj %}
|
| 18 |
{% endfor %}
|
| 19 |
+
{% set in_axis = perm[out_axis] %}
|
| 20 |
{% set in_stride = namespace(value=1) %}
|
| 21 |
+
{% for j in range(in_axis + 1, rank) %}
|
| 22 |
+
{% set dj = ((inputShape[j] / 4) | int) if j == rank - 1 else inputShape[j] %}
|
| 23 |
{% set in_stride.value = in_stride.value * dj %}
|
| 24 |
{% endfor %}
|
| 25 |
let coord{{ out_axis }} = rem / {{ out_stride.value }}u;
|
build/webgpu/manifest.json
CHANGED
|
@@ -2,171 +2,96 @@
|
|
| 2 |
"domain": "ai.onnx",
|
| 3 |
"name": "Transpose",
|
| 4 |
"sinceVersion": 13,
|
| 5 |
-
"
|
| 6 |
-
"
|
| 7 |
-
"
|
| 8 |
-
{
|
| 9 |
-
"role": "transposed",
|
| 10 |
-
"dtype": "T",
|
| 11 |
-
"description": "The transposed output tensor with permuted axes.",
|
| 12 |
-
"rank": "ranks.data"
|
| 13 |
-
}
|
| 14 |
-
],
|
| 15 |
-
"attributes": {},
|
| 16 |
-
"attributeDescriptions": {
|
| 17 |
-
"perm": "Optional permutation of the input axes. It must contain every axis from 0 through rank - 1 exactly once. When omitted, the axes are reversed."
|
| 18 |
-
},
|
| 19 |
"typeConstraints": { "T": ["float32", "float16", "int32", "int16", "uint32", "uint8", "int8", "bool"] },
|
| 20 |
-
"
|
| 21 |
-
"
|
| 22 |
-
"
|
| 23 |
-
|
| 24 |
-
"tunables": { "WORKGROUP_SIZE": 256, "INNER_VEC4_ITEMS_PER_THREAD": 4, "INNER_VEC4_MIN_THREADS": 65536 },
|
| 25 |
-
"tunableDescriptions": {
|
| 26 |
-
"INNER_VEC4_ITEMS_PER_THREAD": "Sets the target maximum number of vec4 items assigned to each invocation by the inner-axis vector route.",
|
| 27 |
-
"INNER_VEC4_MIN_THREADS": "Sets the minimum logical thread count retained when shrinking the inner-axis vector dispatch."
|
| 28 |
},
|
| 29 |
"derive": {
|
| 30 |
"dtypeSupported": "f16Ok(dtypes.T)",
|
| 31 |
-
"
|
| 32 |
-
"
|
| 33 |
-
"
|
| 34 |
-
"
|
| 35 |
-
"rank4IO": "ranks.data == 4 and ranks.transposed == 4",
|
| 36 |
-
"rank5IO": "ranks.data == 5 and ranks.transposed == 5",
|
| 37 |
-
"rank6IO": "ranks.data == 6 and ranks.transposed == 6",
|
| 38 |
-
"rank7IO": "ranks.data == 7 and ranks.transposed == 7",
|
| 39 |
"permPresent": "has(attrs, \"perm\")",
|
| 40 |
-
"permContract": "not permPresent or ((attrs.perm | length) == ranks.
|
| 41 |
-
"
|
| 42 |
-
"
|
| 43 |
-
"
|
| 44 |
-
"
|
| 45 |
-
"
|
| 46 |
-
"
|
| 47 |
-
"explicitRank6": "rank6IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1]) and dim(shapes.transposed, 2) == dim(shapes.data, attrs.perm[2]) and dim(shapes.transposed, 3) == dim(shapes.data, attrs.perm[3]) and dim(shapes.transposed, 4) == dim(shapes.data, attrs.perm[4]) and dim(shapes.transposed, 5) == dim(shapes.data, attrs.perm[5])",
|
| 48 |
-
"explicitRank7": "rank7IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1]) and dim(shapes.transposed, 2) == dim(shapes.data, attrs.perm[2]) and dim(shapes.transposed, 3) == dim(shapes.data, attrs.perm[3]) and dim(shapes.transposed, 4) == dim(shapes.data, attrs.perm[4]) and dim(shapes.transposed, 5) == dim(shapes.data, attrs.perm[5]) and dim(shapes.transposed, 6) == dim(shapes.data, attrs.perm[6])",
|
| 49 |
-
"defaultRank2": "rank2IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 1) and dim(shapes.transposed, 1) == dim(shapes.data, 0)",
|
| 50 |
-
"defaultRank3": "rank3IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 2) and dim(shapes.transposed, 1) == dim(shapes.data, 1) and dim(shapes.transposed, 2) == dim(shapes.data, 0)",
|
| 51 |
-
"defaultRank4": "rank4IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 3) and dim(shapes.transposed, 1) == dim(shapes.data, 2) and dim(shapes.transposed, 2) == dim(shapes.data, 1) and dim(shapes.transposed, 3) == dim(shapes.data, 0)",
|
| 52 |
-
"defaultRank5": "rank5IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 4) and dim(shapes.transposed, 1) == dim(shapes.data, 3) and dim(shapes.transposed, 2) == dim(shapes.data, 2) and dim(shapes.transposed, 3) == dim(shapes.data, 1) and dim(shapes.transposed, 4) == dim(shapes.data, 0)",
|
| 53 |
-
"defaultRank6": "rank6IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 5) and dim(shapes.transposed, 1) == dim(shapes.data, 4) and dim(shapes.transposed, 2) == dim(shapes.data, 3) and dim(shapes.transposed, 3) == dim(shapes.data, 2) and dim(shapes.transposed, 4) == dim(shapes.data, 1) and dim(shapes.transposed, 5) == dim(shapes.data, 0)",
|
| 54 |
-
"defaultRank7": "rank7IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 6) and dim(shapes.transposed, 1) == dim(shapes.data, 5) and dim(shapes.transposed, 2) == dim(shapes.data, 4) and dim(shapes.transposed, 3) == dim(shapes.data, 3) and dim(shapes.transposed, 4) == dim(shapes.data, 2) and dim(shapes.transposed, 5) == dim(shapes.data, 1) and dim(shapes.transposed, 6) == dim(shapes.data, 0)",
|
| 55 |
-
"genericTransposeOk": "rank0Permutation or rank1Permutation or explicitRank2 or defaultRank2 or explicitRank3 or defaultRank3 or explicitRank4 or defaultRank4 or explicitRank5 or defaultRank5 or explicitRank6 or defaultRank6 or explicitRank7 or defaultRank7",
|
| 56 |
-
"defaultPerm": "pick([[rank0IO, []], [rank1IO, [0]], [rank2IO, [1, 0]], [rank3IO, [2, 1, 0]], [rank4IO, [3, 2, 1, 0]], [rank5IO, [4, 3, 2, 1, 0]], [rank6IO, [5, 4, 3, 2, 1, 0]], [rank7IO, [6, 5, 4, 3, 2, 1, 0]]], [])",
|
| 57 |
"genericPerm": "pick([[permPresent, attrs.perm]], defaultPerm)",
|
| 58 |
-
"
|
| 59 |
-
"
|
|
|
|
| 60 |
"innerVec4Ok": "innerVec4Rank3Ok or innerVec4Rank4Ok",
|
| 61 |
"innerVec4Perm": "attrs.perm",
|
| 62 |
-
"tiled2dShapeOk": "(defaultRank2 or (explicitRank2 and attrs.perm[0] == 1 and attrs.perm[1] == 0)) and numel(shapes.
|
| 63 |
-
"tiled2dOk": "tiled2dShapeOk and dim(shapes.
|
| 64 |
-
"tiled3dShapeOk": "explicitRank3 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 1 and numel(shapes.
|
| 65 |
-
"tiled3dOk": "tiled3dShapeOk and dim(shapes.
|
| 66 |
-
"tiled4dShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 3 and attrs.perm[3] == 2 and numel(shapes.
|
| 67 |
-
"tiled4dOk": "tiled4dShapeOk and dim(shapes.
|
| 68 |
-
"tiled5dNcdhwToNdhwcShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 4 and attrs.perm[4] == 1 and numel(shapes.
|
| 69 |
-
"tiled5dNcdhwToNdhwcOk": "tiled5dNcdhwToNdhwcShapeOk and dim(shapes.
|
| 70 |
-
"tiled5dNdhwcToNcdhwShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 4 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and attrs.perm[4] == 3 and numel(shapes.
|
| 71 |
-
"tiled5dNdhwcToNcdhwOk": "tiled5dNdhwcToNcdhwShapeOk and dim(shapes.
|
| 72 |
-
"memcpyPerm3": "explicitRank3 and (dim(shapes.
|
| 73 |
-
"memcpyPerm4": "explicitRank4 and (dim(shapes.
|
| 74 |
-
"memcpyPerm5": "explicitRank5 and (dim(shapes.
|
| 75 |
"memcpyPermOk": "memcpyPerm3 or memcpyPerm4 or memcpyPerm5",
|
| 76 |
-
"identityPermutation": "
|
| 77 |
-
"identityCopyOk": "numel(shapes.
|
| 78 |
-
"tiled4dNchwToNhwcShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 1 and numel(shapes.
|
| 79 |
-
"tiled4dNchwToNhwcOk": "tiled4dNchwToNhwcShapeOk and dim(shapes.
|
| 80 |
-
"tiled4dNhwcToNchwShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 3 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and numel(shapes.
|
| 81 |
-
"tiled4dNhwcToNchwOk": "tiled4dNhwcToNchwShapeOk and (dim(shapes.
|
| 82 |
"tiledTransposeOk": "tiled2dOk or tiled3dOk or tiled4dOk or tiled5dNcdhwToNdhwcOk or tiled5dNdhwcToNcdhwOk or tiled4dNchwToNhwcOk or tiled4dNhwcToNchwOk",
|
| 83 |
"tiledScalarTransposeOk": "tiled2dShapeOk or tiled3dShapeOk or tiled4dShapeOk or tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk",
|
| 84 |
-
"tiledRows": "dim(shapes.
|
| 85 |
-
"tiledCols": "dim(shapes.
|
| 86 |
-
"tiledBatch": "1 if tiled2dShapeOk else (dim(shapes.
|
| 87 |
},
|
| 88 |
-
"
|
| 89 |
-
|
| 90 |
-
"
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
"arg": "x",
|
| 94 |
-
"semantic": "data",
|
| 95 |
-
"buffer": { "type": "read-only-storage" },
|
| 96 |
-
"elementType": "$vectorScalar"
|
| 97 |
-
},
|
| 98 |
-
{
|
| 99 |
-
"name": "dst",
|
| 100 |
-
"arg": "y",
|
| 101 |
-
"semantic": "transposed",
|
| 102 |
-
"buffer": { "type": "storage" },
|
| 103 |
-
"elementType": "$vectorScalar"
|
| 104 |
-
}
|
| 105 |
-
],
|
| 106 |
-
"scalarData": [
|
| 107 |
-
{
|
| 108 |
-
"name": "x",
|
| 109 |
-
"arg": "x",
|
| 110 |
-
"semantic": "data",
|
| 111 |
-
"buffer": { "type": "read-only-storage" },
|
| 112 |
-
"elementType": "$scalar"
|
| 113 |
-
},
|
| 114 |
-
{ "name": "y", "arg": "y", "semantic": "transposed", "buffer": { "type": "storage" }, "elementType": "$scalar" },
|
| 115 |
-
{
|
| 116 |
-
"name": "params",
|
| 117 |
-
"semantic": "kernel.params",
|
| 118 |
-
"buffer": { "type": "uniform" },
|
| 119 |
-
"struct": {
|
| 120 |
-
"name": "Params",
|
| 121 |
-
"fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.transposed)" }]
|
| 122 |
-
}
|
| 123 |
-
}
|
| 124 |
-
],
|
| 125 |
-
"scalarDataNoParams": [
|
| 126 |
-
{
|
| 127 |
-
"name": "x",
|
| 128 |
-
"arg": "x",
|
| 129 |
-
"semantic": "data",
|
| 130 |
-
"buffer": { "type": "read-only-storage" },
|
| 131 |
-
"elementType": "$scalar"
|
| 132 |
-
},
|
| 133 |
-
{ "name": "y", "arg": "y", "semantic": "transposed", "buffer": { "type": "storage" }, "elementType": "$scalar" }
|
| 134 |
-
]
|
| 135 |
},
|
| 136 |
"variants": [
|
| 137 |
{
|
| 138 |
"id": "tiled_vec4",
|
| 139 |
"priority": 22,
|
| 140 |
"when": ["tiledTransposeOk"],
|
| 141 |
-
"
|
| 142 |
"passes": [
|
| 143 |
{
|
| 144 |
"id": "main",
|
| 145 |
"name": "Transpose.tiled",
|
| 146 |
-
"
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
},
|
| 150 |
-
"bindings": "vectorData",
|
| 151 |
"dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
|
| 152 |
}
|
| 153 |
]
|
| 154 |
},
|
| 155 |
{
|
| 156 |
"id": "tiled_scalar",
|
| 157 |
-
"description": "Stages ragged two-dimensional transpose planes through a padded workgroup tile so reads and writes remain coalesced when vec4 alignment is unavailable.",
|
| 158 |
"priority": 19,
|
| 159 |
"when": ["tiledScalarTransposeOk", "dtypeSupported"],
|
| 160 |
-
"
|
| 161 |
"passes": [
|
| 162 |
{
|
| 163 |
"id": "main",
|
| 164 |
"name": "Transpose.tiledScalar",
|
| 165 |
-
"
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
},
|
| 169 |
-
"bindings": "scalarDataNoParams",
|
| 170 |
"dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
|
| 171 |
}
|
| 172 |
]
|
|
@@ -175,41 +100,44 @@
|
|
| 175 |
"id": "identity_vec4",
|
| 176 |
"priority": 21,
|
| 177 |
"when": ["identityCopyOk"],
|
| 178 |
-
"
|
| 179 |
"passes": [
|
| 180 |
{
|
| 181 |
"id": "main",
|
| 182 |
"name": "Transpose.identityVec4",
|
| 183 |
-
"
|
| 184 |
-
"
|
| 185 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
}
|
| 187 |
]
|
| 188 |
},
|
| 189 |
{
|
| 190 |
"id": "inner_vec4",
|
| 191 |
-
"description": "Vectorizes the contiguous innermost dimension for permutations that leave that axis in place. It assigns multiple vectors per invocation only when enough invocations remain to keep the dispatch populated.",
|
| 192 |
"priority": 20,
|
| 193 |
"when": ["innerVec4Ok"],
|
| 194 |
-
"
|
| 195 |
"passes": [
|
| 196 |
{
|
| 197 |
"id": "main",
|
| 198 |
"name": "Transpose.innerVec4",
|
| 199 |
-
"
|
| 200 |
-
|
| 201 |
-
"
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
"count": "numel(shapes.transposed) / 4"
|
| 207 |
-
}
|
| 208 |
},
|
| 209 |
-
"bindings": "
|
| 210 |
"dispatch": {
|
| 211 |
-
"
|
| 212 |
-
"
|
|
|
|
| 213 |
}
|
| 214 |
}
|
| 215 |
]
|
|
@@ -217,23 +145,29 @@
|
|
| 217 |
{
|
| 218 |
"id": "generic",
|
| 219 |
"when": ["genericTransposeOk", "dtypeSupported"],
|
| 220 |
-
"
|
| 221 |
"passes": [
|
| 222 |
{
|
| 223 |
"id": "main",
|
| 224 |
"name": "Transpose",
|
| 225 |
-
"
|
| 226 |
-
|
| 227 |
-
"
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
"itemsPerInvocation": 4
|
| 233 |
-
}
|
| 234 |
},
|
| 235 |
-
"bindings":
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
}
|
| 238 |
]
|
| 239 |
}
|
|
|
|
| 2 |
"domain": "ai.onnx",
|
| 3 |
"name": "Transpose",
|
| 4 |
"sinceVersion": 13,
|
| 5 |
+
"inputs": { "x": { "onnx": "data", "dtype": "T" } },
|
| 6 |
+
"outputs": { "y": { "onnx": "transposed", "dtype": "T", "rank": "ranks.x" } },
|
| 7 |
+
"attributes": { "perm": {} },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
"typeConstraints": { "T": ["float32", "float16", "int32", "int16", "uint32", "uint8", "int8", "bool"] },
|
| 9 |
+
"tunables": {
|
| 10 |
+
"WORKGROUP_SIZE": { "default": 256 },
|
| 11 |
+
"INNER_VEC4_ITEMS_PER_THREAD": { "default": 4 },
|
| 12 |
+
"INNER_VEC4_MIN_THREADS": { "default": 65536 }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
},
|
| 14 |
"derive": {
|
| 15 |
"dtypeSupported": "f16Ok(dtypes.T)",
|
| 16 |
+
"rank2IO": "ranks.x == 2 and ranks.y == 2",
|
| 17 |
+
"rank3IO": "ranks.x == 3 and ranks.y == 3",
|
| 18 |
+
"rank4IO": "ranks.x == 4 and ranks.y == 4",
|
| 19 |
+
"rank5IO": "ranks.x == 5 and ranks.y == 5",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
"permPresent": "has(attrs, \"perm\")",
|
| 21 |
+
"permContract": "not permPresent or ((attrs.perm | length) == ranks.x and isUniqueIntList(attrs.perm, 0, ranks.x))",
|
| 22 |
+
"explicitRank2": "rank2IO and permContract and permPresent and dim(shapes.y, 0) == dim(shapes.x, attrs.perm[0]) and dim(shapes.y, 1) == dim(shapes.x, attrs.perm[1])",
|
| 23 |
+
"explicitRank3": "rank3IO and permContract and permPresent and dim(shapes.y, 0) == dim(shapes.x, attrs.perm[0]) and dim(shapes.y, 1) == dim(shapes.x, attrs.perm[1]) and dim(shapes.y, 2) == dim(shapes.x, attrs.perm[2])",
|
| 24 |
+
"explicitRank4": "rank4IO and permContract and permPresent and dim(shapes.y, 0) == dim(shapes.x, attrs.perm[0]) and dim(shapes.y, 1) == dim(shapes.x, attrs.perm[1]) and dim(shapes.y, 2) == dim(shapes.x, attrs.perm[2]) and dim(shapes.y, 3) == dim(shapes.x, attrs.perm[3])",
|
| 25 |
+
"explicitRank5": "rank5IO and permContract and permPresent and dim(shapes.y, 0) == dim(shapes.x, attrs.perm[0]) and dim(shapes.y, 1) == dim(shapes.x, attrs.perm[1]) and dim(shapes.y, 2) == dim(shapes.x, attrs.perm[2]) and dim(shapes.y, 3) == dim(shapes.x, attrs.perm[3]) and dim(shapes.y, 4) == dim(shapes.x, attrs.perm[4])",
|
| 26 |
+
"defaultRank2": "rank2IO and permContract and not permPresent and dim(shapes.y, 0) == dim(shapes.x, 1) and dim(shapes.y, 1) == dim(shapes.x, 0)",
|
| 27 |
+
"defaultPerm": "reversedAxes(ranks.x)",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
"genericPerm": "pick([[permPresent, attrs.perm]], defaultPerm)",
|
| 29 |
+
"genericTransposeOk": "ranks.x == ranks.y and permContract and sameShape(shapes.y, permuteShape(shapes.x, genericPerm))",
|
| 30 |
+
"innerVec4Rank3Ok": "explicitRank3 and attrs.perm[2] == 2 and dim(shapes.x, 2) % 4 == 0 and numel(shapes.y) > 0 and dtypeSupported",
|
| 31 |
+
"innerVec4Rank4Ok": "explicitRank4 and attrs.perm[3] == 3 and dim(shapes.x, 3) % 4 == 0 and numel(shapes.y) > 0 and dtypeSupported",
|
| 32 |
"innerVec4Ok": "innerVec4Rank3Ok or innerVec4Rank4Ok",
|
| 33 |
"innerVec4Perm": "attrs.perm",
|
| 34 |
+
"tiled2dShapeOk": "(defaultRank2 or (explicitRank2 and attrs.perm[0] == 1 and attrs.perm[1] == 0)) and numel(shapes.x) > 0 and ceil(dim(shapes.x, 0) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
|
| 35 |
+
"tiled2dOk": "tiled2dShapeOk and dim(shapes.x, 0) % 4 == 0 and dim(shapes.x, 1) % 4 == 0",
|
| 36 |
+
"tiled3dShapeOk": "explicitRank3 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 2) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
|
| 37 |
+
"tiled3dOk": "tiled3dShapeOk and dim(shapes.x, 1) % 4 == 0 and dim(shapes.x, 2) % 4 == 0",
|
| 38 |
+
"tiled4dShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 3 and attrs.perm[3] == 2 and numel(shapes.x) > 0 and dim(shapes.x, 0) * dim(shapes.x, 1) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 2) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 3) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
|
| 39 |
+
"tiled4dOk": "tiled4dShapeOk and dim(shapes.x, 2) % 4 == 0 and dim(shapes.x, 3) % 4 == 0",
|
| 40 |
+
"tiled5dNcdhwToNdhwcShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 4 and attrs.perm[4] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4)) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
|
| 41 |
+
"tiled5dNcdhwToNdhwcOk": "tiled5dNcdhwToNdhwcShapeOk and dim(shapes.x, 1) % 4 == 0 and (dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4)) % 4 == 0",
|
| 42 |
+
"tiled5dNdhwcToNcdhwShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 4 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and attrs.perm[4] == 3 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 4) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
|
| 43 |
+
"tiled5dNdhwcToNcdhwOk": "tiled5dNdhwcToNcdhwShapeOk and dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) % 4 == 0 and dim(shapes.x, 4) % 4 == 0",
|
| 44 |
+
"memcpyPerm3": "explicitRank3 and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2])",
|
| 45 |
+
"memcpyPerm4": "explicitRank4 and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[0] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[1] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[2]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[2] < attrs.perm[3])",
|
| 46 |
+
"memcpyPerm5": "explicitRank5 and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[0] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[0]) == 1 or dim(shapes.x, attrs.perm[4]) == 1 or attrs.perm[0] < attrs.perm[4]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[1] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[1]) == 1 or dim(shapes.x, attrs.perm[4]) == 1 or attrs.perm[1] < attrs.perm[4]) and (dim(shapes.x, attrs.perm[2]) == 1 or dim(shapes.x, attrs.perm[3]) == 1 or attrs.perm[2] < attrs.perm[3]) and (dim(shapes.x, attrs.perm[2]) == 1 or dim(shapes.x, attrs.perm[4]) == 1 or attrs.perm[2] < attrs.perm[4]) and (dim(shapes.x, attrs.perm[3]) == 1 or dim(shapes.x, attrs.perm[4]) == 1 or attrs.perm[3] < attrs.perm[4])",
|
| 47 |
"memcpyPermOk": "memcpyPerm3 or memcpyPerm4 or memcpyPerm5",
|
| 48 |
+
"identityPermutation": "genericTransposeOk and sameShape(genericPerm, reversedAxes(ranks.x) | reverse)",
|
| 49 |
+
"identityCopyOk": "numel(shapes.y) > 0 and numel(shapes.y) % 4 == 0 and (identityPermutation or memcpyPermOk) and dtypeSupported",
|
| 50 |
+
"tiled4dNchwToNhwcShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 1 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 1) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 2) * dim(shapes.x, 3)) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
|
| 51 |
+
"tiled4dNchwToNhwcOk": "tiled4dNchwToNhwcShapeOk and dim(shapes.x, 1) % 4 == 0 and (dim(shapes.x, 2) * dim(shapes.x, 3)) % 4 == 0",
|
| 52 |
+
"tiled4dNhwcToNchwShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 3 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and numel(shapes.x) > 0 and dim(shapes.x, 0) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil((dim(shapes.x, 1) * dim(shapes.x, 2)) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceil(dim(shapes.x, 3) / 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and dtypeSupported",
|
| 53 |
+
"tiled4dNhwcToNchwOk": "tiled4dNhwcToNchwShapeOk and (dim(shapes.x, 1) * dim(shapes.x, 2)) % 4 == 0 and dim(shapes.x, 3) % 4 == 0",
|
| 54 |
"tiledTransposeOk": "tiled2dOk or tiled3dOk or tiled4dOk or tiled5dNcdhwToNdhwcOk or tiled5dNdhwcToNcdhwOk or tiled4dNchwToNhwcOk or tiled4dNhwcToNchwOk",
|
| 55 |
"tiledScalarTransposeOk": "tiled2dShapeOk or tiled3dShapeOk or tiled4dShapeOk or tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk",
|
| 56 |
+
"tiledRows": "dim(shapes.x, 0) if tiled2dShapeOk else (dim(shapes.x, 1) if tiled3dShapeOk else (dim(shapes.x, 2) if tiled4dShapeOk else (dim(shapes.x, 1) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.x, 1) * dim(shapes.x, 2) * dim(shapes.x, 3) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.x, 1) if tiled4dNchwToNhwcShapeOk else (dim(shapes.x, 1) * dim(shapes.x, 2) if tiled4dNhwcToNchwShapeOk else 1))))))",
|
| 57 |
+
"tiledCols": "dim(shapes.x, 1) if tiled2dShapeOk else (dim(shapes.x, 2) if tiled3dShapeOk else (dim(shapes.x, 3) if tiled4dShapeOk else (dim(shapes.x, 2) * dim(shapes.x, 3) * dim(shapes.x, 4) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.x, 4) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.x, 2) * dim(shapes.x, 3) if tiled4dNchwToNhwcShapeOk else (dim(shapes.x, 3) if tiled4dNhwcToNchwShapeOk else 1))))))",
|
| 58 |
+
"tiledBatch": "1 if tiled2dShapeOk else (dim(shapes.x, 0) if tiled3dShapeOk else (dim(shapes.x, 0) * dim(shapes.x, 1) if tiled4dShapeOk else (dim(shapes.x, 0) if tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk else 1)))"
|
| 59 |
},
|
| 60 |
+
"bindings": {
|
| 61 |
+
"src": { "arg": "x", "buffer": "read-only-storage", "elementType": "$vectorScalar" },
|
| 62 |
+
"dst": { "arg": "y", "buffer": "storage", "elementType": "$vectorScalar" },
|
| 63 |
+
"x": { "buffer": "read-only-storage", "elementType": "$scalar" },
|
| 64 |
+
"y": { "buffer": "storage", "elementType": "$scalar" }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
},
|
| 66 |
"variants": [
|
| 67 |
{
|
| 68 |
"id": "tiled_vec4",
|
| 69 |
"priority": 22,
|
| 70 |
"when": ["tiledTransposeOk"],
|
| 71 |
+
"derive": { "scalar": "dtypes.T", "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
|
| 72 |
"passes": [
|
| 73 |
{
|
| 74 |
"id": "main",
|
| 75 |
"name": "Transpose.tiled",
|
| 76 |
+
"shader": "datamove-transpose-2d-tiled.wgsl.jinja",
|
| 77 |
+
"derive": { "rowCount": "tiledRows", "cols": "tiledCols" },
|
| 78 |
+
"bindings": ["src", "dst"],
|
|
|
|
|
|
|
| 79 |
"dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
|
| 80 |
}
|
| 81 |
]
|
| 82 |
},
|
| 83 |
{
|
| 84 |
"id": "tiled_scalar",
|
|
|
|
| 85 |
"priority": 19,
|
| 86 |
"when": ["tiledScalarTransposeOk", "dtypeSupported"],
|
| 87 |
+
"derive": { "scalar": "dtypes.T" },
|
| 88 |
"passes": [
|
| 89 |
{
|
| 90 |
"id": "main",
|
| 91 |
"name": "Transpose.tiledScalar",
|
| 92 |
+
"shader": "datamove-transpose-2d-tiled-scalar.wgsl.jinja",
|
| 93 |
+
"derive": { "rowCount": "tiledRows", "cols": "tiledCols" },
|
| 94 |
+
"bindings": ["x", "y"],
|
|
|
|
|
|
|
| 95 |
"dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
|
| 96 |
}
|
| 97 |
]
|
|
|
|
| 100 |
"id": "identity_vec4",
|
| 101 |
"priority": 21,
|
| 102 |
"when": ["identityCopyOk"],
|
| 103 |
+
"derive": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
|
| 104 |
"passes": [
|
| 105 |
{
|
| 106 |
"id": "main",
|
| 107 |
"name": "Transpose.identityVec4",
|
| 108 |
+
"shader": "datamove-flat-copy.wgsl.jinja",
|
| 109 |
+
"derive": { "count": "numel(shapes.y) / 4" },
|
| 110 |
+
"bindings": ["src", "dst"],
|
| 111 |
+
"dispatch": {
|
| 112 |
+
"x": "min(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
|
| 113 |
+
"y": 1,
|
| 114 |
+
"z": 1
|
| 115 |
+
}
|
| 116 |
}
|
| 117 |
]
|
| 118 |
},
|
| 119 |
{
|
| 120 |
"id": "inner_vec4",
|
|
|
|
| 121 |
"priority": 20,
|
| 122 |
"when": ["innerVec4Ok"],
|
| 123 |
+
"derive": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
|
| 124 |
"passes": [
|
| 125 |
{
|
| 126 |
"id": "main",
|
| 127 |
"name": "Transpose.innerVec4",
|
| 128 |
+
"shader": "datamove-transpose-vec4.wgsl.jinja",
|
| 129 |
+
"derive": {
|
| 130 |
+
"inputShape": "shapes.x",
|
| 131 |
+
"outputShape": "shapes.y",
|
| 132 |
+
"rank": "ranks.x",
|
| 133 |
+
"perm": "innerVec4Perm",
|
| 134 |
+
"count": "numel(shapes.y) / 4"
|
|
|
|
|
|
|
| 135 |
},
|
| 136 |
+
"bindings": ["src", "dst"],
|
| 137 |
"dispatch": {
|
| 138 |
+
"x": "min(ceilDiv((max(min(numel(shapes.y) / 4, tunables.INNER_VEC4_MIN_THREADS), ceilDiv(numel(shapes.y) / 4, tunables.INNER_VEC4_ITEMS_PER_THREAD))), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
|
| 139 |
+
"y": 1,
|
| 140 |
+
"z": 1
|
| 141 |
}
|
| 142 |
}
|
| 143 |
]
|
|
|
|
| 145 |
{
|
| 146 |
"id": "generic",
|
| 147 |
"when": ["genericTransposeOk", "dtypeSupported"],
|
| 148 |
+
"derive": { "scalar": "dtypes.T" },
|
| 149 |
"passes": [
|
| 150 |
{
|
| 151 |
"id": "main",
|
| 152 |
"name": "Transpose",
|
| 153 |
+
"shader": "transpose.wgsl.jinja",
|
| 154 |
+
"derive": {
|
| 155 |
+
"inputShape": "shapes.x",
|
| 156 |
+
"outputShape": "shapes.y",
|
| 157 |
+
"rank": "ranks.x",
|
| 158 |
+
"perm": "genericPerm",
|
| 159 |
+
"itemsPerInvocation": 4
|
|
|
|
|
|
|
| 160 |
},
|
| 161 |
+
"bindings": [
|
| 162 |
+
"x",
|
| 163 |
+
"y",
|
| 164 |
+
{ "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
|
| 165 |
+
],
|
| 166 |
+
"dispatch": {
|
| 167 |
+
"x": "min(ceilDiv((ceilDiv(numel(shapes.y), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 168 |
+
"y": "ceilDiv(ceilDiv((ceilDiv(numel(shapes.y), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 169 |
+
"z": 1
|
| 170 |
+
}
|
| 171 |
}
|
| 172 |
]
|
| 173 |
}
|
build/webgpu/metadata.json
CHANGED
|
@@ -1,22 +1,31 @@
|
|
| 1 |
{
|
| 2 |
"name": "ai.onnx.Transpose",
|
| 3 |
-
"id": "
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
-
"bench.json": "
|
| 11 |
-
"datamove-flat-copy.wgsl.jinja": "
|
| 12 |
-
"datamove-transpose-2d-tiled-scalar.wgsl.jinja": "
|
| 13 |
-
"datamove-transpose-2d-tiled.wgsl.jinja": "
|
| 14 |
-
"datamove-transpose-vec4.wgsl.jinja": "
|
| 15 |
-
"manifest.json": "
|
| 16 |
-
"test.json": "
|
| 17 |
-
"transpose.wgsl.jinja": "
|
| 18 |
}
|
| 19 |
},
|
| 20 |
-
"provenance": { "kernel": { "sha": "
|
| 21 |
-
"webgpu": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"name": "ai.onnx.Transpose",
|
| 3 |
+
"id": "_ai_onnx_transpose_webgpu_2a9c5f1",
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
+
"bench.json": "ClUFa7Y4uL69eUy46pTK4YciV/+jJxlDxSJFK+/JU6A=",
|
| 11 |
+
"datamove-flat-copy.wgsl.jinja": "g9d62mer2bmHfbScqX5CIT5Zq/Pdinac0W2dH0CD+5s=",
|
| 12 |
+
"datamove-transpose-2d-tiled-scalar.wgsl.jinja": "ycF9afS0CqHrLIfFi6hAqp+7GIcnHHAyN74hSF+/FrY=",
|
| 13 |
+
"datamove-transpose-2d-tiled.wgsl.jinja": "GNVRxhagDFVQ0DGjsUGD8BVzktDHsELwe0qw4xRHD60=",
|
| 14 |
+
"datamove-transpose-vec4.wgsl.jinja": "NARpwfOEcl8GRu17lnq62KXK/mgh61Jj3YHfi8Zofik=",
|
| 15 |
+
"manifest.json": "F4FH/KuY12tkUnIKlE3wBmydDgzgzAbfPva12DYYGBY=",
|
| 16 |
+
"test.json": "rQerBft2NMuE0I+S7GErvy8rPRoUQxOkI3NvNH5dcKc=",
|
| 17 |
+
"transpose.wgsl.jinja": "a9TDelnkVrpe1XBA8rTAurSGrTmIQ6BO16ZHRJWiQJw="
|
| 18 |
}
|
| 19 |
},
|
| 20 |
+
"provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
|
| 21 |
+
"webgpu": {
|
| 22 |
+
"manifestSpec": "2.0",
|
| 23 |
+
"variants": {
|
| 24 |
+
"tiled_vec4": ["datamove-transpose-2d-tiled.wgsl.jinja"],
|
| 25 |
+
"tiled_scalar": ["datamove-transpose-2d-tiled-scalar.wgsl.jinja"],
|
| 26 |
+
"identity_vec4": ["datamove-flat-copy.wgsl.jinja"],
|
| 27 |
+
"inner_vec4": ["datamove-transpose-vec4.wgsl.jinja"],
|
| 28 |
+
"generic": ["transpose.wgsl.jinja"]
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
}
|
build/webgpu/test.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
{
|
| 2 |
-
"op": "ai.onnx.Transpose",
|
| 3 |
"fixtureArrays": {
|
| 4 |
"onnx_backend_transpose_input_x": [0.54881352186203, 0.7151893377304077, 0.6027633547782898, 0.5448831915855408, 0.42365479469299316, 0.6458941102027893, 0.4375872015953064, 0.891772985458374, 0.9636627435684204, 0.3834415078163147, 0.7917250394821167, 0.5288949012756348, 0.5680445432662964, 0.9255966544151306, 0.07103605568408966, 0.08712930232286453, 0.020218396559357643, 0.832619845867157, 0.7781567573547363, 0.8700121641159058, 0.978618323802948, 0.7991585731506348, 0.4614793658256531, 0.7805292010307312],
|
| 5 |
"ort_transpose021_f32_input_x": [1, 2, 3, 4, 5, 6, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 1.3, 2.3, 3.3, 4.3, 5.3, 6.3],
|
|
@@ -189,7 +188,7 @@
|
|
| 189 |
"provenance": {
|
| 190 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 191 |
"test": "TransposeOpTest.TwoDimNoAttr",
|
| 192 |
-
"notes": "
|
| 193 |
},
|
| 194 |
"inputs": {
|
| 195 |
"x": { "dtype": "bool", "shape": [2, 3], "data": { "kind": "values", "values": [1, 0, 1, 0, 1, 0] } }
|
|
@@ -249,7 +248,7 @@
|
|
| 249 |
"provenance": {
|
| 250 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 251 |
"test": "TransposeOpTest.TwoDim_int32",
|
| 252 |
-
"notes": "
|
| 253 |
},
|
| 254 |
"attrs": { "perm": [1, 0] },
|
| 255 |
"inputs": {
|
|
@@ -401,7 +400,7 @@
|
|
| 401 |
"provenance": {
|
| 402 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 403 |
"test": "TransposeOpTest.SingleAxisMovingInwardsBlockCopy",
|
| 404 |
-
"notes": "
|
| 405 |
},
|
| 406 |
"attrs": { "perm": [1, 2, 0, 3] },
|
| 407 |
"inputs": {
|
|
@@ -807,7 +806,7 @@
|
|
| 807 |
{
|
| 808 |
"name": "rank2_scalar_odd_width_5x7",
|
| 809 |
"provenance": {
|
| 810 |
-
"notes": "
|
| 811 |
},
|
| 812 |
"attrs": { "perm": [1, 0] },
|
| 813 |
"inputs": {
|
|
@@ -824,7 +823,7 @@
|
|
| 824 |
"provenance": {
|
| 825 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 826 |
"test": "TransposeOpTest (generic N-dim)",
|
| 827 |
-
"notes": "
|
| 828 |
},
|
| 829 |
"attrs": { "perm": [1, 0, 3, 2, 5, 4, 6] },
|
| 830 |
"inputs": {
|
|
@@ -844,7 +843,7 @@
|
|
| 844 |
"provenance": {
|
| 845 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 846 |
"test": "TransposeOpTest.TwoDim (bool payload)",
|
| 847 |
-
"notes": "
|
| 848 |
},
|
| 849 |
"attrs": { "perm": [1, 0] },
|
| 850 |
"inputs": {
|
|
@@ -861,7 +860,7 @@
|
|
| 861 |
"provenance": {
|
| 862 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 863 |
"test": "TransposeOpTest.TransposeReshape (scrambled rank5)",
|
| 864 |
-
"notes": "
|
| 865 |
},
|
| 866 |
"attrs": { "perm": [4, 2, 0, 3, 1] },
|
| 867 |
"inputs": {
|
|
@@ -893,7 +892,7 @@
|
|
| 893 |
{
|
| 894 |
"name": "rank1_2d_fold_boundary_last_element_guard",
|
| 895 |
"provenance": {
|
| 896 |
-
"notes": "
|
| 897 |
},
|
| 898 |
"inputs": {
|
| 899 |
"x": {
|
|
@@ -1081,7 +1080,7 @@
|
|
| 1081 |
"provenance": {
|
| 1082 |
"source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
|
| 1083 |
"test": "unit-dim permutations lower to a flat copy",
|
| 1084 |
-
"notes": "
|
| 1085 |
},
|
| 1086 |
"attrs": { "perm": [0, 2, 1, 3] },
|
| 1087 |
"inputs": {
|
|
@@ -1092,6 +1091,38 @@
|
|
| 1092 |
}
|
| 1093 |
},
|
| 1094 |
"outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3, 4], "tolerance": 0 } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1095 |
}
|
| 1096 |
]
|
| 1097 |
}
|
|
|
|
| 1 |
{
|
|
|
|
| 2 |
"fixtureArrays": {
|
| 3 |
"onnx_backend_transpose_input_x": [0.54881352186203, 0.7151893377304077, 0.6027633547782898, 0.5448831915855408, 0.42365479469299316, 0.6458941102027893, 0.4375872015953064, 0.891772985458374, 0.9636627435684204, 0.3834415078163147, 0.7917250394821167, 0.5288949012756348, 0.5680445432662964, 0.9255966544151306, 0.07103605568408966, 0.08712930232286453, 0.020218396559357643, 0.832619845867157, 0.7781567573547363, 0.8700121641159058, 0.978618323802948, 0.7991585731506348, 0.4614793658256531, 0.7805292010307312],
|
| 4 |
"ort_transpose021_f32_input_x": [1, 2, 3, 4, 5, 6, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 1.3, 2.3, 3.3, 4.3, 5.3, 6.3],
|
|
|
|
| 188 |
"provenance": {
|
| 189 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 190 |
"test": "TransposeOpTest.TwoDimNoAttr",
|
| 191 |
+
"notes": "A two-dimensional transpose with an ONNX-valid bool payload verifies bit-preserving data movement."
|
| 192 |
},
|
| 193 |
"inputs": {
|
| 194 |
"x": { "dtype": "bool", "shape": [2, 3], "data": { "kind": "values", "values": [1, 0, 1, 0, 1, 0] } }
|
|
|
|
| 248 |
"provenance": {
|
| 249 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 250 |
"test": "TransposeOpTest.TwoDim_int32",
|
| 251 |
+
"notes": "A uint32 payload exercises an explicit two-dimensional transpose."
|
| 252 |
},
|
| 253 |
"attrs": { "perm": [1, 0] },
|
| 254 |
"inputs": {
|
|
|
|
| 400 |
"provenance": {
|
| 401 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 402 |
"test": "TransposeOpTest.SingleAxisMovingInwardsBlockCopy",
|
| 403 |
+
"notes": "Projects representable uint64 source values to uint32 storage."
|
| 404 |
},
|
| 405 |
"attrs": { "perm": [1, 2, 0, 3] },
|
| 406 |
"inputs": {
|
|
|
|
| 806 |
{
|
| 807 |
"name": "rank2_scalar_odd_width_5x7",
|
| 808 |
"provenance": {
|
| 809 |
+
"notes": "A 5-by-7 transpose avoids vector and tiled alignment, exercising scalar rank-2 address mapping."
|
| 810 |
},
|
| 811 |
"attrs": { "perm": [1, 0] },
|
| 812 |
"inputs": {
|
|
|
|
| 823 |
"provenance": {
|
| 824 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 825 |
"test": "TransposeOpTest (generic N-dim)",
|
| 826 |
+
"notes": "A rank-7 explicit permutation exercises generic high-rank index decomposition."
|
| 827 |
},
|
| 828 |
"attrs": { "perm": [1, 0, 3, 2, 5, 4, 6] },
|
| 829 |
"inputs": {
|
|
|
|
| 843 |
"provenance": {
|
| 844 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 845 |
"test": "TransposeOpTest.TwoDim (bool payload)",
|
| 846 |
+
"notes": "An aligned 4-by-4 bool tensor uses u32 storage and exercises vectorized tiled transpose without requiring subnormal preservation."
|
| 847 |
},
|
| 848 |
"attrs": { "perm": [1, 0] },
|
| 849 |
"inputs": {
|
|
|
|
| 860 |
"provenance": {
|
| 861 |
"source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
|
| 862 |
"test": "TransposeOpTest.TransposeReshape (scrambled rank5)",
|
| 863 |
+
"notes": "The rank-5 permutation [4,2,0,3,1] scrambles all nontrivial strides and exercises generic scalar gathering."
|
| 864 |
},
|
| 865 |
"attrs": { "perm": [4, 2, 0, 3, 1] },
|
| 866 |
"inputs": {
|
|
|
|
| 892 |
{
|
| 893 |
"name": "rank1_2d_fold_boundary_last_element_guard",
|
| 894 |
"provenance": {
|
| 895 |
+
"notes": "A rank-1 identity transpose with 16,777,217 elements requires 65,536 workgroups and therefore a two-row scalar dispatch. The odd element count exercises the final-row bound and makes a dropped tail element observable."
|
| 896 |
},
|
| 897 |
"inputs": {
|
| 898 |
"x": {
|
|
|
|
| 1080 |
"provenance": {
|
| 1081 |
"source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
|
| 1082 |
"test": "unit-dim permutations lower to a flat copy",
|
| 1083 |
+
"notes": "Permutation [0,2,1,3] swaps non-unit extents 3 and 4, so it must perform a true transpose rather than a flat copy."
|
| 1084 |
},
|
| 1085 |
"attrs": { "perm": [0, 2, 1, 3] },
|
| 1086 |
"inputs": {
|
|
|
|
| 1091 |
}
|
| 1092 |
},
|
| 1093 |
"outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3, 4], "tolerance": 0 } }
|
| 1094 |
+
},
|
| 1095 |
+
{
|
| 1096 |
+
"name": "rank8_explicit_perm_regroups_two_middle_axes",
|
| 1097 |
+
"provenance": {
|
| 1098 |
+
"source": "ONNX Transpose-13 specification",
|
| 1099 |
+
"notes": "A rank-8 staging permutation that moves one middle axis past two others. A distinct value per position makes any axis-stride mistake visible at the position it corrupts."
|
| 1100 |
+
},
|
| 1101 |
+
"attrs": { "perm": [0, 1, 2, 3, 5, 6, 4, 7] },
|
| 1102 |
+
"inputs": {
|
| 1103 |
+
"x": {
|
| 1104 |
+
"dtype": "float32",
|
| 1105 |
+
"shape": [2, 3, 2, 2, 4, 2, 2, 4],
|
| 1106 |
+
"data": { "kind": "linspace", "start": 0.0, "end": 1535.0 }
|
| 1107 |
+
}
|
| 1108 |
+
},
|
| 1109 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 3, 2, 2, 2, 2, 4, 4], "tolerance": 0 } }
|
| 1110 |
+
},
|
| 1111 |
+
{
|
| 1112 |
+
"name": "rank8_default_perm_reverses_every_axis",
|
| 1113 |
+
"provenance": {
|
| 1114 |
+
"source": "ONNX Transpose-13 specification",
|
| 1115 |
+
"notes": "With `perm` omitted on a rank-8 tensor, the operator reverses every axis."
|
| 1116 |
+
},
|
| 1117 |
+
"attrs": {},
|
| 1118 |
+
"inputs": {
|
| 1119 |
+
"x": {
|
| 1120 |
+
"dtype": "float32",
|
| 1121 |
+
"shape": [2, 3, 2, 2, 4, 2, 2, 4],
|
| 1122 |
+
"data": { "kind": "linspace", "start": 0.0, "end": 1535.0 }
|
| 1123 |
+
}
|
| 1124 |
+
},
|
| 1125 |
+
"outputs": { "y": { "dtype": "float32", "shape": [4, 2, 2, 4, 2, 2, 3, 2], "tolerance": 0 } }
|
| 1126 |
}
|
| 1127 |
]
|
| 1128 |
}
|
build/webgpu/transpose.wgsl.jinja
CHANGED
|
@@ -1,39 +1,37 @@
|
|
| 1 |
{% macro flat_tail_open() %}
|
| 2 |
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 3 |
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>
|
| 4 |
// 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
|
| 5 |
-
//
|
| 6 |
-
let invocation = gid.x + gid.y *
|
| 7 |
// Tail-safe scalar x4 keeps vector-like dispatch density without requiring
|
| 8 |
// the logical tensor length (or its storage binding) to be vec4 aligned.
|
| 9 |
-
|
| 10 |
-
let
|
|
|
|
| 11 |
for (var i = begin; i < end; i = i + 1u) {
|
| 12 |
{%- endmacro %}
|
| 13 |
{% macro flat_tail_close() %}
|
| 14 |
}
|
| 15 |
{% endmacro %}
|
| 16 |
|
| 17 |
-
{% if usesF16 %}
|
| 18 |
-
enable f16;
|
| 19 |
-
{% endif %}
|
| 20 |
{{ env.wgsl.resourceDeclarations }}
|
| 21 |
|
| 22 |
-
fn input_offset({% if
|
| 23 |
-
{% if
|
| 24 |
var rem = out_index;
|
| 25 |
{% endif %}
|
| 26 |
var offset = 0u;
|
| 27 |
-
{% for out_axis in range(
|
| 28 |
{% set out_stride = namespace(value=1) %}
|
| 29 |
-
{% for j in range(out_axis + 1,
|
| 30 |
-
{% set out_stride.value = out_stride.value *
|
| 31 |
{% endfor %}
|
| 32 |
{% set out_stride_divisor = out_stride.value if out_stride.value != 0 else 1 %}
|
| 33 |
-
{% set in_axis =
|
| 34 |
{% set in_stride = namespace(value=1) %}
|
| 35 |
-
{% for j in range(in_axis + 1,
|
| 36 |
-
{% set in_stride.value = in_stride.value *
|
| 37 |
{% endfor %}
|
| 38 |
let coord{{ out_axis }} = rem / {{ out_stride_divisor }}u;
|
| 39 |
rem = rem % {{ out_stride_divisor }}u;
|
|
@@ -43,6 +41,6 @@ fn input_offset({% if source.rank > 0 %}out_index: u32{% endif %}) -> u32 {
|
|
| 43 |
}
|
| 44 |
|
| 45 |
{{ flat_tail_open() }}
|
| 46 |
-
y[i] = x[input_offset({% if
|
| 47 |
{{ flat_tail_close() -}}
|
| 48 |
}
|
|
|
|
| 1 |
{% macro flat_tail_open() %}
|
| 2 |
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 3 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 4 |
// 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
|
| 5 |
+
// dispatch's per-axis workgroup fold width (the dispatch caps x and spills the rest into y).
|
| 6 |
+
let invocation = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 7 |
// Tail-safe scalar x4 keeps vector-like dispatch density without requiring
|
| 8 |
// the logical tensor length (or its storage binding) to be vec4 aligned.
|
| 9 |
+
{% set itemsPerInvocation = itemsPerInvocation if itemsPerInvocation is defined else 4 %}
|
| 10 |
+
let begin = invocation * {{ itemsPerInvocation }}u;
|
| 11 |
+
let end = min(begin + {{ itemsPerInvocation }}u, params.count);
|
| 12 |
for (var i = begin; i < end; i = i + 1u) {
|
| 13 |
{%- endmacro %}
|
| 14 |
{% macro flat_tail_close() %}
|
| 15 |
}
|
| 16 |
{% endmacro %}
|
| 17 |
|
|
|
|
|
|
|
|
|
|
| 18 |
{{ env.wgsl.resourceDeclarations }}
|
| 19 |
|
| 20 |
+
fn input_offset({% if rank > 0 %}out_index: u32{% endif %}) -> u32 {
|
| 21 |
+
{% if rank > 0 %}
|
| 22 |
var rem = out_index;
|
| 23 |
{% endif %}
|
| 24 |
var offset = 0u;
|
| 25 |
+
{% for out_axis in range(rank) %}
|
| 26 |
{% set out_stride = namespace(value=1) %}
|
| 27 |
+
{% for j in range(out_axis + 1, rank) %}
|
| 28 |
+
{% set out_stride.value = out_stride.value * outputShape[j] %}
|
| 29 |
{% endfor %}
|
| 30 |
{% set out_stride_divisor = out_stride.value if out_stride.value != 0 else 1 %}
|
| 31 |
+
{% set in_axis = perm[out_axis] %}
|
| 32 |
{% set in_stride = namespace(value=1) %}
|
| 33 |
+
{% for j in range(in_axis + 1, rank) %}
|
| 34 |
+
{% set in_stride.value = in_stride.value * inputShape[j] %}
|
| 35 |
{% endfor %}
|
| 36 |
let coord{{ out_axis }} = rem / {{ out_stride_divisor }}u;
|
| 37 |
rem = rem % {{ out_stride_divisor }}u;
|
|
|
|
| 41 |
}
|
| 42 |
|
| 43 |
{{ flat_tail_open() }}
|
| 44 |
+
y[i] = x[input_offset({% if rank > 0 %}i{% endif %})];
|
| 45 |
{{ flat_tail_close() -}}
|
| 46 |
}
|