Xenova HF Staff commited on
Commit
18df5b3
·
verified ·
1 Parent(s): 649b922

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,15 +18,15 @@ See the [ONNX `Not` spec](https://onnx.ai/onnx/operators/onnx__Not.html) for the
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `x` | `B` | — | — | Input boolean tensor. | required |
24
 
25
  ## Outputs
26
 
27
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
28
  | --- | --- | --- | --- | --- | --- | --- |
29
- | `Y` | `y` | `B` | same as `X` | same as `X` | Output boolean tensor with each element logically negated. | required |
30
 
31
  ## Type constraints
32
 
@@ -36,7 +36,7 @@ See the [ONNX `Not` spec](https://onnx.ai/onnx/operators/onnx__Not.html) for the
36
 
37
  ## Files
38
 
39
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
40
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
41
  - [`test.json`](build/webgpu/test.json) — correctness cases
42
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -45,10 +45,14 @@ See the [ONNX `Not` spec](https://onnx.ai/onnx/operators/onnx__Not.html) for the
45
 
46
  ## Use with `@huggingface/kernels`
47
 
48
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
49
- It then allocates the result tensors automatically.
 
 
 
50
 
51
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
52
 
53
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
54
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `x` | `X` | `B` | — | — | Input boolean tensor. | required |
24
 
25
  ## Outputs
26
 
27
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
28
  | --- | --- | --- | --- | --- | --- | --- |
29
+ | `y` | `Y` | `B` | same as `x` | same as `x` | Output boolean tensor with each element logically negated. | required |
30
 
31
  ## Type constraints
32
 
 
36
 
37
  ## Files
38
 
39
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
40
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
41
  - [`test.json`](build/webgpu/test.json) — correctness cases
42
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
45
 
46
  ## Use with `@huggingface/kernels`
47
 
48
+ ```sh
49
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
50
+ ```
51
+
52
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
53
 
54
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
55
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
56
 
57
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
58
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Not",
3
  "cases": [
4
  {
5
  "name": "1m_bool",
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "1m_bool",
build/webgpu/manifest.json CHANGED
@@ -2,85 +2,60 @@
2
  "domain": "ai.onnx",
3
  "name": "Not",
4
  "sinceVersion": 1,
5
- "description": "Computes the elementwise logical NOT of a boolean tensor. Each output element is `true` where the corresponding input element is `false`, and vice versa.",
6
- "inputs": [{ "role": "X", "dtype": "B", "description": "Input boolean tensor." }],
7
- "outputs": [
8
- {
9
- "role": "Y",
10
- "dtype": "B",
11
- "rank": "ranks.X",
12
- "description": "Output boolean tensor with each element logically negated.",
13
- "shape": "shapes.X"
14
- }
15
- ],
16
  "typeConstraints": { "B": ["bool"] },
17
- "args": {
18
- "x": { "kind": "tensor", "semantic": "X", "role": "input" },
19
- "y": { "kind": "tensor", "semantic": "Y", "role": "output" }
20
- },
21
- "tunables": { "WORKGROUP_SIZE": 256 },
22
  "derive": {
23
  "wideVec4StorageOk": "device.features.has(\"subgroups\") or not (has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 8 and device.adapterInfo.subgroupMaxSize <= 32)"
24
  },
 
25
  "variants": [
26
  {
27
  "id": "same_layout_vec4",
28
- "when": ["numel(shapes.X) > 0", "numel(shapes.X) % 4 == 0", "numel(shapes.X) == numel(shapes.Y)", "wideVec4StorageOk"],
 
 
29
  "passes": [
30
  {
31
  "id": "main",
32
  "name": "Not.vec4",
33
- "source": { "shader": "unary-vec4.wgsl.jinja", "inputs": { "op": "\"not\"" } },
 
 
 
 
34
  "bindings": [
35
- {
36
- "name": "x",
37
- "arg": "x",
38
- "semantic": "X",
39
- "buffer": { "type": "read-only-storage" },
40
- "elementType": "vec4<u32>"
41
- },
42
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "vec4<u32>" },
43
- {
44
- "name": "params",
45
- "semantic": "kernel.params",
46
- "buffer": { "type": "uniform" },
47
- "struct": {
48
- "name": "Params",
49
- "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.Y) / 4" }]
50
- }
51
- }
52
  ],
53
- "dispatch": { "threads": "numel(shapes.Y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
54
  }
55
- ],
56
- "priority": 20,
57
- "constants": { "scalar": "dtypes.B", "usesF16": false, "vectorScalar": "\"vec4<\" ~ dtypes.B ~ \">\"" }
58
  },
59
  {
60
  "id": "elementwise",
61
- "when": "numel(shapes.X) == numel(shapes.Y)",
62
  "passes": [
63
  {
64
  "id": "main",
65
  "name": "Not",
66
- "source": { "shader": "unary-scalar.wgsl.jinja", "inputs": { "op": "\"not\"" } },
 
67
  "bindings": [
68
- {
69
- "name": "x",
70
- "arg": "x",
71
- "semantic": "X",
72
- "buffer": { "type": "read-only-storage" },
73
- "elementType": "u32"
74
- },
75
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "u32" },
76
- {
77
- "name": "params",
78
- "semantic": "kernel.params",
79
- "buffer": { "type": "uniform" },
80
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.Y)" }] }
81
- }
82
  ],
83
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
84
  }
85
  ]
86
  }
 
2
  "domain": "ai.onnx",
3
  "name": "Not",
4
  "sinceVersion": 1,
5
+ "inputs": { "x": { "onnx": "X", "dtype": "B" } },
6
+ "outputs": { "y": { "onnx": "Y", "dtype": "B", "rank": "ranks.x", "shape": "shapes.x" } },
 
 
 
 
 
 
 
 
 
7
  "typeConstraints": { "B": ["bool"] },
8
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
 
 
 
 
9
  "derive": {
10
  "wideVec4StorageOk": "device.features.has(\"subgroups\") or not (has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 8 and device.adapterInfo.subgroupMaxSize <= 32)"
11
  },
12
+ "when": ["numel(shapes.x) == numel(shapes.y)"],
13
  "variants": [
14
  {
15
  "id": "same_layout_vec4",
16
+ "priority": 20,
17
+ "when": ["numel(shapes.x) > 0", "numel(shapes.x) % 4 == 0", "wideVec4StorageOk"],
18
+ "derive": { "scalar": "dtypes.B", "usesF16": false, "vectorScalar": "\"vec4<\" ~ dtypes.B ~ \">\"" },
19
  "passes": [
20
  {
21
  "id": "main",
22
  "name": "Not.vec4",
23
+ "shader": "unary-vec4.wgsl.jinja",
24
+ "derive": {
25
+ "op": "\"not\"",
26
+ "vec4PerThread": "4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1"
27
+ },
28
  "bindings": [
29
+ { "arg": "x", "elementType": "vec4<u32>" },
30
+ { "arg": "y", "elementType": "vec4<u32>" },
31
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  ],
33
+ "dispatch": {
34
+ "x": "min(ceilDiv((ceilDiv(numel(shapes.y) / 4, 4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1)), (tunables.WORKGROUP_SIZE)), 65535)",
35
+ "y": "ceilDiv(ceilDiv((ceilDiv(numel(shapes.y) / 4, 4 if numel(shapes.y) * dtypeBytes(tensorDtypes.y) <= 16777216 else 1)), (tunables.WORKGROUP_SIZE)), 65535)",
36
+ "z": 1
37
+ }
38
  }
39
+ ]
 
 
40
  },
41
  {
42
  "id": "elementwise",
 
43
  "passes": [
44
  {
45
  "id": "main",
46
  "name": "Not",
47
+ "shader": "unary-scalar.wgsl.jinja",
48
+ "derive": { "op": "\"not\"" },
49
  "bindings": [
50
+ { "arg": "x", "elementType": "u32" },
51
+ { "arg": "y", "elementType": "u32" },
52
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
 
 
 
 
 
 
 
 
 
 
 
53
  ],
54
+ "dispatch": {
55
+ "x": "min(ceilDiv((ceilDiv(numel(shapes.y), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
56
+ "y": "ceilDiv(ceilDiv((ceilDiv(numel(shapes.y), 4)), (tunables.WORKGROUP_SIZE)), 65535)",
57
+ "z": 1
58
+ }
59
  }
60
  ]
61
  }
build/webgpu/metadata.json CHANGED
@@ -1,19 +1,22 @@
1
  {
2
  "name": "ai.onnx.Not",
3
- "id": "_ai_onnx_not_webgpu_57bebe0",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "WHSdkLkXazF/f8HKIJ1KSyIV89eguvAi7ac99EgCUW4=",
11
- "manifest.json": "OtseGXk5ZdhyPpPY4Kzp6vaC6TJkQtX4HnbcOMZz5o8=",
12
- "test.json": "0pg588+zzH/qMBLQH3y4kuOCNObQPNAswB/BJASd2gA=",
13
- "unary-scalar.wgsl.jinja": "zhTCLhU7z3PVyvPl4rMfDCv55JFjr8wNm4OvsfKqUGQ=",
14
- "unary-vec4.wgsl.jinja": "xgxipqkd61CfxU63Rjg+Cn6ISilONZmzX8Stwl1Os7M="
15
  }
16
  },
17
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
18
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Not" }
 
 
 
19
  }
 
1
  {
2
  "name": "ai.onnx.Not",
3
+ "id": "_ai_onnx_not_webgpu_36db185",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "L8DnqPVQmtoUbVzOJf4ZOBc4B5szJWInfBVo8dLAsjI=",
11
+ "manifest.json": "XcMLy6Mv7EKh+Xx5cHodb3BrUbRuFMx5wwBDmoC34qQ=",
12
+ "test.json": "C0OWAWpvBPaJ3TPJV+Q7uEeKYDziZXz17iNTn7Bl8+s=",
13
+ "unary-scalar.wgsl.jinja": "aF5jl3TxM+kRr5JdAJgNXYpsijeufs+DRUN95lLAl50=",
14
+ "unary-vec4.wgsl.jinja": "UQ/vPY94Jhd4/nX5WCB8jGObg2b9oNcqq4hpdqDXUVM="
15
  }
16
  },
