sync 6fdf6301e2bb
Browse files- README.md +2 -2
- build/webgpu/attn-flash-decode-splitk-merge.wgsl.jinja +2 -30
- build/webgpu/bench.json +95 -18
- build/webgpu/manifest.json +41 -49
- build/webgpu/metadata.json +9 -9
- build/webgpu/paged-attention.wgsl.jinja +5 -68
- build/webgpu/paged-scatter-kv.wgsl.jinja +8 -38
- build/webgpu/test.json +336 -1
README.md
CHANGED
|
@@ -74,7 +74,7 @@ Every implementation variant requires `shader-f16`; the package has no variant-l
|
|
| 74 |
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
|
| 75 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 76 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 77 |
-
- [`bench.json`](build/webgpu/bench.json) — benchmark
|
| 78 |
- [`attn-flash-decode-splitk-merge.wgsl.jinja`](build/webgpu/attn-flash-decode-splitk-merge.wgsl.jinja)
|
| 79 |
- [`paged-attention.wgsl.jinja`](build/webgpu/paged-attention.wgsl.jinja)
|
| 80 |
- [`paged-scatter-kv.wgsl.jinja`](build/webgpu/paged-scatter-kv.wgsl.jinja)
|
|
@@ -82,7 +82,7 @@ Every implementation variant requires `shader-f16`; the package has no variant-l
|
|
| 82 |
## Use with `@huggingface/kernels`
|
| 83 |
|
| 84 |
```sh
|
| 85 |
-
npm install --save-exact @huggingface/kernels@0.0.1-preview.
|
| 86 |
```
|
| 87 |
|
| 88 |
Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
|
|
|
|
| 74 |
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
|
| 75 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 76 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 77 |
+
- [`bench.json`](build/webgpu/bench.json) — benchmark cases
|
| 78 |
- [`attn-flash-decode-splitk-merge.wgsl.jinja`](build/webgpu/attn-flash-decode-splitk-merge.wgsl.jinja)
|
| 79 |
- [`paged-attention.wgsl.jinja`](build/webgpu/paged-attention.wgsl.jinja)
|
| 80 |
- [`paged-scatter-kv.wgsl.jinja`](build/webgpu/paged-scatter-kv.wgsl.jinja)
|
|
|
|
| 82 |
## Use with `@huggingface/kernels`
|
| 83 |
|
| 84 |
```sh
|
| 85 |
+
npm install --save-exact @huggingface/kernels@0.0.1-preview.3
|
| 86 |
```
|
| 87 |
|
| 88 |
Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
|
build/webgpu/attn-flash-decode-splitk-merge.wgsl.jinja
CHANGED
|
@@ -1,9 +1,3 @@
|
|
| 1 |
-
{% if scalar == "f16" %}
|
| 2 |
-
enable f16;
|
| 3 |
-
{% endif %}
|
| 4 |
-
{% if splitQueries is not defined %}{% set splitQueries = false %}{% endif %}
|
| 5 |
-
{% if hasGate is not defined %}{% set hasGate = false %}{% endif %}
|
| 6 |
-
{% set qSeq = qSeq | default(0) %}
|
| 7 |
{{ env.wgsl.resourceDeclarations }}
|
| 8 |
|
| 9 |
// Split-K flash decode, pass 2 of 2. Combines the per-split un-normalized online
|
|
@@ -19,9 +13,6 @@ enable f16;
|
|
| 19 |
const HEAD_DIM_V4: u32 = {{ headDimV4 }}u;
|
| 20 |
const Q_HEADS: u32 = {{ qNumHeads }}u;
|
| 21 |
const NUM_SPLITS: u32 = {{ numSplits }}u;
|
| 22 |
-
{% if splitQueries %}
|
| 23 |
-
const Q_SEQ: u32 = {{ qSeq }}u;
|
| 24 |
-
{% endif %}
|
| 25 |
const Q_HIDDEN_V4: u32 = {{ qHiddenV4 }}u;
|
| 26 |
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 27 |
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
|
@@ -42,6 +33,7 @@ fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
|
| 42 |
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 43 |
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 44 |
}
|
|
|
|
| 45 |
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 46 |
return exp(shifted_value(value, maxValue));
|
| 47 |
}
|
|
@@ -50,21 +42,14 @@ fn main(
|
|
| 50 |
@builtin(workgroup_id) wg: vec3<u32>,
|
| 51 |
@builtin(local_invocation_id) lid: vec3<u32>
|
| 52 |
) {
|
| 53 |
-
{% if splitQueries %}
|
| 54 |
-
let queryToken = wg.x;
|
| 55 |
-
{% endif %}
|
| 56 |
let h = wg.y;
|
| 57 |
let b = wg.z;
|
| 58 |
let d4 = lid.x;
|
| 59 |
-
if (h >= Q_HEADS || d4 >= HEAD_DIM_V4
|
| 60 |
return;
|
| 61 |
}
|
| 62 |
|
| 63 |
-
{% if splitQueries %}
|
| 64 |
-
let mdBase = ((b * Q_SEQ + queryToken) * Q_HEADS + h) * NUM_SPLITS;
|
| 65 |
-
{% else %}
|
| 66 |
let mdBase = (b * Q_HEADS + h) * NUM_SPLITS;
|
| 67 |
-
{% endif %}
|
| 68 |
var globalMax = -FLT_MAX;
|
| 69 |
for (var s: u32 = 0u; s < NUM_SPLITS; s = s + 1u) {
|
| 70 |
globalMax = max(globalMax, partial_stats[mdBase + s].x);
|
|
@@ -75,28 +60,15 @@ fn main(
|
|
| 75 |
let stats = partial_stats[mdBase + s];
|
| 76 |
let w = exp_shift(stats.x, globalMax);
|
| 77 |
globalDenom = globalDenom + stats.y * w;
|
| 78 |
-
{% if splitQueries %}
|
| 79 |
-
let pBase = (((b * Q_SEQ + queryToken) * Q_HEADS + h) * NUM_SPLITS + s) * HEAD_DIM_V4;
|
| 80 |
-
{% else %}
|
| 81 |
let pBase = ((b * Q_HEADS + h) * NUM_SPLITS + s) * HEAD_DIM_V4;
|
| 82 |
-
{% endif %}
|
| 83 |
outV = outV + partial_out[pBase + d4] * w;
|
| 84 |
}
|
| 85 |
|
| 86 |
-
{% if splitQueries %}
|
| 87 |
-
let qBaseV4 = (b * Q_SEQ + queryToken) * Q_HIDDEN_V4 + h * HEAD_DIM_V4;
|
| 88 |
-
{% else %}
|
| 89 |
let qBaseV4 = b * Q_HIDDEN_V4 + h * HEAD_DIM_V4;
|
| 90 |
-
{% endif %}
|
| 91 |
// A split that covers no keys writes vec2(-FLT_MAX, 0), so when every split is empty —
|
| 92 |
// a query whose window admits nothing — globalMax stays -FLT_MAX, exp_shift(x, x) is 1,
|
| 93 |
// and globalDenom lands on exactly 0. Emit 0 rather than dividing so an all-empty
|
| 94 |
// row has a defined zero result.
|
| 95 |
var outValue = select(vec4<f32>(0.0), outV / globalDenom, globalDenom > 0.0);
|
| 96 |
-
{% if hasGate %}
|
| 97 |
-
// The gated route multiplies the normalized attention output elementwise by its gate.
|
| 98 |
-
let gateV = vec4<f32>(gate[qBaseV4 + d4]);
|
| 99 |
-
outValue = outValue * (vec4<f32>(1.0) / (vec4<f32>(1.0) + exp(-gateV)));
|
| 100 |
-
{% endif %}
|
| 101 |
output[qBaseV4 + d4] = vec4<{{ scalar }}>(outValue);
|
| 102 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
{{ env.wgsl.resourceDeclarations }}
|
| 2 |
|
| 3 |
// Split-K flash decode, pass 2 of 2. Combines the per-split un-normalized online
|
|
|
|
| 13 |
const HEAD_DIM_V4: u32 = {{ headDimV4 }}u;
|
| 14 |
const Q_HEADS: u32 = {{ qNumHeads }}u;
|
| 15 |
const NUM_SPLITS: u32 = {{ numSplits }}u;
|
|
|
|
|
|
|
|
|
|
| 16 |
const Q_HIDDEN_V4: u32 = {{ qHiddenV4 }}u;
|
| 17 |
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 18 |
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
|
|
|
| 33 |
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 34 |
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 35 |
}
|
| 36 |
+
|
| 37 |
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 38 |
return exp(shifted_value(value, maxValue));
|
| 39 |
}
|
|
|
|
| 42 |
@builtin(workgroup_id) wg: vec3<u32>,
|
| 43 |
@builtin(local_invocation_id) lid: vec3<u32>
|
| 44 |
) {
|
|
|
|
|
|
|
|
|
|
| 45 |
let h = wg.y;
|
| 46 |
let b = wg.z;
|
| 47 |
let d4 = lid.x;
|
| 48 |
+
if (h >= Q_HEADS || d4 >= HEAD_DIM_V4) {
|
| 49 |
return;
|
| 50 |
}
|
| 51 |
|
|
|
|
|
|
|
|
|
|
| 52 |
let mdBase = (b * Q_HEADS + h) * NUM_SPLITS;
|
|
|
|
| 53 |
var globalMax = -FLT_MAX;
|
| 54 |
for (var s: u32 = 0u; s < NUM_SPLITS; s = s + 1u) {
|
| 55 |
globalMax = max(globalMax, partial_stats[mdBase + s].x);
|
|
|
|
| 60 |
let stats = partial_stats[mdBase + s];
|
| 61 |
let w = exp_shift(stats.x, globalMax);
|
| 62 |
globalDenom = globalDenom + stats.y * w;
|
|
|
|
|
|
|
|
|
|
| 63 |
let pBase = ((b * Q_HEADS + h) * NUM_SPLITS + s) * HEAD_DIM_V4;
|
|
|
|
| 64 |
outV = outV + partial_out[pBase + d4] * w;
|
| 65 |
}
|
| 66 |
|
|
|
|
|
|
|
|
|
|
| 67 |
let qBaseV4 = b * Q_HIDDEN_V4 + h * HEAD_DIM_V4;
|
|
|
|
| 68 |
// A split that covers no keys writes vec2(-FLT_MAX, 0), so when every split is empty —
|
| 69 |
// a query whose window admits nothing — globalMax stays -FLT_MAX, exp_shift(x, x) is 1,
|
| 70 |
// and globalDenom lands on exactly 0. Emit 0 rather than dividing so an all-empty
|
| 71 |
// row has a defined zero result.
|
| 72 |
var outValue = select(vec4<f32>(0.0), outV / globalDenom, globalDenom > 0.0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
output[qBaseV4 + d4] = vec4<{{ scalar }}>(outValue);
|
| 74 |
}
|
build/webgpu/bench.json
CHANGED
|
@@ -38,7 +38,7 @@
|
|
| 38 |
"name": "paged-decode-llama-b16-past2048-h32kv32-d128",
|
| 39 |
"preset": "model",
|
| 40 |
"provenance": {
|
| 41 |
-
"notes": "Llama class defaults (32 query heads, no GQA, head_dim 128) decoding 16 concurrent sequences with 2048 tokens of paged KV each."
|
| 42 |
},
|
| 43 |
"vars": { "dtype": "float16", "batch": 16, "past": 2048, "heads": 32, "kvHeads": 32, "headDim": 128 },
|
| 44 |
"attrs": { "num_heads": 32, "kv_num_heads": 32 },
|
|
@@ -57,12 +57,9 @@
|
|
| 57 |
"cumulativeSequenceLengthT": {
|
| 58 |
"shape": [17],
|
| 59 |
"dtype": "int32",
|
| 60 |
-
"
|
| 61 |
-
"seed": 7605,
|
| 62 |
-
"step": 1,
|
| 63 |
-
"mod": 17
|
| 64 |
},
|
| 65 |
-
"pastSeqlensT": { "shape": [16], "dtype": "int32", "dist": "constant", "value":
|
| 66 |
"blockTableT": {
|
| 67 |
"shape": [16, 128],
|
| 68 |
"dtype": "int32",
|
|
@@ -81,7 +78,7 @@
|
|
| 81 |
"name": "paged-decode-mistral-b16-past4096-h32kv8-d128",
|
| 82 |
"preset": "model",
|
| 83 |
"provenance": {
|
| 84 |
-
"notes": "Mistral class defaults (32 query heads, 8 key/value heads, head_dim 128) at 4096 tokens
|
| 85 |
},
|
| 86 |
"vars": { "dtype": "float16", "batch": 16, "past": 4096, "heads": 32, "kvHeads": 8, "headDim": 128 },
|
| 87 |
"attrs": { "num_heads": 32, "kv_num_heads": 8 },
|
|
@@ -94,12 +91,9 @@
|
|
| 94 |
"cumulativeSequenceLengthT": {
|
| 95 |
"shape": [17],
|
| 96 |
"dtype": "int32",
|
| 97 |
-
"
|
| 98 |
-
"seed": 7705,
|
| 99 |
-
"step": 1,
|
| 100 |
-
"mod": 17
|
| 101 |
},
|
| 102 |
-
"pastSeqlensT": { "shape": [16], "dtype": "int32", "dist": "constant", "value":
|
| 103 |
"blockTableT": {
|
| 104 |
"shape": [16, 256],
|
| 105 |
"dtype": "int32",
|
|
@@ -118,7 +112,7 @@
|
|
| 118 |
"name": "paged-decode-gemma3-b8-past2048-h8kv4-d256",
|
| 119 |
"preset": "model",
|
| 120 |
"provenance": {
|
| 121 |
-
"notes": "Gemma3 class defaults (8 query heads, 4 key/value heads, head_dim 256) -- few heads but the widest head dimension in this set."
|
| 122 |
},
|
| 123 |
"vars": { "dtype": "float16", "batch": 8, "past": 2048, "heads": 8, "kvHeads": 4, "headDim": 256 },
|
| 124 |
"attrs": { "num_heads": 8, "kv_num_heads": 4 },
|
|
@@ -131,12 +125,9 @@
|
|
| 131 |
"cumulativeSequenceLengthT": {
|
| 132 |
"shape": [9],
|
| 133 |
"dtype": "int32",
|
| 134 |
-
"
|
| 135 |
-
"seed": 7805,
|
| 136 |
-
"step": 1,
|
| 137 |
-
"mod": 9
|
| 138 |
},
|
| 139 |
-
"pastSeqlensT": { "shape": [8], "dtype": "int32", "dist": "constant", "value":
|
| 140 |
"blockTableT": {
|
| 141 |
"shape": [8, 128],
|
| 142 |
"dtype": "int32",
|
|
@@ -150,6 +141,92 @@
|
|
| 150 |
"bench": {
|
| 151 |
"metrics": [{ "type": "bandwidth", "value": "2 * args.batch * args.kvHeads * args.past * args.headDim * 2" }]
|
| 152 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
}
|
| 154 |
]
|
| 155 |
}
|
|
|
|
| 38 |
"name": "paged-decode-llama-b16-past2048-h32kv32-d128",
|
| 39 |
"preset": "model",
|
| 40 |
"provenance": {
|
| 41 |
+
"notes": "Llama class defaults (32 query heads, no GQA, head_dim 128) decoding 16 concurrent sequences with 2048 tokens of paged KV each. Context counts include this step: past_seqlens is context - 1 and the exclusive prefix [0, ..., batch] assigns one new token per sequence. All logical blocks map to distinct physical blocks, including the final append slot."
|
| 42 |
},
|
| 43 |
"vars": { "dtype": "float16", "batch": 16, "past": 2048, "heads": 32, "kvHeads": 32, "headDim": 128 },
|
| 44 |
"attrs": { "num_heads": 32, "kv_num_heads": 32 },
|
|
|
|
| 57 |
"cumulativeSequenceLengthT": {
|
| 58 |
"shape": [17],
|
| 59 |
"dtype": "int32",
|
| 60 |
+
"data": { "kind": "linspace", "start": 0, "end": 16 }
|
|
|
|
|
|
|
|
|
|
| 61 |
},
|
| 62 |
+
"pastSeqlensT": { "shape": [16], "dtype": "int32", "dist": "constant", "value": 2047 },
|
| 63 |
"blockTableT": {
|
| 64 |
"shape": [16, 128],
|
| 65 |
"dtype": "int32",
|
|
|
|
| 78 |
"name": "paged-decode-mistral-b16-past4096-h32kv8-d128",
|
| 79 |
"preset": "model",
|
| 80 |
"provenance": {
|
| 81 |
+
"notes": "Mistral class defaults (32 query heads, 8 key/value heads, head_dim 128) at 4096 tokens per sequence including the current token -- the GQA case, where the cache is a quarter the size for the same context. Context counts include this step: past_seqlens is context - 1 and the exclusive prefix [0, ..., batch] assigns one new token per sequence. All logical blocks map to distinct physical blocks, including the final append slot."
|
| 82 |
},
|
| 83 |
"vars": { "dtype": "float16", "batch": 16, "past": 4096, "heads": 32, "kvHeads": 8, "headDim": 128 },
|
| 84 |
"attrs": { "num_heads": 32, "kv_num_heads": 8 },
|
|
|
|
| 91 |
"cumulativeSequenceLengthT": {
|
| 92 |
"shape": [17],
|
| 93 |
"dtype": "int32",
|
| 94 |
+
"data": { "kind": "linspace", "start": 0, "end": 16 }
|
|
|
|
|
|
|
|
|
|
| 95 |
},
|
| 96 |
+
"pastSeqlensT": { "shape": [16], "dtype": "int32", "dist": "constant", "value": 4095 },
|
| 97 |
"blockTableT": {
|
| 98 |
"shape": [16, 256],
|
| 99 |
"dtype": "int32",
|
|
|
|
| 112 |
"name": "paged-decode-gemma3-b8-past2048-h8kv4-d256",
|
| 113 |
"preset": "model",
|
| 114 |
"provenance": {
|
| 115 |
+
"notes": "Gemma3 class defaults (8 query heads, 4 key/value heads, head_dim 256) -- few heads but the widest head dimension in this set. Context counts include this step: past_seqlens is context - 1 and the exclusive prefix [0, ..., batch] assigns one new token per sequence. All logical blocks map to distinct physical blocks, including the final append slot."
|
| 116 |
},
|
| 117 |
"vars": { "dtype": "float16", "batch": 8, "past": 2048, "heads": 8, "kvHeads": 4, "headDim": 256 },
|
| 118 |
"attrs": { "num_heads": 8, "kv_num_heads": 4 },
|
|
|
|
| 125 |
"cumulativeSequenceLengthT": {
|
| 126 |
"shape": [9],
|
| 127 |
"dtype": "int32",
|
| 128 |
+
"data": { "kind": "linspace", "start": 0, "end": 8 }
|
|
|
|
|
|
|
|
|
|
| 129 |
},
|
| 130 |
+
"pastSeqlensT": { "shape": [8], "dtype": "int32", "dist": "constant", "value": 2047 },
|
| 131 |
"blockTableT": {
|
| 132 |
"shape": [8, 128],
|
| 133 |
"dtype": "int32",
|
|
|
|
| 141 |
"bench": {
|
| 142 |
"metrics": [{ "type": "bandwidth", "value": "2 * args.batch * args.kvHeads * args.past * args.headDim * 2" }]
|
| 143 |
}
|
| 144 |
+
},
|
| 145 |
+
{
|
| 146 |
+
"name": "scatter-fold-wg1-independent-sequences",
|
| 147 |
+
"provenance": {
|
| 148 |
+
"notes": "A large scatter workload crosses the WebGPU dispatch-grid limit. One-token sequences bound attention cost; nonzero dyadic cycles expose missing tail writes and misplaced cache/value elements."
|
| 149 |
+
},
|
| 150 |
+
"attrs": { "num_heads": 8, "kv_num_heads": 8 },
|
| 151 |
+
"tunables": { "WORKGROUP_SIZE": 64, "SCATTER_WORKGROUP_SIZE": 1 },
|
| 152 |
+
"inputs": {
|
| 153 |
+
"queryT": { "dtype": "float16", "shape": [64, 1024], "data": { "kind": "constant", "value": 0.0 } },
|
| 154 |
+
"keyT": {
|
| 155 |
+
"dtype": "float16",
|
| 156 |
+
"shape": [64, 1024],
|
| 157 |
+
"data": { "kind": "cycle", "values": [1.0, 2.0, 4.0, -1.0, -2.0] }
|
| 158 |
+
},
|
| 159 |
+
"valueT": {
|
| 160 |
+
"dtype": "float16",
|
| 161 |
+
"shape": [64, 1024],
|
| 162 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] }
|
| 163 |
+
},
|
| 164 |
+
"keyCacheT": { "dtype": "float16", "shape": [64, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 165 |
+
"valueCacheT": { "dtype": "float16", "shape": [64, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 166 |
+
"cumulativeSequenceLengthT": {
|
| 167 |
+
"dtype": "int32",
|
| 168 |
+
"shape": [65],
|
| 169 |
+
"data": { "kind": "linspace", "start": 0, "end": 64 }
|
| 170 |
+
},
|
| 171 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [64], "data": { "kind": "constant", "value": 0 } },
|
| 172 |
+
"blockTableT": { "dtype": "int32", "shape": [64, 1], "data": { "kind": "linspace", "start": 0, "end": 63 } }
|
| 173 |
+
},
|
| 174 |
+
"outputs": {
|
| 175 |
+
"outputT": { "dtype": "float16", "shape": [64, 1024] },
|
| 176 |
+
"keyCacheT": { "dtype": "float16", "shape": [64, 1, 8, 128] },
|
| 177 |
+
"valueCacheT": { "dtype": "float16", "shape": [64, 1, 8, 128] }
|
| 178 |
+
},
|
| 179 |
+
"preset": "edge",
|
| 180 |
+
"vars": { "tokens": 64, "heads": 8, "headDim": 128 },
|
| 181 |
+
"tunableSpace": { "WORKGROUP_SIZE": [64], "SCATTER_WORKGROUP_SIZE": [1] },
|
| 182 |
+
"bench": {
|
| 183 |
+
"metrics": [
|
| 184 |
+
{ "type": "bandwidth", "value": "6 * args.tokens * args.heads * args.headDim * 2", "provenance": "logical" }
|
| 185 |
+
]
|
| 186 |
+
}
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"name": "scatter-fold-wg4-independent-sequences",
|
| 190 |
+
"provenance": {
|
| 191 |
+
"notes": "A large scatter workload crosses the WebGPU dispatch-grid limit. One-token sequences bound attention cost; nonzero dyadic cycles expose missing tail writes and misplaced cache/value elements."
|
| 192 |
+
},
|
| 193 |
+
"attrs": { "num_heads": 8, "kv_num_heads": 8 },
|
| 194 |
+
"tunables": { "WORKGROUP_SIZE": 64, "SCATTER_WORKGROUP_SIZE": 4 },
|
| 195 |
+
"inputs": {
|
| 196 |
+
"queryT": { "dtype": "float16", "shape": [256, 1024], "data": { "kind": "constant", "value": 0.0 } },
|
| 197 |
+
"keyT": {
|
| 198 |
+
"dtype": "float16",
|
| 199 |
+
"shape": [256, 1024],
|
| 200 |
+
"data": { "kind": "cycle", "values": [1.0, 2.0, 4.0, -1.0, -2.0] }
|
| 201 |
+
},
|
| 202 |
+
"valueT": {
|
| 203 |
+
"dtype": "float16",
|
| 204 |
+
"shape": [256, 1024],
|
| 205 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] }
|
| 206 |
+
},
|
| 207 |
+
"keyCacheT": { "dtype": "float16", "shape": [256, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 208 |
+
"valueCacheT": { "dtype": "float16", "shape": [256, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 209 |
+
"cumulativeSequenceLengthT": {
|
| 210 |
+
"dtype": "int32",
|
| 211 |
+
"shape": [257],
|
| 212 |
+
"data": { "kind": "linspace", "start": 0, "end": 256 }
|
| 213 |
+
},
|
| 214 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [256], "data": { "kind": "constant", "value": 0 } },
|
| 215 |
+
"blockTableT": { "dtype": "int32", "shape": [256, 1], "data": { "kind": "linspace", "start": 0, "end": 255 } }
|
| 216 |
+
},
|
| 217 |
+
"outputs": {
|
| 218 |
+
"outputT": { "dtype": "float16", "shape": [256, 1024] },
|
| 219 |
+
"keyCacheT": { "dtype": "float16", "shape": [256, 1, 8, 128] },
|
| 220 |
+
"valueCacheT": { "dtype": "float16", "shape": [256, 1, 8, 128] }
|
| 221 |
+
},
|
| 222 |
+
"preset": "edge",
|
| 223 |
+
"vars": { "tokens": 256, "heads": 8, "headDim": 128 },
|
| 224 |
+
"tunableSpace": { "WORKGROUP_SIZE": [64], "SCATTER_WORKGROUP_SIZE": [4] },
|
| 225 |
+
"bench": {
|
| 226 |
+
"metrics": [
|
| 227 |
+
{ "type": "bandwidth", "value": "6 * args.tokens * args.heads * args.headDim * 2", "provenance": "logical" }
|
| 228 |
+
]
|
| 229 |
+
}
|
| 230 |
}
|
| 231 |
]
|
| 232 |
}
|
build/webgpu/manifest.json
CHANGED
|
@@ -86,51 +86,43 @@
|
|
| 86 |
"headDim": "headSize",
|
| 87 |
"qNumHeads": "attrs.num_heads",
|
| 88 |
"qHiddenV4": "qHidden / 4",
|
| 89 |
-
"hasBias": false,
|
| 90 |
"outVec": "\"vec4<f16>\" if dtypes.T == \"f16\" else \"vec4<f32>\""
|
| 91 |
},
|
| 92 |
"when": ["pagedContractOk", "dispatchFits"],
|
| 93 |
"bindings": {
|
| 94 |
-
"key": { "arg": "keyT", "
|
| 95 |
-
"value": { "arg": "valueT", "
|
| 96 |
-
"key_cache": { "arg": "keyCacheT", "
|
| 97 |
-
"value_cache": { "arg": "valueCacheT", "
|
| 98 |
-
"cumulative_sequence_length": {
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
"elementType": "i32"
|
| 102 |
-
},
|
| 103 |
-
"past_seqlens": { "arg": "pastSeqlensT", "buffer": "read-only-storage", "elementType": "i32" },
|
| 104 |
-
"block_table": { "arg": "blockTableT", "buffer": "read-only-storage", "elementType": "i32" },
|
| 105 |
"params": {
|
| 106 |
-
"buffer": "uniform",
|
| 107 |
"struct": [
|
| 108 |
{ "name": "batchSize", "type": "u32", "value": "batchSize" },
|
| 109 |
{ "name": "scatterCount", "type": "u32", "value": "tokenCount * attrs.kv_num_heads * headSize" }
|
| 110 |
]
|
| 111 |
},
|
| 112 |
-
"slot_mapping": { "arg": "slotMappingT", "
|
| 113 |
-
"
|
| 114 |
"name": "params",
|
| 115 |
-
"buffer": "uniform",
|
| 116 |
"struct": [{ "name": "scatterCount", "type": "u32", "value": "tokenCount * attrs.kv_num_heads * headSize" }]
|
| 117 |
},
|
| 118 |
-
"query": { "arg": "queryT", "
|
| 119 |
-
"
|
| 120 |
"arg": "keyCacheT",
|
| 121 |
"name": "key_cache",
|
| 122 |
"buffer": "read-only-storage",
|
| 123 |
"elementType": "$cacheVec"
|
| 124 |
},
|
| 125 |
-
"
|
| 126 |
"arg": "valueCacheT",
|
| 127 |
"name": "value_cache",
|
| 128 |
"buffer": "read-only-storage",
|
| 129 |
"elementType": "$cacheVec"
|
| 130 |
},
|
| 131 |
-
"
|
| 132 |
"name": "params",
|
| 133 |
-
"buffer": "uniform",
|
| 134 |
"struct": [
|
| 135 |
{ "name": "batchSize", "type": "u32", "value": "batchSize" },
|
| 136 |
{ "name": "scale", "type": "f32", "value": "attrs.scale if has(attrs, \"scale\") else 0" }
|
|
@@ -167,14 +159,14 @@
|
|
| 167 |
"derive": { "splitK": true },
|
| 168 |
"bindings": [
|
| 169 |
"query",
|
| 170 |
-
"
|
| 171 |
-
"
|
| 172 |
"cumulative_sequence_length",
|
| 173 |
"past_seqlens",
|
| 174 |
"block_table",
|
| 175 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 176 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 177 |
-
"
|
| 178 |
],
|
| 179 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 180 |
},
|
|
@@ -224,13 +216,13 @@
|
|
| 224 |
"shader": "paged-attention.wgsl.jinja",
|
| 225 |
"bindings": [
|
| 226 |
"query",
|
| 227 |
-
"
|
| 228 |
-
"
|
| 229 |
"cumulative_sequence_length",
|
| 230 |
"past_seqlens",
|
| 231 |
"block_table",
|
| 232 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 233 |
-
"
|
| 234 |
],
|
| 235 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 236 |
}
|
|
@@ -251,7 +243,7 @@
|
|
| 251 |
"id": "scatter",
|
| 252 |
"name": "PagedAttention.ScatterKV",
|
| 253 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 254 |
-
"bindings": ["key", "value", "key_cache", "value_cache", "slot_mapping", "
|
| 255 |
"dispatch": {
|
| 256 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 257 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
@@ -265,14 +257,14 @@
|
|
| 265 |
"derive": { "splitK": true },
|
| 266 |
"bindings": [
|
| 267 |
"query",
|
| 268 |
-
"
|
| 269 |
-
"
|
| 270 |
"cumulative_sequence_length",
|
| 271 |
"past_seqlens",
|
| 272 |
"block_table",
|
| 273 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 274 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 275 |
-
"
|
| 276 |
],
|
| 277 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 278 |
},
|
|
@@ -309,7 +301,7 @@
|
|
| 309 |
"id": "scatter",
|
| 310 |
"name": "PagedAttention.ScatterKV",
|
| 311 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 312 |
-
"bindings": ["key", "value", "key_cache", "value_cache", "slot_mapping", "
|
| 313 |
"dispatch": {
|
| 314 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 315 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
@@ -322,13 +314,13 @@
|
|
| 322 |
"shader": "paged-attention.wgsl.jinja",
|
| 323 |
"bindings": [
|
| 324 |
"query",
|
| 325 |
-
"
|
| 326 |
-
"
|
| 327 |
"cumulative_sequence_length",
|
| 328 |
"past_seqlens",
|
| 329 |
"block_table",
|
| 330 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 331 |
-
"
|
| 332 |
],
|
| 333 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 334 |
}
|
|
@@ -363,14 +355,14 @@
|
|
| 363 |
"derive": { "splitK": true },
|
| 364 |
"bindings": [
|
| 365 |
"query",
|
| 366 |
-
"
|
| 367 |
-
"
|
| 368 |
"cumulative_sequence_length",
|
| 369 |
"past_seqlens",
|
| 370 |
"block_table",
|
| 371 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 372 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 373 |
-
"
|
| 374 |
],
|
| 375 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 376 |
},
|
|
@@ -420,13 +412,13 @@
|
|
| 420 |
"shader": "paged-attention.wgsl.jinja",
|
| 421 |
"bindings": [
|
| 422 |
"query",
|
| 423 |
-
"
|
| 424 |
-
"
|
| 425 |
"cumulative_sequence_length",
|
| 426 |
"past_seqlens",
|
| 427 |
"block_table",
|
| 428 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 429 |
-
"
|
| 430 |
],
|
| 431 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 432 |
}
|
|
@@ -447,7 +439,7 @@
|
|
| 447 |
"id": "scatter",
|
| 448 |
"name": "PagedAttention.ScatterKV",
|
| 449 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 450 |
-
"bindings": ["query", "key_cache", "value_cache", "slot_mapping", "
|
| 451 |
"dispatch": {
|
| 452 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 453 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
@@ -461,14 +453,14 @@
|
|
| 461 |
"derive": { "splitK": true },
|
| 462 |
"bindings": [
|
| 463 |
"query",
|
| 464 |
-
"
|
| 465 |
-
"
|
| 466 |
"cumulative_sequence_length",
|
| 467 |
"past_seqlens",
|
| 468 |
"block_table",
|
| 469 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 470 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 471 |
-
"
|
| 472 |
],
|
| 473 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 474 |
},
|
|
@@ -505,7 +497,7 @@
|
|
| 505 |
"id": "scatter",
|
| 506 |
"name": "PagedAttention.ScatterKV",
|
| 507 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 508 |
-
"bindings": ["query", "key_cache", "value_cache", "slot_mapping", "
|
| 509 |
"dispatch": {
|
| 510 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 511 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
@@ -518,13 +510,13 @@
|
|
| 518 |
"shader": "paged-attention.wgsl.jinja",
|
| 519 |
"bindings": [
|
| 520 |
"query",
|
| 521 |
-
"
|
| 522 |
-
"
|
| 523 |
"cumulative_sequence_length",
|
| 524 |
"past_seqlens",
|
| 525 |
"block_table",
|
| 526 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 527 |
-
"
|
| 528 |
],
|
| 529 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 530 |
}
|
|
|
|
| 86 |
"headDim": "headSize",
|
| 87 |
"qNumHeads": "attrs.num_heads",
|
| 88 |
"qHiddenV4": "qHidden / 4",
|
|
|
|
| 89 |
"outVec": "\"vec4<f16>\" if dtypes.T == \"f16\" else \"vec4<f32>\""
|
| 90 |
},
|
| 91 |
"when": ["pagedContractOk", "dispatchFits"],
|
| 92 |
"bindings": {
|
| 93 |
+
"key": { "arg": "keyT", "elementType": "$aScalar" },
|
| 94 |
+
"value": { "arg": "valueT", "elementType": "$aScalar" },
|
| 95 |
+
"key_cache": { "arg": "keyCacheT", "elementType": "$aScalar" },
|
| 96 |
+
"value_cache": { "arg": "valueCacheT", "elementType": "$aScalar" },
|
| 97 |
+
"cumulative_sequence_length": { "arg": "cumulativeSequenceLengthT", "elementType": "i32" },
|
| 98 |
+
"past_seqlens": { "arg": "pastSeqlensT", "elementType": "i32" },
|
| 99 |
+
"block_table": { "arg": "blockTableT", "elementType": "i32" },
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
"params": {
|
|
|
|
| 101 |
"struct": [
|
| 102 |
{ "name": "batchSize", "type": "u32", "value": "batchSize" },
|
| 103 |
{ "name": "scatterCount", "type": "u32", "value": "tokenCount * attrs.kv_num_heads * headSize" }
|
| 104 |
]
|
| 105 |
},
|
| 106 |
+
"slot_mapping": { "arg": "slotMappingT", "elementType": "i32" },
|
| 107 |
+
"params_scatter": {
|
| 108 |
"name": "params",
|
|
|
|
| 109 |
"struct": [{ "name": "scatterCount", "type": "u32", "value": "tokenCount * attrs.kv_num_heads * headSize" }]
|
| 110 |
},
|
| 111 |
+
"query": { "arg": "queryT", "elementType": "$aScalar" },
|
| 112 |
+
"key_cache_main": {
|
| 113 |
"arg": "keyCacheT",
|
| 114 |
"name": "key_cache",
|
| 115 |
"buffer": "read-only-storage",
|
| 116 |
"elementType": "$cacheVec"
|
| 117 |
},
|
| 118 |
+
"value_cache_main": {
|
| 119 |
"arg": "valueCacheT",
|
| 120 |
"name": "value_cache",
|
| 121 |
"buffer": "read-only-storage",
|
| 122 |
"elementType": "$cacheVec"
|
| 123 |
},
|
| 124 |
+
"params_main": {
|
| 125 |
"name": "params",
|
|
|
|
| 126 |
"struct": [
|
| 127 |
{ "name": "batchSize", "type": "u32", "value": "batchSize" },
|
| 128 |
{ "name": "scale", "type": "f32", "value": "attrs.scale if has(attrs, \"scale\") else 0" }
|
|
|
|
| 159 |
"derive": { "splitK": true },
|
| 160 |
"bindings": [
|
| 161 |
"query",
|
| 162 |
+
"key_cache_main",
|
| 163 |
+
"value_cache_main",
|
| 164 |
"cumulative_sequence_length",
|
| 165 |
"past_seqlens",
|
| 166 |
"block_table",
|
| 167 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 168 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 169 |
+
"params_main"
|
| 170 |
],
|
| 171 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 172 |
},
|
|
|
|
| 216 |
"shader": "paged-attention.wgsl.jinja",
|
| 217 |
"bindings": [
|
| 218 |
"query",
|
| 219 |
+
"key_cache_main",
|
| 220 |
+
"value_cache_main",
|
| 221 |
"cumulative_sequence_length",
|
| 222 |
"past_seqlens",
|
| 223 |
"block_table",
|
| 224 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 225 |
+
"params_main"
|
| 226 |
],
|
| 227 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 228 |
}
|
|
|
|
| 243 |
"id": "scatter",
|
| 244 |
"name": "PagedAttention.ScatterKV",
|
| 245 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 246 |
+
"bindings": ["key", "value", "key_cache", "value_cache", "slot_mapping", "params_scatter"],
|
| 247 |
"dispatch": {
|
| 248 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 249 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
|
|
| 257 |
"derive": { "splitK": true },
|
| 258 |
"bindings": [
|
| 259 |
"query",
|
| 260 |
+
"key_cache_main",
|
| 261 |
+
"value_cache_main",
|
| 262 |
"cumulative_sequence_length",
|
| 263 |
"past_seqlens",
|
| 264 |
"block_table",
|
| 265 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 266 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 267 |
+
"params_main"
|
| 268 |
],
|
| 269 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 270 |
},
|
|
|
|
| 301 |
"id": "scatter",
|
| 302 |
"name": "PagedAttention.ScatterKV",
|
| 303 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 304 |
+
"bindings": ["key", "value", "key_cache", "value_cache", "slot_mapping", "params_scatter"],
|
| 305 |
"dispatch": {
|
| 306 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 307 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
|
|
| 314 |
"shader": "paged-attention.wgsl.jinja",
|
| 315 |
"bindings": [
|
| 316 |
"query",
|
| 317 |
+
"key_cache_main",
|
| 318 |
+
"value_cache_main",
|
| 319 |
"cumulative_sequence_length",
|
| 320 |
"past_seqlens",
|
| 321 |
"block_table",
|
| 322 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 323 |
+
"params_main"
|
| 324 |
],
|
| 325 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 326 |
}
|
|
|
|
| 355 |
"derive": { "splitK": true },
|
| 356 |
"bindings": [
|
| 357 |
"query",
|
| 358 |
+
"key_cache_main",
|
| 359 |
+
"value_cache_main",
|
| 360 |
"cumulative_sequence_length",
|
| 361 |
"past_seqlens",
|
| 362 |
"block_table",
|
| 363 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 364 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 365 |
+
"params_main"
|
| 366 |
],
|
| 367 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 368 |
},
|
|
|
|
| 412 |
"shader": "paged-attention.wgsl.jinja",
|
| 413 |
"bindings": [
|
| 414 |
"query",
|
| 415 |
+
"key_cache_main",
|
| 416 |
+
"value_cache_main",
|
| 417 |
"cumulative_sequence_length",
|
| 418 |
"past_seqlens",
|
| 419 |
"block_table",
|
| 420 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 421 |
+
"params_main"
|
| 422 |
],
|
| 423 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 424 |
}
|
|
|
|
| 439 |
"id": "scatter",
|
| 440 |
"name": "PagedAttention.ScatterKV",
|
| 441 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 442 |
+
"bindings": ["query", "key_cache", "value_cache", "slot_mapping", "params_scatter"],
|
| 443 |
"dispatch": {
|
| 444 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 445 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
|
|
| 453 |
"derive": { "splitK": true },
|
| 454 |
"bindings": [
|
| 455 |
"query",
|
| 456 |
+
"key_cache_main",
|
| 457 |
+
"value_cache_main",
|
| 458 |
"cumulative_sequence_length",
|
| 459 |
"past_seqlens",
|
| 460 |
"block_table",
|
| 461 |
{ "scratch": "partialOut", "name": "partial_out", "elementType": "vec4<f32>" },
|
| 462 |
{ "scratch": "partialStats", "name": "partial_stats", "elementType": "vec2<f32>" },
|
| 463 |
+
"params_main"
|
| 464 |
],
|
| 465 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads", "z": "numSplits" }
|
| 466 |
},
|
|
|
|
| 497 |
"id": "scatter",
|
| 498 |
"name": "PagedAttention.ScatterKV",
|
| 499 |
"shader": "paged-scatter-kv.wgsl.jinja",
|
| 500 |
+
"bindings": ["query", "key_cache", "value_cache", "slot_mapping", "params_scatter"],
|
| 501 |
"dispatch": {
|
| 502 |
"x": "min(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
| 503 |
"y": "ceilDiv(ceilDiv((tokenCount * attrs.kv_num_heads * headSize), (tunables.SCATTER_WORKGROUP_SIZE)), 65535)",
|
|
|
|
| 510 |
"shader": "paged-attention.wgsl.jinja",
|
| 511 |
"bindings": [
|
| 512 |
"query",
|
| 513 |
+
"key_cache_main",
|
| 514 |
+
"value_cache_main",
|
| 515 |
"cumulative_sequence_length",
|
| 516 |
"past_seqlens",
|
| 517 |
"block_table",
|
| 518 |
{ "arg": "outputT", "name": "output", "elementType": "$aScalar" },
|
| 519 |
+
"params_main"
|
| 520 |
],
|
| 521 |
"dispatch": { "x": "tokenCount", "y": "attrs.kv_num_heads" }
|
| 522 |
}
|
build/webgpu/metadata.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
| 1 |
{
|
| 2 |
"name": "com.microsoft.PagedAttention",
|
| 3 |
-
"id": "
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
-
"attn-flash-decode-splitk-merge.wgsl.jinja": "
|
| 11 |
-
"bench.json": "
|
| 12 |
-
"manifest.json": "
|
| 13 |
-
"paged-attention.wgsl.jinja": "
|
| 14 |
-
"paged-scatter-kv.wgsl.jinja": "
|
| 15 |
-
"test.json": "
|
| 16 |
}
|
| 17 |
},
|
| 18 |
-
"provenance": { "kernel": { "sha": "
|
| 19 |
"webgpu": {
|
| 20 |
-
"manifestSpec": "2.
|
| 21 |
"variants": {
|
| 22 |
"separate_derived_splitk": ["attn-flash-decode-splitk-merge.wgsl.jinja", "paged-attention.wgsl.jinja", "paged-scatter-kv.wgsl.jinja"],
|
| 23 |
"separate_derived": ["paged-attention.wgsl.jinja", "paged-scatter-kv.wgsl.jinja"],
|
|
|
|
| 1 |
{
|
| 2 |
"name": "com.microsoft.PagedAttention",
|
| 3 |
+
"id": "_com_microsoft_pagedattention_webgpu_bcb1627",
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
+
"attn-flash-decode-splitk-merge.wgsl.jinja": "P0yN9VRWOBKM6lMTWKDX7eKuxcmVjj3YX+eRlYgyG0w=",
|
| 11 |
+
"bench.json": "wiVFqyO9vEyYhD/i3BuWEwEMtorPGtDpjrJQSTX+VEU=",
|
| 12 |
+
"manifest.json": "RVVRT3P/Ix1RKhWszk/4atE5P78+OEHtHAkSyDYe1v0=",
|
| 13 |
+
"paged-attention.wgsl.jinja": "Tkqnp3/PxodwRygVuawTRrPNrS2WDP+n/IYU6693Hog=",
|
| 14 |
+
"paged-scatter-kv.wgsl.jinja": "Xq8l6iRmNLXlx510ylhkcIj4AQSbb5jM5M0/A1IOp7Q=",
|
| 15 |
+
"test.json": "zGwuobgxFob47dayYp0kP1EbTZE9CSCKd0pFZjptm40="
|
| 16 |
}
|
| 17 |
},
|
| 18 |
+
"provenance": { "kernel": { "sha": "6fdf6301e2bbcc2f03bf1eaf493b7ad55ef33afc", "dirty": false } },
|
| 19 |
"webgpu": {
|
| 20 |
+
"manifestSpec": "2.1",
|
| 21 |
"variants": {
|
| 22 |
"separate_derived_splitk": ["attn-flash-decode-splitk-merge.wgsl.jinja", "paged-attention.wgsl.jinja", "paged-scatter-kv.wgsl.jinja"],
|
| 23 |
"separate_derived": ["paged-attention.wgsl.jinja", "paged-scatter-kv.wgsl.jinja"],
|
build/webgpu/paged-attention.wgsl.jinja
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
{% set splitK = splitK is defined and splitK %}
|
| 2 |
{% set keyLo = "splitStart" if splitK else "0u" %}
|
| 3 |
{% set keyHi = "splitEnd" if splitK else "key_bound" %}
|
| 4 |
-
enable f16;
|
| 5 |
{{ env.wgsl.resourceDeclarations }}
|
| 6 |
|
| 7 |
// com.microsoft.PagedAttention, attention pass.
|
|
@@ -56,6 +55,7 @@ fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
|
| 56 |
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 57 |
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 58 |
}
|
|
|
|
| 59 |
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 60 |
return exp(shifted_value(value, maxValue));
|
| 61 |
}
|
|
@@ -78,59 +78,6 @@ var<workgroup> key_rows: array<u32, WG>;
|
|
| 78 |
// Both the subgroup and portable barrier-tree engines return the same merged
|
| 79 |
// pair to every invocation. Repeated merges require a workgroup barrier between
|
| 80 |
// calls before their shared partial storage is reused.
|
| 81 |
-
{% set combineSubgroups = combineSubgroups is defined and combineSubgroups %}
|
| 82 |
-
{% if combineSubgroups %}
|
| 83 |
-
// Cross-subgroup merge that assumes nothing about which invocations share a
|
| 84 |
-
// subgroup or how many subgroups there are: each subgroup's elected lane
|
| 85 |
-
// publishes the subgroup pair in the slot at its OWN invocation index and sets
|
| 86 |
-
// that index's bit in a workgroup bitmask; thread 0 then folds exactly the
|
| 87 |
-
// published slots, in ascending index order (the online (m, d) merge is not
|
| 88 |
-
// float-associative, so the order is fixed), and clears the mask for the next
|
| 89 |
-
// call as it reads it. Workgroup memory starts zeroed, so the mask needs no
|
| 90 |
-
// setup. Same three collectives as a single-subgroup reduce, two barriers.
|
| 91 |
-
var<workgroup> partialM: array<f32, WG>;
|
| 92 |
-
var<workgroup> partialD: array<f32, WG>;
|
| 93 |
-
var<workgroup> leaderMask: array<atomic<u32>, (WG + 31u) / 32u>;
|
| 94 |
-
var<workgroup> combinedMD: vec2<f32>;
|
| 95 |
-
|
| 96 |
-
// When the whole workgroup is one subgroup the subgroup reduce already covers
|
| 97 |
-
// it (no barriers, no shared state). `subgroup_size` is the size of the current
|
| 98 |
-
// subgroup and uniform, so the test is exact and may guard the barriers below.
|
| 99 |
-
fn combine_partials(m: f32, d: f32, lidx: u32, sgSize: u32) -> vec2<f32> {
|
| 100 |
-
let sgM = subgroupMax(m);
|
| 101 |
-
// A lane with no elements contributes d == 0 (exact identity). A +inf
|
| 102 |
-
// element made exp(inf - inf) = NaN stick in that lane's d; a NaN element
|
| 103 |
-
// landed in d via exp(NaN); both survive the merge and are detected by the
|
| 104 |
-
// code after the reduction.
|
| 105 |
-
let sgD = subgroupAdd(d * exp_shift(m, sgM));
|
| 106 |
-
if (sgSize == WG) {
|
| 107 |
-
return vec2<f32>(sgM, sgD);
|
| 108 |
-
}
|
| 109 |
-
if (subgroupElect()) {
|
| 110 |
-
partialM[lidx] = sgM;
|
| 111 |
-
partialD[lidx] = sgD;
|
| 112 |
-
atomicOr(&leaderMask[lidx / 32u], 1u << (lidx % 32u));
|
| 113 |
-
}
|
| 114 |
-
workgroupBarrier();
|
| 115 |
-
if (lidx == 0u) {
|
| 116 |
-
var accM = -FLT_MAX;
|
| 117 |
-
var accD = 0.0;
|
| 118 |
-
for (var w = 0u; w < (WG + 31u) / 32u; w = w + 1u) {
|
| 119 |
-
var bits = atomicExchange(&leaderMask[w], 0u);
|
| 120 |
-
while (bits != 0u) {
|
| 121 |
-
let slot = w * 32u + firstTrailingBit(bits);
|
| 122 |
-
bits = bits & (bits - 1u);
|
| 123 |
-
let mNew = max(accM, partialM[slot]);
|
| 124 |
-
accD = accD * exp_shift(accM, mNew) + partialD[slot] * exp_shift(partialM[slot], mNew);
|
| 125 |
-
accM = mNew;
|
| 126 |
-
}
|
| 127 |
-
}
|
| 128 |
-
combinedMD = vec2<f32>(accM, accD);
|
| 129 |
-
}
|
| 130 |
-
workgroupBarrier();
|
| 131 |
-
return combinedMD;
|
| 132 |
-
}
|
| 133 |
-
{% else %}
|
| 134 |
{% set mdStreamed = mdStreams is defined %}
|
| 135 |
{% set mdStreams = mdStreams if mdStreams is defined else 1 %}
|
| 136 |
{% set mdExtent = "WG" if mdStreams == 1 else "WG * " ~ mdStreams ~ "u" %}
|
|
@@ -195,16 +142,12 @@ fn combine_partials(m: f32, d: f32, lidx: u32) -> vec2<f32> {
|
|
| 195 |
return merged;
|
| 196 |
}
|
| 197 |
{% endif %}
|
| 198 |
-
{% endif %}
|
| 199 |
-
|
| 200 |
|
| 201 |
-
{%
|
| 202 |
-
fn scale_value() -> f32 {
|
| 203 |
if (params.scale != 0.0) { return params.scale; }
|
| 204 |
return inverseSqrt(f32({{ ATTN_SCALE_DIM }}));
|
| 205 |
}
|
| 206 |
|
| 207 |
-
|
| 208 |
{% macro varlen_sequence_of_token(buffer="cumulative_sequence_length") %}
|
| 209 |
fn sequence_of_token(token: u32, batch_size: u32) -> u32 {
|
| 210 |
for (var b: u32 = 0u; b < batch_size; b = b + 1u) {
|
|
@@ -213,23 +156,17 @@ fn sequence_of_token(token: u32, batch_size: u32) -> u32 {
|
|
| 213 |
}
|
| 214 |
}
|
| 215 |
return batch_size - 1u;
|
| 216 |
-
}
|
| 217 |
-
{%- endmacro %}
|
| 218 |
-
|
| 219 |
{% macro paged_sequence_index(withLookup=true) %}
|
| 220 |
-
{% if withLookup %}
|
| 221 |
{{ varlen_sequence_of_token() }}
|
| 222 |
-
{% endif %}
|
| 223 |
|
| 224 |
// Physical element base of one (slot, kv head) row of the paged cache, whose
|
| 225 |
// layout is (num_blocks, block_size, kv_num_heads, head_size).
|
| 226 |
fn paged_row_base(block_id: u32, slot_in_block: u32, kv_head: u32) -> u32 {
|
| 227 |
return ((block_id * BLOCK_SIZE + slot_in_block) * KV_HEADS + kv_head) * HEAD_DIM;
|
| 228 |
}
|
| 229 |
-
{%
|
| 230 |
-
|
| 231 |
{{ paged_sequence_index() }}
|
| 232 |
-
|
| 233 |
@compute @workgroup_size(WG, 1, 1)
|
| 234 |
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
| 235 |
let token = wg.x;
|
|
@@ -403,4 +340,4 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid:
|
|
| 403 |
{% endfor %}
|
| 404 |
}
|
| 405 |
}
|
| 406 |
-
{%
|
|
|
|
| 1 |
{% set splitK = splitK is defined and splitK %}
|
| 2 |
{% set keyLo = "splitStart" if splitK else "0u" %}
|
| 3 |
{% set keyHi = "splitEnd" if splitK else "key_bound" %}
|
|
|
|
| 4 |
{{ env.wgsl.resourceDeclarations }}
|
| 5 |
|
| 6 |
// com.microsoft.PagedAttention, attention pass.
|
|
|
|
| 55 |
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 56 |
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 57 |
}
|
| 58 |
+
|
| 59 |
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 60 |
return exp(shifted_value(value, maxValue));
|
| 61 |
}
|
|
|
|
| 78 |
// Both the subgroup and portable barrier-tree engines return the same merged
|
| 79 |
// pair to every invocation. Repeated merges require a workgroup barrier between
|
| 80 |
// calls before their shared partial storage is reused.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
{% set mdStreamed = mdStreams is defined %}
|
| 82 |
{% set mdStreams = mdStreams if mdStreams is defined else 1 %}
|
| 83 |
{% set mdExtent = "WG" if mdStreams == 1 else "WG * " ~ mdStreams ~ "u" %}
|
|
|
|
| 142 |
return merged;
|
| 143 |
}
|
| 144 |
{% endif %}
|
|
|
|
|
|
|
| 145 |
|
| 146 |
+
{% set ATTN_SCALE_DIM = "HEAD_DIM" %}fn scale_value() -> f32 {
|
|
|
|
| 147 |
if (params.scale != 0.0) { return params.scale; }
|
| 148 |
return inverseSqrt(f32({{ ATTN_SCALE_DIM }}));
|
| 149 |
}
|
| 150 |
|
|
|
|
| 151 |
{% macro varlen_sequence_of_token(buffer="cumulative_sequence_length") %}
|
| 152 |
fn sequence_of_token(token: u32, batch_size: u32) -> u32 {
|
| 153 |
for (var b: u32 = 0u; b < batch_size; b = b + 1u) {
|
|
|
|
| 156 |
}
|
| 157 |
}
|
| 158 |
return batch_size - 1u;
|
| 159 |
+
}{% endmacro %}
|
|
|
|
|
|
|
| 160 |
{% macro paged_sequence_index(withLookup=true) %}
|
|
|
|
| 161 |
{{ varlen_sequence_of_token() }}
|
|
|
|
| 162 |
|
| 163 |
// Physical element base of one (slot, kv head) row of the paged cache, whose
|
| 164 |
// layout is (num_blocks, block_size, kv_num_heads, head_size).
|
| 165 |
fn paged_row_base(block_id: u32, slot_in_block: u32, kv_head: u32) -> u32 {
|
| 166 |
return ((block_id * BLOCK_SIZE + slot_in_block) * KV_HEADS + kv_head) * HEAD_DIM;
|
| 167 |
}
|
| 168 |
+
{% endmacro %}
|
|
|
|
| 169 |
{{ paged_sequence_index() }}
|
|
|
|
| 170 |
@compute @workgroup_size(WG, 1, 1)
|
| 171 |
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
| 172 |
let token = wg.x;
|
|
|
|
| 340 |
{% endfor %}
|
| 341 |
}
|
| 342 |
}
|
| 343 |
+
{% endif %}
|
build/webgpu/paged-scatter-kv.wgsl.jinja
CHANGED
|
@@ -1,37 +1,11 @@
|
|
| 1 |
-
{% macro flat_index_2d(name="i", bound="params.count", guardInline=false
|
| 2 |
-
{%
|
| 3 |
-
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 4 |
-
// per-axis workgroup fold width (outputs > 16.7M elements).
|
| 5 |
-
{% elif note == "limit" %}
|
| 6 |
-
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 7 |
-
// per-axis workgroup fold width.
|
| 8 |
-
{% elif note == "device-axis" %}
|
| 9 |
-
// The flat dispatch is folded across x/y at a fixed per-axis workgroup
|
| 10 |
-
// width; gid.y carries the high portion of the output index.
|
| 11 |
-
{% elif note == "vec4-limit" %}
|
| 12 |
-
// 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
|
| 13 |
-
// per-axis workgroup fold width (the dispatch caps x and spills into y).
|
| 14 |
-
{% elif note == "element-limit" %}
|
| 15 |
-
// 2D-folded flat element index: gid.y carries the high bits past the
|
| 16 |
-
// dispatch's per-axis workgroup fold width.
|
| 17 |
-
{% elif note == "dispatch" %}
|
| 18 |
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 19 |
// per-axis workgroup fold width.
|
| 20 |
-
{
|
| 21 |
-
{% if bound == "" %}
|
| 22 |
-
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 23 |
-
{%- elif guardInline %}
|
| 24 |
-
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 25 |
-
if ({{ name }} >= {{ bound }}) { return; }
|
| 26 |
-
{%- else %}
|
| 27 |
-
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 28 |
if ({{ name }} >= {{ bound }}) {
|
| 29 |
return;
|
| 30 |
-
}
|
| 31 |
-
{%- endif %}
|
| 32 |
-
{% endmacro %}
|
| 33 |
-
|
| 34 |
-
enable f16;
|
| 35 |
{{ env.wgsl.resourceDeclarations }}
|
| 36 |
|
| 37 |
// com.microsoft.PagedAttention, KV scatter pass.
|
|
@@ -62,26 +36,22 @@ fn sequence_of_token(token: u32, batch_size: u32) -> u32 {
|
|
| 62 |
}
|
| 63 |
}
|
| 64 |
return batch_size - 1u;
|
| 65 |
-
}
|
| 66 |
-
{%- endmacro %}
|
| 67 |
-
|
| 68 |
{% macro paged_sequence_index(withLookup=true) %}
|
| 69 |
{% if withLookup %}
|
| 70 |
{{ varlen_sequence_of_token() }}
|
| 71 |
-
{% endif %}
|
| 72 |
|
|
|
|
| 73 |
// Physical element base of one (slot, kv head) row of the paged cache, whose
|
| 74 |
// layout is (num_blocks, block_size, kv_num_heads, head_size).
|
| 75 |
fn paged_row_base(block_id: u32, slot_in_block: u32, kv_head: u32) -> u32 {
|
| 76 |
return ((block_id * BLOCK_SIZE + slot_in_block) * KV_HEADS + kv_head) * HEAD_DIM;
|
| 77 |
}
|
| 78 |
-
{%
|
| 79 |
-
|
| 80 |
{{ paged_sequence_index(withLookup=not hasSlotMapping) }}
|
| 81 |
-
|
| 82 |
@compute @workgroup_size({{ tunables.SCATTER_WORKGROUP_SIZE }})
|
| 83 |
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 84 |
-
{{ flat_index_2d(bound="params.scatterCount"
|
| 85 |
let dim = i % HEAD_DIM;
|
| 86 |
let kv_head = (i / HEAD_DIM) % KV_HEADS;
|
| 87 |
let token = i / (KV_HEADS * HEAD_DIM);
|
|
|
|
| 1 |
+
{% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
|
| 2 |
+
{% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 4 |
// per-axis workgroup fold width.
|
| 5 |
+
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
if ({{ name }} >= {{ bound }}) {
|
| 7 |
return;
|
| 8 |
+
}{% endmacro %}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
{{ env.wgsl.resourceDeclarations }}
|
| 10 |
|
| 11 |
// com.microsoft.PagedAttention, KV scatter pass.
|
|
|
|
| 36 |
}
|
| 37 |
}
|
| 38 |
return batch_size - 1u;
|
| 39 |
+
}{% endmacro %}
|
|
|
|
|
|
|
| 40 |
{% macro paged_sequence_index(withLookup=true) %}
|
| 41 |
{% if withLookup %}
|
| 42 |
{{ varlen_sequence_of_token() }}
|
|
|
|
| 43 |
|
| 44 |
+
{% endif %}
|
| 45 |
// Physical element base of one (slot, kv head) row of the paged cache, whose
|
| 46 |
// layout is (num_blocks, block_size, kv_num_heads, head_size).
|
| 47 |
fn paged_row_base(block_id: u32, slot_in_block: u32, kv_head: u32) -> u32 {
|
| 48 |
return ((block_id * BLOCK_SIZE + slot_in_block) * KV_HEADS + kv_head) * HEAD_DIM;
|
| 49 |
}
|
| 50 |
+
{% endmacro %}
|
|
|
|
| 51 |
{{ paged_sequence_index(withLookup=not hasSlotMapping) }}
|
|
|
|
| 52 |
@compute @workgroup_size({{ tunables.SCATTER_WORKGROUP_SIZE }})
|
| 53 |
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 54 |
+
{{ flat_index_2d(tunables.SCATTER_WORKGROUP_SIZE, bound="params.scatterCount") }}
|
| 55 |
let dim = i % HEAD_DIM;
|
| 56 |
let kv_head = (i / HEAD_DIM) % KV_HEADS;
|
| 57 |
let token = i / (KV_HEADS * HEAD_DIM);
|
build/webgpu/test.json
CHANGED
|
@@ -686,7 +686,7 @@
|
|
| 686 |
{
|
| 687 |
"name": "splitk_slot_mapping",
|
| 688 |
"provenance": {
|
| 689 |
-
"notes": "A 201-key history over a 256-key table
|
| 690 |
},
|
| 691 |
"attrs": { "num_heads": 2, "kv_num_heads": 1 },
|
| 692 |
"inputs": {
|
|
@@ -842,6 +842,341 @@
|
|
| 842 |
}
|
| 843 |
},
|
| 844 |
"outputs": { "outputT": { "dtype": "float16", "shape": [1, 2048], "tolerance": 0.00001, "relTolerance": 0.0001 } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 845 |
}
|
| 846 |
]
|
| 847 |
}
|
|
|
|
| 686 |
{
|
| 687 |
"name": "splitk_slot_mapping",
|
| 688 |
"provenance": {
|
| 689 |
+
"notes": "A 201-key history over a 256-key cache table with explicit slot mapping checks attention over multiple key ranges and cache publication."
|
| 690 |
},
|
| 691 |
"attrs": { "num_heads": 2, "kv_num_heads": 1 },
|
| 692 |
"inputs": {
|
|
|
|
| 842 |
}
|
| 843 |
},
|
| 844 |
"outputs": { "outputT": { "dtype": "float16", "shape": [1, 2048], "tolerance": 0.00001, "relTolerance": 0.0001 } }
|
| 845 |
+
},
|
| 846 |
+
{
|
| 847 |
+
"name": "scatter_fold_wg1_independent_sequences",
|
| 848 |
+
"provenance": {
|
| 849 |
+
"notes": "A large scatter workload crosses the WebGPU dispatch-grid limit. One-token sequences bound attention cost; nonzero dyadic cycles expose missing tail writes and misplaced cache/value elements."
|
| 850 |
+
},
|
| 851 |
+
"attrs": { "num_heads": 8, "kv_num_heads": 8 },
|
| 852 |
+
"tunables": { "WORKGROUP_SIZE": 64, "SCATTER_WORKGROUP_SIZE": 1 },
|
| 853 |
+
"inputs": {
|
| 854 |
+
"queryT": { "dtype": "float16", "shape": [64, 1024], "data": { "kind": "constant", "value": 0.0 } },
|
| 855 |
+
"keyT": {
|
| 856 |
+
"dtype": "float16",
|
| 857 |
+
"shape": [64, 1024],
|
| 858 |
+
"data": { "kind": "cycle", "values": [1.0, 2.0, 4.0, -1.0, -2.0] }
|
| 859 |
+
},
|
| 860 |
+
"valueT": {
|
| 861 |
+
"dtype": "float16",
|
| 862 |
+
"shape": [64, 1024],
|
| 863 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] }
|
| 864 |
+
},
|
| 865 |
+
"keyCacheT": { "dtype": "float16", "shape": [64, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 866 |
+
"valueCacheT": { "dtype": "float16", "shape": [64, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 867 |
+
"cumulativeSequenceLengthT": {
|
| 868 |
+
"dtype": "int32",
|
| 869 |
+
"shape": [65],
|
| 870 |
+
"data": { "kind": "linspace", "start": 0, "end": 64 }
|
| 871 |
+
},
|
| 872 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [64], "data": { "kind": "constant", "value": 0 } },
|
| 873 |
+
"blockTableT": { "dtype": "int32", "shape": [64, 1], "data": { "kind": "linspace", "start": 0, "end": 63 } }
|
| 874 |
+
},
|
| 875 |
+
"outputs": {
|
| 876 |
+
"outputT": {
|
| 877 |
+
"dtype": "float16",
|
| 878 |
+
"shape": [64, 1024],
|
| 879 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] },
|
| 880 |
+
"tolerance": 0,
|
| 881 |
+
"relTolerance": 0
|
| 882 |
+
},
|
| 883 |
+
"keyCacheT": {
|
| 884 |
+
"dtype": "float16",
|
| 885 |
+
"shape": [64, 1, 8, 128],
|
| 886 |
+
"data": { "kind": "cycle", "values": [1.0, 2.0, 4.0, -1.0, -2.0] },
|
| 887 |
+
"tolerance": 0,
|
| 888 |
+
"relTolerance": 0
|
| 889 |
+
},
|
| 890 |
+
"valueCacheT": {
|
| 891 |
+
"dtype": "float16",
|
| 892 |
+
"shape": [64, 1, 8, 128],
|
| 893 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] },
|
| 894 |
+
"tolerance": 0,
|
| 895 |
+
"relTolerance": 0
|
| 896 |
+
}
|
| 897 |
+
}
|
| 898 |
+
},
|
| 899 |
+
{
|
| 900 |
+
"name": "scatter_fold_wg4_independent_sequences",
|
| 901 |
+
"provenance": {
|
| 902 |
+
"notes": "A large scatter workload crosses the WebGPU dispatch-grid limit. One-token sequences bound attention cost; nonzero dyadic cycles expose missing tail writes and misplaced cache/value elements."
|
| 903 |
+
},
|
| 904 |
+
"attrs": { "num_heads": 8, "kv_num_heads": 8 },
|
| 905 |
+
"tunables": { "WORKGROUP_SIZE": 64, "SCATTER_WORKGROUP_SIZE": 4 },
|
| 906 |
+
"inputs": {
|
| 907 |
+
"queryT": { "dtype": "float16", "shape": [256, 1024], "data": { "kind": "constant", "value": 0.0 } },
|
| 908 |
+
"keyT": {
|
| 909 |
+
"dtype": "float16",
|
| 910 |
+
"shape": [256, 1024],
|
| 911 |
+
"data": { "kind": "cycle", "values": [1.0, 2.0, 4.0, -1.0, -2.0] }
|
| 912 |
+
},
|
| 913 |
+
"valueT": {
|
| 914 |
+
"dtype": "float16",
|
| 915 |
+
"shape": [256, 1024],
|
| 916 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] }
|
| 917 |
+
},
|
| 918 |
+
"keyCacheT": { "dtype": "float16", "shape": [256, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 919 |
+
"valueCacheT": { "dtype": "float16", "shape": [256, 1, 8, 128], "data": { "kind": "constant", "value": 0.0 } },
|
| 920 |
+
"cumulativeSequenceLengthT": {
|
| 921 |
+
"dtype": "int32",
|
| 922 |
+
"shape": [257],
|
| 923 |
+
"data": { "kind": "linspace", "start": 0, "end": 256 }
|
| 924 |
+
},
|
| 925 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [256], "data": { "kind": "constant", "value": 0 } },
|
| 926 |
+
"blockTableT": { "dtype": "int32", "shape": [256, 1], "data": { "kind": "linspace", "start": 0, "end": 255 } }
|
| 927 |
+
},
|
| 928 |
+
"outputs": {
|
| 929 |
+
"outputT": {
|
| 930 |
+
"dtype": "float16",
|
| 931 |
+
"shape": [256, 1024],
|
| 932 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] },
|
| 933 |
+
"tolerance": 0,
|
| 934 |
+
"relTolerance": 0
|
| 935 |
+
},
|
| 936 |
+
"keyCacheT": {
|
| 937 |
+
"dtype": "float16",
|
| 938 |
+
"shape": [256, 1, 8, 128],
|
| 939 |
+
"data": { "kind": "cycle", "values": [1.0, 2.0, 4.0, -1.0, -2.0] },
|
| 940 |
+
"tolerance": 0,
|
| 941 |
+
"relTolerance": 0
|
| 942 |
+
},
|
| 943 |
+
"valueCacheT": {
|
| 944 |
+
"dtype": "float16",
|
| 945 |
+
"shape": [256, 1, 8, 128],
|
| 946 |
+
"data": { "kind": "cycle", "values": [2.0, -4.0, 8.0, 1.0, -2.0, 4.0, -1.0] },
|
| 947 |
+
"tolerance": 0,
|
| 948 |
+
"relTolerance": 0
|
| 949 |
+
}
|
| 950 |
+
}
|
| 951 |
+
},
|
| 952 |
+
{
|
| 953 |
+
"name": "ort_paged_ragged_empty_request_b2_h8kv4_d128",
|
| 954 |
+
"attrs": { "num_heads": 8, "kv_num_heads": 4 },
|
| 955 |
+
"inputs": {
|
| 956 |
+
"queryT": {
|
| 957 |
+
"dtype": "float16",
|
| 958 |
+
"shape": [2, 1024],
|
| 959 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.029, "scale": 0.5 }
|
| 960 |
+
},
|
| 961 |
+
"keyT": {
|
| 962 |
+
"dtype": "float16",
|
| 963 |
+
"shape": [2, 512],
|
| 964 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.031, "scale": 0.5 }
|
| 965 |
+
},
|
| 966 |
+
"valueT": {
|
| 967 |
+
"dtype": "float16",
|
| 968 |
+
"shape": [2, 512],
|
| 969 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.037, "scale": 0.5 }
|
| 970 |
+
},
|
| 971 |
+
"keyCacheT": {
|
| 972 |
+
"dtype": "float16",
|
| 973 |
+
"shape": [4, 8, 4, 128],
|
| 974 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.019, "scale": 0.25 }
|
| 975 |
+
},
|
| 976 |
+
"valueCacheT": {
|
| 977 |
+
"dtype": "float16",
|
| 978 |
+
"shape": [4, 8, 4, 128],
|
| 979 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.007, "cosStep": 0.041, "scale": 0.25 }
|
| 980 |
+
},
|
| 981 |
+
"cumulativeSequenceLengthT": {
|
| 982 |
+
"dtype": "int32",
|
| 983 |
+
"shape": [3],
|
| 984 |
+
"data": { "kind": "values", "values": [0, 2, 2] }
|
| 985 |
+
},
|
| 986 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [0, 0] } },
|
| 987 |
+
"blockTableT": { "dtype": "int32", "shape": [2, 2], "data": { "kind": "values", "values": [2, 0, 3, 1] } }
|
| 988 |
+
},
|
| 989 |
+
"outputs": { "outputT": { "dtype": "float16", "shape": [2, 1024], "tolerance": 0.005 } }
|
| 990 |
+
},
|
| 991 |
+
{
|
| 992 |
+
"name": "ort_paged_ragged_new_seqlens_1_2_b2_h8kv4_d128",
|
| 993 |
+
"attrs": { "num_heads": 8, "kv_num_heads": 4 },
|
| 994 |
+
"inputs": {
|
| 995 |
+
"queryT": {
|
| 996 |
+
"dtype": "float16",
|
| 997 |
+
"shape": [3, 1024],
|
| 998 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.027, "scale": 0.5 }
|
| 999 |
+
},
|
| 1000 |
+
"keyT": {
|
| 1001 |
+
"dtype": "float16",
|
| 1002 |
+
"shape": [3, 512],
|
| 1003 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.021, "cosStep": 0.033, "scale": 0.5 }
|
| 1004 |
+
},
|
| 1005 |
+
"valueT": {
|
| 1006 |
+
"dtype": "float16",
|
| 1007 |
+
"shape": [3, 512],
|
| 1008 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.015, "cosStep": 0.039, "scale": 0.5 }
|
| 1009 |
+
},
|
| 1010 |
+
"keyCacheT": {
|
| 1011 |
+
"dtype": "float16",
|
| 1012 |
+
"shape": [4, 8, 4, 128],
|
| 1013 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.019, "scale": 0.25 }
|
| 1014 |
+
},
|
| 1015 |
+
"valueCacheT": {
|
| 1016 |
+
"dtype": "float16",
|
| 1017 |
+
"shape": [4, 8, 4, 128],
|
| 1018 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.007, "cosStep": 0.041, "scale": 0.25 }
|
| 1019 |
+
},
|
| 1020 |
+
"cumulativeSequenceLengthT": {
|
| 1021 |
+
"dtype": "int32",
|
| 1022 |
+
"shape": [3],
|
| 1023 |
+
"data": { "kind": "values", "values": [0, 1, 3] }
|
| 1024 |
+
},
|
| 1025 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [0, 0] } },
|
| 1026 |
+
"blockTableT": { "dtype": "int32", "shape": [2, 2], "data": { "kind": "values", "values": [2, 0, 3, 1] } }
|
| 1027 |
+
},
|
| 1028 |
+
"outputs": { "outputT": { "dtype": "float16", "shape": [3, 1024], "tolerance": 0.005 } }
|
| 1029 |
+
},
|
| 1030 |
+
{
|
| 1031 |
+
"name": "ort_paged_prefill_multibatch_varlen_b2t48_h2kv2d128",
|
| 1032 |
+
"attrs": { "num_heads": 2, "kv_num_heads": 2 },
|
| 1033 |
+
"inputs": {
|
| 1034 |
+
"queryT": {
|
| 1035 |
+
"dtype": "float16",
|
| 1036 |
+
"shape": [48, 128],
|
| 1037 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.023, "scale": 0.4 }
|
| 1038 |
+
},
|
| 1039 |
+
"keyT": {
|
| 1040 |
+
"dtype": "float16",
|
| 1041 |
+
"shape": [48, 128],
|
| 1042 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.029, "scale": 0.4 }
|
| 1043 |
+
},
|
| 1044 |
+
"valueT": {
|
| 1045 |
+
"dtype": "float16",
|
| 1046 |
+
"shape": [48, 128],
|
| 1047 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.031, "scale": 0.4 }
|
| 1048 |
+
},
|
| 1049 |
+
"keyCacheT": {
|
| 1050 |
+
"dtype": "float16",
|
| 1051 |
+
"shape": [6, 16, 2, 64],
|
| 1052 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.007, "cosStep": 0.037, "scale": 0.2 }
|
| 1053 |
+
},
|
| 1054 |
+
"valueCacheT": {
|
| 1055 |
+
"dtype": "float16",
|
| 1056 |
+
"shape": [6, 16, 2, 64],
|
| 1057 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.041, "scale": 0.2 }
|
| 1058 |
+
},
|
| 1059 |
+
"cumulativeSequenceLengthT": {
|
| 1060 |
+
"dtype": "int32",
|
| 1061 |
+
"shape": [3],
|
| 1062 |
+
"data": { "kind": "values", "values": [0, 32, 48] }
|
| 1063 |
+
},
|
| 1064 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [0, 0] } },
|
| 1065 |
+
"blockTableT": { "dtype": "int32", "shape": [2, 2], "data": { "kind": "values", "values": [4, 1, 5, 0] } }
|
| 1066 |
+
},
|
| 1067 |
+
"outputs": { "outputT": { "dtype": "float16", "shape": [48, 128], "tolerance": 0.005 } }
|
| 1068 |
+
},
|
| 1069 |
+
{
|
| 1070 |
+
"name": "ort_paged_empty_tokens_copy_caches",
|
| 1071 |
+
"attrs": { "num_heads": 1, "kv_num_heads": 1 },
|
| 1072 |
+
"inputs": {
|
| 1073 |
+
"queryT": { "dtype": "float16", "shape": [0, 8], "data": { "kind": "values", "values": [] } },
|
| 1074 |
+
"keyT": { "dtype": "float16", "shape": [0, 8], "data": { "kind": "values", "values": [] } },
|
| 1075 |
+
"valueT": { "dtype": "float16", "shape": [0, 8], "data": { "kind": "values", "values": [] } },
|
| 1076 |
+
"keyCacheT": {
|
| 1077 |
+
"dtype": "float16",
|
| 1078 |
+
"shape": [2, 4, 1, 8],
|
| 1079 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.13, "scale": 0.5 }
|
| 1080 |
+
},
|
| 1081 |
+
"valueCacheT": {
|
| 1082 |
+
"dtype": "float16",
|
| 1083 |
+
"shape": [2, 4, 1, 8],
|
| 1084 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.17, "scale": 0.5 }
|
| 1085 |
+
},
|
| 1086 |
+
"cumulativeSequenceLengthT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [0, 0] } },
|
| 1087 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [0] } },
|
| 1088 |
+
"blockTableT": { "dtype": "int32", "shape": [1, 1], "data": { "kind": "values", "values": [0] } }
|
| 1089 |
+
},
|
| 1090 |
+
"outputs": {
|
| 1091 |
+
"outputT": { "dtype": "float16", "shape": [0, 8], "data": { "kind": "values", "values": [] } },
|
| 1092 |
+
"keyCacheT": { "dtype": "float16", "shape": [2, 4, 1, 8], "tolerance": 0.001 },
|
| 1093 |
+
"valueCacheT": { "dtype": "float16", "shape": [2, 4, 1, 8], "tolerance": 0.001 }
|
| 1094 |
+
}
|
| 1095 |
+
},
|
| 1096 |
+
{
|
| 1097 |
+
"name": "ort_paged_mixed_prefill_decode_b2t5_past2_4_h2kv1d8",
|
| 1098 |
+
"attrs": { "num_heads": 2, "kv_num_heads": 1 },
|
| 1099 |
+
"inputs": {
|
| 1100 |
+
"queryT": {
|
| 1101 |
+
"dtype": "float16",
|
| 1102 |
+
"shape": [5, 16],
|
| 1103 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29, "scale": 0.5 }
|
| 1104 |
+
},
|
| 1105 |
+
"keyT": {
|
| 1106 |
+
"dtype": "float16",
|
| 1107 |
+
"shape": [5, 8],
|
| 1108 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.23, "scale": 0.5 }
|
| 1109 |
+
},
|
| 1110 |
+
"valueT": {
|
| 1111 |
+
"dtype": "float16",
|
| 1112 |
+
"shape": [5, 8],
|
| 1113 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.37, "scale": 0.5 }
|
| 1114 |
+
},
|
| 1115 |
+
"keyCacheT": {
|
| 1116 |
+
"dtype": "float16",
|
| 1117 |
+
"shape": [4, 4, 1, 8],
|
| 1118 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.43, "scale": 0.25 }
|
| 1119 |
+
},
|
| 1120 |
+
"valueCacheT": {
|
| 1121 |
+
"dtype": "float16",
|
| 1122 |
+
"shape": [4, 4, 1, 8],
|
| 1123 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.05, "cosStep": 0.47, "scale": 0.25 }
|
| 1124 |
+
},
|
| 1125 |
+
"cumulativeSequenceLengthT": {
|
| 1126 |
+
"dtype": "int32",
|
| 1127 |
+
"shape": [3],
|
| 1128 |
+
"data": { "kind": "values", "values": [0, 3, 5] }
|
| 1129 |
+
},
|
| 1130 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [2, 4] } },
|
| 1131 |
+
"blockTableT": { "dtype": "int32", "shape": [2, 2], "data": { "kind": "values", "values": [3, 1, 0, 2] } }
|
| 1132 |
+
},
|
| 1133 |
+
"outputs": { "outputT": { "dtype": "float16", "shape": [5, 16], "tolerance": 0.005 } }
|
| 1134 |
+
},
|
| 1135 |
+
{
|
| 1136 |
+
"name": "ort_paged_splitk_decode_ragged_batch_b2_past200_400_h2kv1d64",
|
| 1137 |
+
"attrs": { "num_heads": 2, "kv_num_heads": 1 },
|
| 1138 |
+
"inputs": {
|
| 1139 |
+
"queryT": {
|
| 1140 |
+
"dtype": "float16",
|
| 1141 |
+
"shape": [2, 128],
|
| 1142 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.023, "scale": 0.4 }
|
| 1143 |
+
},
|
| 1144 |
+
"keyT": {
|
| 1145 |
+
"dtype": "float16",
|
| 1146 |
+
"shape": [2, 64],
|
| 1147 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.029, "scale": 0.4 }
|
| 1148 |
+
},
|
| 1149 |
+
"valueT": {
|
| 1150 |
+
"dtype": "float16",
|
| 1151 |
+
"shape": [2, 64],
|
| 1152 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.031, "scale": 0.4 }
|
| 1153 |
+
},
|
| 1154 |
+
"keyCacheT": {
|
| 1155 |
+
"dtype": "float16",
|
| 1156 |
+
"shape": [32, 32, 1, 64],
|
| 1157 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.003, "cosStep": 0.047, "scale": 0.2 }
|
| 1158 |
+
},
|
| 1159 |
+
"valueCacheT": {
|
| 1160 |
+
"dtype": "float16",
|
| 1161 |
+
"shape": [32, 32, 1, 64],
|
| 1162 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.005, "cosStep": 0.053, "scale": 0.2 }
|
| 1163 |
+
},
|
| 1164 |
+
"cumulativeSequenceLengthT": {
|
| 1165 |
+
"dtype": "int32",
|
| 1166 |
+
"shape": [3],
|
| 1167 |
+
"data": { "kind": "values", "values": [0, 1, 2] }
|
| 1168 |
+
},
|
| 1169 |
+
"pastSeqlensT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [200, 400] } },
|
| 1170 |
+
"blockTableT": {
|
| 1171 |
+
"dtype": "int32",
|
| 1172 |
+
"shape": [2, 16],
|
| 1173 |
+
"data": {
|
| 1174 |
+
"kind": "values",
|
| 1175 |
+
"values": [3, 10, 17, 24, 31, 6, 13, 20, 27, 2, 9, 16, 23, 30, 5, 12, 1, 8, 15, 22, 29, 4, 11, 18, 25, 0, 7, 14, 21, 28, 19, 26]
|
| 1176 |
+
}
|
| 1177 |
+
}
|
| 1178 |
+
},
|
| 1179 |
+
"outputs": { "outputT": { "dtype": "float16", "shape": [2, 128], "tolerance": 0.00043 } }
|
| 1180 |
}
|
| 1181 |
]
|
| 1182 |
}
|