sync c928d21e6cc1
Browse files
build/webgpu/manifest.json
CHANGED
|
@@ -640,7 +640,11 @@
|
|
| 640 |
},
|
| 641 |
"derive": {
|
| 642 |
"deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
|
|
|
|
| 643 |
"narrowSubgroupRange": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize < device.adapterInfo.subgroupMaxSize and device.adapterInfo.subgroupMaxSize <= 16",
|
|
|
|
|
|
|
|
|
|
| 644 |
"packedFeature": "device.wgslLanguageFeatures.has(\"packed_4x8_integer_dot_product\")",
|
| 645 |
"kBlocksExpected": "ceilDiv(attrs.K, attrs.block_size)",
|
| 646 |
"blobSizeExpected": "ceilDiv(attrs.block_size * attrs.bits, 8)",
|
|
@@ -760,10 +764,9 @@
|
|
| 760 |
"requires": {
|
| 761 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 762 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
| 763 |
-
"subgroupMinSize": 32,
|
| 764 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 765 |
},
|
| 766 |
-
"when": ["commonShapeValid", "defaultEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "
|
| 767 |
"constants": {
|
| 768 |
"hasZero": false,
|
| 769 |
"hasBias": false,
|
|
@@ -947,10 +950,9 @@
|
|
| 947 |
"requires": {
|
| 948 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 949 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
| 950 |
-
"subgroupMinSize": 32,
|
| 951 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 952 |
},
|
| 953 |
-
"when": ["commonShapeValid", "zeroBiasEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "
|
| 954 |
"constants": {
|
| 955 |
"hasZero": true,
|
| 956 |
"hasBias": true,
|
|
@@ -1134,10 +1136,9 @@
|
|
| 1134 |
"requires": {
|
| 1135 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 1136 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
| 1137 |
-
"subgroupMinSize": 32,
|
| 1138 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 1139 |
},
|
| 1140 |
-
"when": ["commonShapeValid", "zeroOnlyEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "
|
| 1141 |
"constants": {
|
| 1142 |
"hasZero": true,
|
| 1143 |
"hasBias": false,
|
|
@@ -1321,10 +1322,9 @@
|
|
| 1321 |
"requires": {
|
| 1322 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 1323 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
| 1324 |
-
"subgroupMinSize": 32,
|
| 1325 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 1326 |
},
|
| 1327 |
-
"when": ["commonShapeValid", "biasOnlyEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "
|
| 1328 |
"constants": {
|
| 1329 |
"hasZero": false,
|
| 1330 |
"hasBias": true,
|
|
|
|
| 640 |
},
|
| 641 |
"derive": {
|
| 642 |
"deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
|
| 643 |
+
"wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
|
| 644 |
"narrowSubgroupRange": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize < device.adapterInfo.subgroupMaxSize and device.adapterInfo.subgroupMaxSize <= 16",
|
| 645 |
+
"canPinSubgroupSize32": "device.features.has(\"subgroups\") and device.features.has(\"subgroup-size-control\") and has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize <= 32 and device.adapterInfo.subgroupMaxSize >= 32",
|
| 646 |
+
"pinSubgroupSize32": "canPinSubgroupSize32 and not wave32Adapter",
|
| 647 |
+
"wave32Effective": "wave32Adapter or pinSubgroupSize32",
|
| 648 |
"packedFeature": "device.wgslLanguageFeatures.has(\"packed_4x8_integer_dot_product\")",
|
| 649 |
"kBlocksExpected": "ceilDiv(attrs.K, attrs.block_size)",
|
| 650 |
"blobSizeExpected": "ceilDiv(attrs.block_size * attrs.bits, 8)",
|
|
|
|
| 764 |
"requires": {
|
| 765 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 766 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
|
|
|
| 767 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 768 |
},
|
| 769 |
+
"when": ["commonShapeValid", "defaultEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "wave32Effective", "dispatchN64 <= device.limits.maxComputeWorkgroupsPerDimension", "sgmatDispatchM <= device.limits.maxComputeWorkgroupsPerDimension", "portableWorkgroupFits", "sgmatWorkgroupFits"],
|
| 770 |
"constants": {
|
| 771 |
"hasZero": false,
|
| 772 |
"hasBias": false,
|
|
|
|
| 950 |
"requires": {
|
| 951 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 952 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
|
|
|
| 953 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 954 |
},
|
| 955 |
+
"when": ["commonShapeValid", "zeroBiasEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "wave32Effective", "dispatchN64 <= device.limits.maxComputeWorkgroupsPerDimension", "sgmatDispatchM <= device.limits.maxComputeWorkgroupsPerDimension", "portableWorkgroupFits", "sgmatWorkgroupFits"],
|
| 956 |
"constants": {
|
| 957 |
"hasZero": true,
|
| 958 |
"hasBias": true,
|
|
|
|
| 1136 |
"requires": {
|
| 1137 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 1138 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
|
|
|
| 1139 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 1140 |
},
|
| 1141 |
+
"when": ["commonShapeValid", "zeroOnlyEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "wave32Effective", "dispatchN64 <= device.limits.maxComputeWorkgroupsPerDimension", "sgmatDispatchM <= device.limits.maxComputeWorkgroupsPerDimension", "portableWorkgroupFits", "sgmatWorkgroupFits"],
|
| 1142 |
"constants": {
|
| 1143 |
"hasZero": true,
|
| 1144 |
"hasBias": false,
|
|
|
|
| 1322 |
"requires": {
|
| 1323 |
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 1324 |
"limits": { "maxComputeWorkgroupStorageSize": 16384 },
|
|
|
|
| 1325 |
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 1326 |
},
|
| 1327 |
+
"when": ["commonShapeValid", "biasOnlyEpilogue", "bitsSupported", "attrs.K % attrs.block_size == 0", "attrs.block_size % 16 == 0", "attrs.K % 32 == 0", "aRows >= 32", "wave32Effective", "dispatchN64 <= device.limits.maxComputeWorkgroupsPerDimension", "sgmatDispatchM <= device.limits.maxComputeWorkgroupsPerDimension", "portableWorkgroupFits", "sgmatWorkgroupFits"],
|
| 1328 |
"constants": {
|
| 1329 |
"hasZero": false,
|
| 1330 |
"hasBias": true,
|
build/webgpu/matmul-nbits-gemv-q4.wgsl.jinja
CHANGED
|
@@ -8,9 +8,12 @@ enable f16;
|
|
| 8 |
{% set nCols = gemvNCols %}
|
| 9 |
{% set groups = 2 if nCols == 8 else 1 %}
|
| 10 |
{% set comps = ["x", "y", "z", "w"] %}
|
|
|
|
|
|
|
| 11 |
|
| 12 |
const WG: u32 = {{ workgroupSize }}u;
|
| 13 |
const N_COLS: u32 = {{ nCols }}u;
|
|
|
|
| 14 |
|
| 15 |
{% for g in range(groups) %}
|
| 16 |
{% set sfx = "" if groups == 1 else g %}
|
|
@@ -18,25 +21,11 @@ var<workgroup> partials{{ sfx }}: array<vec4<f32>, {{ workgroupSize }}>;
|
|
| 18 |
{% endfor %}
|
| 19 |
|
| 20 |
// {{ bits }}-bit unpack: q2 packs four codes per byte, q4 two nibbles, q8 one byte/weight.
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
let shift = (offset % 4u) * 2u;
|
| 26 |
-
let packed_index = (n * {{ kBlocks }} + block) * {{ blobSize }} + byte_index;
|
| 27 |
-
return ({{ buffer }}[packed_index] >> shift) & 3u;
|
| 28 |
-
{% elif bits == 4 %}
|
| 29 |
-
let byte_index = offset / 2u;
|
| 30 |
-
let shift = (offset % 2u) * 4u;
|
| 31 |
-
let packed_index = (n * {{ kBlocks }} + block) * {{ blobSize }} + byte_index;
|
| 32 |
-
return ({{ buffer }}[packed_index] >> shift) & 15u;
|
| 33 |
-
{% else %}
|
| 34 |
-
let packed_index = (n * {{ kBlocks }} + block) * {{ blobSize }} + offset;
|
| 35 |
-
return {{ buffer }}[packed_index] & 255u;
|
| 36 |
-
{% endif %}
|
| 37 |
}
|
| 38 |
-
{%- endmacro %}
|
| 39 |
-
{{- matmul_nbits_packed_code(bits=bits) }}
|
| 40 |
|
| 41 |
// Per-(column, block) zero point. Symmetric weights use the baked default
|
| 42 |
// ({{ defaultZero }}); asymmetric weights carry one widened u32 per (col, block)
|
|
@@ -68,23 +57,41 @@ fn main(
|
|
| 68 |
var acc{{ sfx }} = vec4<f32>(0.0);
|
| 69 |
{% endfor %}
|
| 70 |
|
| 71 |
-
|
|
|
|
| 72 |
let block = k / params.blockSize;
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
{% for g in range(groups) %}
|
| 76 |
{% set sfx = "" if groups == 1 else g %}
|
| 77 |
{% for c in range(4) %}
|
| 78 |
{% set i = g * 4 + c %}
|
| 79 |
{% if i == 0 %}
|
| 80 |
let scale0 = f32(scales[(col_base + 0u) * params.kBlocks + block]);
|
| 81 |
-
let
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
{% else %}
|
| 84 |
if (col_base + {{ i }}u < params.N) {
|
| 85 |
let scale{{ i }} = f32(scales[(col_base + {{ i }}u) * params.kBlocks + block]);
|
| 86 |
-
let
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
}
|
| 89 |
{% endif %}
|
| 90 |
{% endfor %}
|
|
|
|
| 8 |
{% set nCols = gemvNCols %}
|
| 9 |
{% set groups = 2 if nCols == 8 else 1 %}
|
| 10 |
{% set comps = ["x", "y", "z", "w"] %}
|
| 11 |
+
{% set codesPerByte = 4 if bits == 2 else (2 if bits == 4 else 1) %}
|
| 12 |
+
{% set codeMask = 3 if bits == 2 else (15 if bits == 4 else 255) %}
|
| 13 |
|
| 14 |
const WG: u32 = {{ workgroupSize }}u;
|
| 15 |
const N_COLS: u32 = {{ nCols }}u;
|
| 16 |
+
const CODES: u32 = {{ codesPerByte }}u;
|
| 17 |
|
| 18 |
{% for g in range(groups) %}
|
| 19 |
{% set sfx = "" if groups == 1 else g %}
|
|
|
|
| 21 |
{% endfor %}
|
| 22 |
|
| 23 |
// {{ bits }}-bit unpack: q2 packs four codes per byte, q4 two nibbles, q8 one byte/weight.
|
| 24 |
+
// The whole stored byte, addressed by its index within the block rather than by
|
| 25 |
+
// one of the codes inside it.
|
| 26 |
+
fn packed_byte(n: u32, block: u32, byte_index: u32) -> u32 {
|
| 27 |
+
return b[(n * params.kBlocks + block) * params.blobSize + byte_index];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
| 29 |
|
| 30 |
// Per-(column, block) zero point. Symmetric weights use the baked default
|
| 31 |
// ({{ defaultZero }}); asymmetric weights carry one widened u32 per (col, block)
|
|
|
|
| 57 |
var acc{{ sfx }} = vec4<f32>(0.0);
|
| 58 |
{% endfor %}
|
| 59 |
|
| 60 |
+
// Stride whole bytes: every code a byte holds is consumed from one fetch.
|
| 61 |
+
for (var k = tid * CODES; k < params.K; k = k + WG * CODES) {
|
| 62 |
let block = k / params.blockSize;
|
| 63 |
+
// blockSize is a power of two of at least 16, so a byte never straddles two
|
| 64 |
+
// blocks and every code below shares this block's scale and zero point.
|
| 65 |
+
let byte_index = (k % params.blockSize) / CODES;
|
| 66 |
+
{% for j in range(codesPerByte) %}
|
| 67 |
+
{% if j == 0 %}
|
| 68 |
+
let av0 = f32(a[k]);
|
| 69 |
+
{% else %}
|
| 70 |
+
// K need not be a multiple of CODES; a code past the end contributes zero.
|
| 71 |
+
let av{{ j }} = select(0.0, f32(a[min(k + {{ j }}u, params.K - 1u)]), k + {{ j }}u < params.K);
|
| 72 |
+
{% endif %}
|
| 73 |
+
{% endfor %}
|
| 74 |
{% for g in range(groups) %}
|
| 75 |
{% set sfx = "" if groups == 1 else g %}
|
| 76 |
{% for c in range(4) %}
|
| 77 |
{% set i = g * 4 + c %}
|
| 78 |
{% if i == 0 %}
|
| 79 |
let scale0 = f32(scales[(col_base + 0u) * params.kBlocks + block]);
|
| 80 |
+
let zero0 = zero_point({% if hasZero %}col_base + 0u, block{% endif %});
|
| 81 |
+
let word0 = packed_byte(col_base + 0u, block, byte_index);
|
| 82 |
+
{% for j in range(codesPerByte) %}
|
| 83 |
+
acc{{ sfx }}.x = acc{{ sfx }}.x
|
| 84 |
+
+ av{{ j }} * (f32((word0 >> {{ j * bits }}u) & {{ codeMask }}u) - zero0) * scale0;
|
| 85 |
+
{% endfor %}
|
| 86 |
{% else %}
|
| 87 |
if (col_base + {{ i }}u < params.N) {
|
| 88 |
let scale{{ i }} = f32(scales[(col_base + {{ i }}u) * params.kBlocks + block]);
|
| 89 |
+
let zero{{ i }} = zero_point({% if hasZero %}col_base + {{ i }}u, block{% endif %});
|
| 90 |
+
let word{{ i }} = packed_byte(col_base + {{ i }}u, block, byte_index);
|
| 91 |
+
{% for j in range(codesPerByte) %}
|
| 92 |
+
acc{{ sfx }}.{{ comps[c] }} = acc{{ sfx }}.{{ comps[c] }}
|
| 93 |
+
+ av{{ j }} * (f32((word{{ i }} >> {{ j * bits }}u) & {{ codeMask }}u) - zero{{ i }}) * scale{{ i }};
|
| 94 |
+
{% endfor %}
|
| 95 |
}
|
| 96 |
{% endif %}
|
| 97 |
{% endfor %}
|
build/webgpu/matmul-nbits-q4-sgmat.wgsl.jinja
CHANGED
|
@@ -8,6 +8,9 @@
|
|
| 8 |
// Alignment gates keep K % 32 == 0 and N % 64 == 0 so subgroupMatrixLoad never
|
| 9 |
// sees partial 8x8 tiles; M tails handled by row_limit + the loadSHMA zero-fill.
|
| 10 |
enable subgroups;
|
|
|
|
|
|
|
|
|
|
| 11 |
enable chromium_experimental_subgroup_matrix;
|
| 12 |
{% if usesF16 %}
|
| 13 |
enable f16;
|
|
@@ -118,7 +121,7 @@ fn storeOutput(offset: u32, col_base: u32, row: u32, col: u32, src_slot: u32, ro
|
|
| 118 |
}
|
| 119 |
}
|
| 120 |
|
| 121 |
-
@compute @workgroup_size({{ workgroupSize }}, 1, 1)
|
| 122 |
fn main(
|
| 123 |
@builtin(workgroup_id) workgroup_id: vec3<u32>,
|
| 124 |
@builtin(local_invocation_index) local_idx: u32,
|
|
@@ -145,14 +148,14 @@ fn main(
|
|
| 145 |
|
| 146 |
for (var step = 0u; step < TILE_K; step = step + 8u) {
|
| 147 |
{% set operandScalar = "f32" %} let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + step;
|
| 148 |
-
var matA0: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>>(&tile_A, matrix_a_offset,
|
| 149 |
-
var matA1: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>>(&tile_A, matrix_a_offset + 8u * TILE_K,
|
| 150 |
|
| 151 |
let matrix_b_offset = subtile_idx * SUB_COLS * TILE_K + step;
|
| 152 |
-
var matB0: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>>(&tile_B, matrix_b_offset,
|
| 153 |
-
var matB1: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>>(&tile_B, matrix_b_offset + 8u * TILE_K,
|
| 154 |
-
var matB2: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>>(&tile_B, matrix_b_offset + 16u * TILE_K,
|
| 155 |
-
var matB3: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>>(&tile_B, matrix_b_offset + 24u * TILE_K,
|
| 156 |
|
| 157 |
matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
|
| 158 |
matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
|
|
@@ -172,7 +175,7 @@ fn main(
|
|
| 172 |
// clobbering the first's still-in-flight readback when a partial final M-tile
|
| 173 |
// diverges storeOutput's guard. Without both barriers the last valid row can be corrupted.
|
| 174 |
{% for col in [0, 1, 2, 3] %}
|
| 175 |
-
subgroupMatrixStore(&scratch[subtile_id][{{ col }}], 0u, matC0{{ col }},
|
| 176 |
{% endfor %}
|
| 177 |
workgroupBarrier();
|
| 178 |
let row = sg_id / 4u;
|
|
@@ -184,7 +187,7 @@ fn main(
|
|
| 184 |
workgroupBarrier();
|
| 185 |
|
| 186 |
{% for col in [0, 1, 2, 3] %}
|
| 187 |
-
subgroupMatrixStore(&scratch[subtile_id][{{ col }}], 0u, matC1{{ col }},
|
| 188 |
{% endfor %}
|
| 189 |
workgroupBarrier();
|
| 190 |
matrix_c_offset = matrix_c_offset + 8u * N;
|
|
|
|
| 8 |
// Alignment gates keep K % 32 == 0 and N % 64 == 0 so subgroupMatrixLoad never
|
| 9 |
// sees partial 8x8 tiles; M tails handled by row_limit + the loadSHMA zero-fill.
|
| 10 |
enable subgroups;
|
| 11 |
+
{% if pinSubgroupSize32 %}
|
| 12 |
+
enable subgroup_size_control;
|
| 13 |
+
{% endif %}
|
| 14 |
enable chromium_experimental_subgroup_matrix;
|
| 15 |
{% if usesF16 %}
|
| 16 |
enable f16;
|
|
|
|
| 121 |
}
|
| 122 |
}
|
| 123 |
|
| 124 |
+
@compute @workgroup_size({{ workgroupSize }}, 1, 1){{ " @subgroup_size(32)" if pinSubgroupSize32 else "" }}
|
| 125 |
fn main(
|
| 126 |
@builtin(workgroup_id) workgroup_id: vec3<u32>,
|
| 127 |
@builtin(local_invocation_index) local_idx: u32,
|
|
|
|
| 148 |
|
| 149 |
for (var step = 0u; step < TILE_K; step = step + 8u) {
|
| 150 |
{% set operandScalar = "f32" %} let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + step;
|
| 151 |
+
var matA0: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset, TILE_K);
|
| 152 |
+
var matA1: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset + 8u * TILE_K, TILE_K);
|
| 153 |
|
| 154 |
let matrix_b_offset = subtile_idx * SUB_COLS * TILE_K + step;
|
| 155 |
+
var matB0: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset, TILE_K);
|
| 156 |
+
var matB1: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 8u * TILE_K, TILE_K);
|
| 157 |
+
var matB2: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 16u * TILE_K, TILE_K);
|
| 158 |
+
var matB3: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 24u * TILE_K, TILE_K);
|
| 159 |
|
| 160 |
matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
|
| 161 |
matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
|
|
|
|
| 175 |
// clobbering the first's still-in-flight readback when a partial final M-tile
|
| 176 |
// diverges storeOutput's guard. Without both barriers the last valid row can be corrupted.
|
| 177 |
{% for col in [0, 1, 2, 3] %}
|
| 178 |
+
subgroupMatrixStore<row_major>(&scratch[subtile_id][{{ col }}], 0u, matC0{{ col }}, 8u);
|
| 179 |
{% endfor %}
|
| 180 |
workgroupBarrier();
|
| 181 |
let row = sg_id / 4u;
|
|
|
|
| 187 |
workgroupBarrier();
|
| 188 |
|
| 189 |
{% for col in [0, 1, 2, 3] %}
|
| 190 |
+
subgroupMatrixStore<row_major>(&scratch[subtile_id][{{ col }}], 0u, matC1{{ col }}, 8u);
|
| 191 |
{% endfor %}
|
| 192 |
workgroupBarrier();
|
| 193 |
matrix_c_offset = matrix_c_offset + 8u * N;
|
build/webgpu/metadata.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
{
|
| 2 |
"name": "com.microsoft.MatMulNBits",
|
| 3 |
-
"id": "
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
|
@@ -8,18 +8,18 @@
|
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
"bench.json": "BbpRtHBH/vkJDaybyRBNKhuUCpe4XMJtKmW3bhRcEZE=",
|
| 11 |
-
"manifest.json": "
|
| 12 |
"matmul-nbits-dp4a-quantize.wgsl.jinja": "WEUGSDH5qWYg10f/mkAHxq8AkIkMW9R/Wn/Y23Q0Jw0=",
|
| 13 |
-
"matmul-nbits-gemv-q4.wgsl.jinja": "
|
| 14 |
"matmul-nbits-q4-dp4a-prefill.wgsl.jinja": "qUrURZDpPzMpABnTgESkHgLCQiAWxEZ8wvzkAPTW2kI=",
|
| 15 |
"matmul-nbits-q4-prefill-tile4x4.wgsl.jinja": "HjZraSjOpaSl5Cjns4scselOIn+ibxFEu+9LTeUI0QI=",
|
| 16 |
"matmul-nbits-q4-prefill-tiled-reg.wgsl.jinja": "oU7BcxJnKIEfgnkWHPq77RrCmrE0iIm6ED7cgVfZUYs=",
|
| 17 |
"matmul-nbits-q4-prefill-tiled.wgsl.jinja": "TdetCiv5o88hvWCGNDewpFX+M+NZrz9rekU/d5kMZTM=",
|
| 18 |
-
"matmul-nbits-q4-sgmat.wgsl.jinja": "
|
| 19 |
"matmul-nbits.wgsl.jinja": "DRkwODEER11pYQxt2ssS9YvFggO3CbUqQ9yag53J5lY=",
|
| 20 |
-
"test.json": "
|
| 21 |
}
|
| 22 |
},
|
| 23 |
-
"provenance": { "kernel": { "sha": "
|
| 24 |
"webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.MatMulNBits" }
|
| 25 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"name": "com.microsoft.MatMulNBits",
|
| 3 |
+
"id": "_com_microsoft_matmulnbits_webgpu_5495bd2",
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
|
|
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
"bench.json": "BbpRtHBH/vkJDaybyRBNKhuUCpe4XMJtKmW3bhRcEZE=",
|
| 11 |
+
"manifest.json": "z8oCYWRaMHew5ogYuf7Txv4/6y2qpVoVODDlLxEZ+vA=",
|
| 12 |
"matmul-nbits-dp4a-quantize.wgsl.jinja": "WEUGSDH5qWYg10f/mkAHxq8AkIkMW9R/Wn/Y23Q0Jw0=",
|
| 13 |
+
"matmul-nbits-gemv-q4.wgsl.jinja": "x1LxVx2cJBdN2cqapXz2T6AScpTcbmTO+B3tzvIXuT8=",
|
| 14 |
"matmul-nbits-q4-dp4a-prefill.wgsl.jinja": "qUrURZDpPzMpABnTgESkHgLCQiAWxEZ8wvzkAPTW2kI=",
|
| 15 |
"matmul-nbits-q4-prefill-tile4x4.wgsl.jinja": "HjZraSjOpaSl5Cjns4scselOIn+ibxFEu+9LTeUI0QI=",
|
| 16 |
"matmul-nbits-q4-prefill-tiled-reg.wgsl.jinja": "oU7BcxJnKIEfgnkWHPq77RrCmrE0iIm6ED7cgVfZUYs=",
|
| 17 |
"matmul-nbits-q4-prefill-tiled.wgsl.jinja": "TdetCiv5o88hvWCGNDewpFX+M+NZrz9rekU/d5kMZTM=",
|
| 18 |
+
"matmul-nbits-q4-sgmat.wgsl.jinja": "PfO9QrUyNflp7RtUbUTRDwx7GDI6UUwz9I+6mJpO1OA=",
|
| 19 |
"matmul-nbits.wgsl.jinja": "DRkwODEER11pYQxt2ssS9YvFggO3CbUqQ9yag53J5lY=",
|
| 20 |
+
"test.json": "XLwnbLdOJASke2yBnDScjJi+o2XISmdRCebb2jwWpDI="
|
| 21 |
}
|
| 22 |
},
|
| 23 |
+
"provenance": { "kernel": { "sha": "c928d21e6cc1310861cba3bafb75f5f679ecf5f3", "dirty": false } },
|
| 24 |
"webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.MatMulNBits" }
|
| 25 |
}
|
build/webgpu/test.json
CHANGED
|
@@ -858,6 +858,56 @@
|
|
| 858 |
"outputs": { "yT": { "dtype": "float32", "shape": [1, 7], "tolerance": 0.00001 } },
|
| 859 |
"attrs": { "K": 32, "N": 7, "bits": 4, "block_size": 32 }
|
| 860 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 861 |
{
|
| 862 |
"name": "q4_gemv_default_zero_m1_n13_ncols8",
|
| 863 |
"tunables": { "GEMV_N_COLS": 8 },
|
|
|
|
| 858 |
"outputs": { "yT": { "dtype": "float32", "shape": [1, 7], "tolerance": 0.00001 } },
|
| 859 |
"attrs": { "K": 32, "N": 7, "bits": 4, "block_size": 32 }
|
| 860 |
},
|
| 861 |
+
{
|
| 862 |
+
"name": "q4_gemv_default_zero_m1_odd_k33_n5",
|
| 863 |
+
"provenance": {
|
| 864 |
+
"notes": "K=33 is odd, so the last stored byte of the second block carries one live nibble and one past the end of K. The GEMV walks whole bytes, so this is the only shape that exercises its out-of-range activation guard; every other M==1 case has an even K."
|
| 865 |
+
},
|
| 866 |
+
"inputs": {
|
| 867 |
+
"aT": {
|
| 868 |
+
"dtype": "float32",
|
| 869 |
+
"shape": [1, 33],
|
| 870 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
|
| 871 |
+
},
|
| 872 |
+
"bT": {
|
| 873 |
+
"dtype": "uint8",
|
| 874 |
+
"shape": [5, 2, 16],
|
| 875 |
+
"data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/q4_weight_cycle_b_t" } }
|
| 876 |
+
},
|
| 877 |
+
"scalesT": {
|
| 878 |
+
"dtype": "float32",
|
| 879 |
+
"shape": [5, 2],
|
| 880 |
+
"data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/quant_scale_cycle_t" } }
|
| 881 |
+
}
|
| 882 |
+
},
|
| 883 |
+
"outputs": { "yT": { "dtype": "float32", "shape": [1, 5], "tolerance": 0.00001 } },
|
| 884 |
+
"attrs": { "K": 33, "N": 5, "bits": 4, "block_size": 32 }
|
| 885 |
+
},
|
| 886 |
+
{
|
| 887 |
+
"name": "q2_gemv_default_zero_m1_k18_n5",
|
| 888 |
+
"provenance": {
|
| 889 |
+
"notes": "The only two-bit M==1 case: four codes share one stored byte, and K=18 leaves two of the last byte's four codes past the end of K. Covers both the two-bit unpack in the GEMV and its out-of-range activation guard at the widest code-per-byte packing."
|
| 890 |
+
},
|
| 891 |
+
"inputs": {
|
| 892 |
+
"aT": {
|
| 893 |
+
"dtype": "float32",
|
| 894 |
+
"shape": [1, 18],
|
| 895 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
|
| 896 |
+
},
|
| 897 |
+
"bT": {
|
| 898 |
+
"dtype": "uint8",
|
| 899 |
+
"shape": [5, 2, 4],
|
| 900 |
+
"data": { "kind": "cycle", "values": [27, 180, 75, 226, 33, 150, 201, 108, 57, 246, 129, 66, 195, 84, 237] }
|
| 901 |
+
},
|
| 902 |
+
"scalesT": {
|
| 903 |
+
"dtype": "float32",
|
| 904 |
+
"shape": [5, 2],
|
| 905 |
+
"data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/quant_scale_cycle_t" } }
|
| 906 |
+
}
|
| 907 |
+
},
|
| 908 |
+
"outputs": { "yT": { "dtype": "float32", "shape": [1, 5], "tolerance": 0.00001 } },
|
| 909 |
+
"attrs": { "K": 18, "N": 5, "bits": 2, "block_size": 16 }
|
| 910 |
+
},
|
| 911 |
{
|
| 912 |
"name": "q4_gemv_default_zero_m1_n13_ncols8",
|
| 913 |
"tunables": { "GEMV_N_COLS": 8 },
|