17
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
18
+ "webgpu": {
19
+ "manifestSpec": "2.0",
20
+ "variants": { "same_layout_vec4": ["unary-vec4.wgsl.jinja"], "elementwise": ["unary-scalar.wgsl.jinja"] }
21
+ }
22
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Not",
3
  "cases": [
4
  {
5
  "name": "bool_vector_basic",
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "bool_vector_basic",
build/webgpu/unary-scalar.wgsl.jinja CHANGED
@@ -1,15 +1,25 @@
1
-
2
- // Scalar unary fallback. Each branch retains the operation's numeric hardening,
3
- // including Payne-Hanek trigonometric range reduction and NaN/overflow guards.
4
- {{ env.wgsl.resourceDeclarations }}
5
-
6
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
7
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
8
  // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
9
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills the rest into y).
10
- let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
11
- if (i >= params.count) {
12
- return;
 
 
 
 
 
 
13
  }
 
 
 
 
 
 
 
14
  y[i] = select(0u, 1u, x[i] == 0u);
 
15
  }
 
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
+ // Scalar unary elementwise implementation. Specialization emits only the
19
+ // selected operation and any numerical helper it requires.
20
+ {{ env.wgsl.resourceDeclarations }}
21
+
22
+ {{ flat_tail_open() }}
23
  y[i] = select(0u, 1u, x[i] == 0u);
24
+ {{ flat_tail_close() -}}
25
  }
build/webgpu/unary-vec4.wgsl.jinja CHANGED
@@ -1,19 +1,38 @@
1
- // Loads and stores vec4<T> (128 bits) while retaining scalar per-component
2
- // arithmetic, including per-component helper calls for guard-heavy operations.
3
- {% if usesF16 %}
4
- enable f16;
5
- {% endif %}
6
  {{ env.wgsl.resourceDeclarations }}
7
 
8
 
 
 
 
 
 
9
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
10
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
11
  // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
12
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills the rest into y).
13
- let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
 
 
 
 
 
 
 
 
 
 
 
 
14
  if (i >= params.count) {
15
  return;
16
  }
 
 
17
  let xv = x[i];
18
  y[i] = select(vec4<u32>(0u), vec4<u32>(1u), xv == vec4<u32>(0u));
 
 
 
19
  }
 
1
+ // Loads and stores vec4<T> while evaluating the selected unary operation per
2
+ // component.
 
 
 
3
  {{ env.wgsl.resourceDeclarations }}
4
 
5
 
6
+ {% set vec4PerThread = vec4PerThread %}
7
+ {% if vec4PerThread > 1 %}
8
+ const ITEMS: u32 = {{ vec4PerThread }}u;
9
+ {% endif %}
10
+
11
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
12
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
13
  // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
14
+ // per-axis dispatch fold width (the dispatch caps x and spills the rest into y).
15
+ {% if vec4PerThread > 1 %}
16
+ // Each invocation walks ITEMS vec4 groups a span apart. Consecutive lanes
17
+ // access consecutive words on every step, while each lane can keep several
18
+ // independent loads in flight.
19
+ let tid = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
20
+ let span = (params.count + ITEMS - 1u) / ITEMS;
21
+ for (var j = 0u; j < ITEMS; j = j + 1u) {
22
+ let i = tid + j * span;
23
+ if (i >= params.count) {
24
+ break;
25
+ }
26
+ {% else %}
27
+ let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if (i >= params.count) {
29
  return;
30
  }
31
+ {% endif %}
32
+
33
  let xv = x[i];
34
  y[i] = select(vec4<u32>(0u), vec4<u32>(1u), xv == vec4<u32>(0u));
35
+ {% if vec4PerThread > 1 %}
36
+ }
37
+ {% endif %}
38
  }