Download engine/f5engine.js from DownFlow/webasmr: direct link, hf CLI and curl.
- Browser
- Download file 55.1 kB
-
https://huggingface.co/spaces/DownFlow/webasmr/resolve/main/engine/f5engine.js
- Command line
-
hf download hf://spaces/DownFlow/webasmr/engine/f5engine.js
-
curl -L -o f5engine.js https://huggingface.co/spaces/DownFlow/webasmr/resolve/main/engine/f5engine.js
55.1 kB
| // F5-TTS DiT custom WebGPU engine (fp16 weights, fp32 accumulators). | |
| const PRELUDE = `enable f16;\n`; | |
| const REDUCE8 = ` | |
| if (tid < 128u) { red[tid] += red[tid + 128u]; } workgroupBarrier(); | |
| if (tid < 64u) { red[tid] += red[tid + 64u]; } workgroupBarrier(); | |
| if (tid < 32u) { red[tid] += red[tid + 32u]; } workgroupBarrier(); | |
| if (tid < 16u) { red[tid] += red[tid + 16u]; } workgroupBarrier(); | |
| if (tid < 8u) { red[tid] += red[tid + 8u]; } workgroupBarrier(); | |
| if (tid < 4u) { red[tid] += red[tid + 4u]; } workgroupBarrier(); | |
| if (tid < 2u) { red[tid] += red[tid + 2u]; } workgroupBarrier(); | |
| if (tid < 1u) { red[tid] += red[tid + 1u]; } workgroupBarrier(); | |
| `; | |
| const REDUCE8MAX = ` | |
| if (tid < 128u) { red[tid] = max(red[tid], red[tid + 128u]); } workgroupBarrier(); | |
| if (tid < 64u) { red[tid] = max(red[tid], red[tid + 64u]); } workgroupBarrier(); | |
| if (tid < 32u) { red[tid] = max(red[tid], red[tid + 32u]); } workgroupBarrier(); | |
| if (tid < 16u) { red[tid] = max(red[tid], red[tid + 16u]); } workgroupBarrier(); | |
| if (tid < 8u) { red[tid] = max(red[tid], red[tid + 8u]); } workgroupBarrier(); | |
| if (tid < 4u) { red[tid] = max(red[tid], red[tid + 4u]); } workgroupBarrier(); | |
| if (tid < 2u) { red[tid] = max(red[tid], red[tid + 2u]); } workgroupBarrier(); | |
| if (tid < 1u) { red[tid] = max(red[tid], red[tid + 1u]); } workgroupBarrier(); | |
| `; | |
| const GEMM = ` | |
| enable chromium_experimental_subgroup_matrix; | |
| diagnostic(off, chromium.subgroup_matrix_uniformity); | |
| struct P { M: u32, N: u32, K: u32, pad: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> A: array<f16>; | |
| @group(0) @binding(2) var<storage, read> B: array<f16>; | |
| @group(0) @binding(3) var<storage, read> bias: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> C: array<f16>; | |
| const TR: u32 = 128u; const TC: u32 = 64u; const TK: u32 = 32u; | |
| var<workgroup> tile_A: array<f16, TR * TK>; | |
| var<workgroup> tile_B: array<f16, TC * TK>; | |
| var<workgroup> scratch: array<array<f32, 64>, 8>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let bm = wg.x * TR; let bn = wg.y * TC; | |
| let sg = tid / 32u; let lane = tid % 32u; | |
| let sg_row = sg / 2u; let sg_col = sg % 2u; | |
| var acc: array<subgroup_matrix_result<f32, 8, 8>, 16>; | |
| for (var i = 0u; i < 16u; i++) { acc[i] = subgroup_matrix_result<f32, 8, 8>(); } | |
| let nk = min((p.K + TK - 1u) / TK, 2048u); | |
| for (var kt = 0u; kt < nk; kt++) { | |
| let k0 = kt * TK; | |
| for (var i = 0u; i < 16u; i++) { | |
| let idx = i * 256u + tid; let r = idx / TK; let c = idx % TK; | |
| let gr = bm + r; let gc = k0 + c; | |
| var v: f16 = f16(0.0); | |
| if (gr < p.M && gc < p.K) { v = A[gr * p.K + gc]; } | |
| tile_A[idx] = v; | |
| } | |
| for (var i = 0u; i < 8u; i++) { | |
| let idx = i * 256u + tid; let gn = idx % TC; let gk = idx / TC; | |
| let gr = bn + gn; let gc = k0 + gk; | |
| var v: f16 = f16(0.0); | |
| if (gr < p.N && gc < p.K) { v = B[gc * p.N + gr]; } | |
| tile_B[gn * TK + gk] = v; | |
| } | |
| workgroupBarrier(); | |
| for (var step = 0u; step < TK; step += 8u) { | |
| let a_off = (sg_row * 32u) * TK + step; | |
| let m0 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off, false, TK); | |
| let m1 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off + 8u * TK, false, TK); | |
| let m2 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off + 16u * TK, false, TK); | |
| let m3 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off + 24u * TK, false, TK); | |
| let b_off = (sg_col * 32u) * TK + step; | |
| let n0 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off, true, TK); | |
| let n1 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off + 8u * TK, true, TK); | |
| let n2 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off + 16u * TK, true, TK); | |
| let n3 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off + 24u * TK, true, TK); | |
| acc[0] = subgroupMatrixMultiplyAccumulate(m0, n0, acc[0]); | |
| acc[1] = subgroupMatrixMultiplyAccumulate(m0, n1, acc[1]); | |
| acc[2] = subgroupMatrixMultiplyAccumulate(m0, n2, acc[2]); | |
| acc[3] = subgroupMatrixMultiplyAccumulate(m0, n3, acc[3]); | |
| acc[4] = subgroupMatrixMultiplyAccumulate(m1, n0, acc[4]); | |
| acc[5] = subgroupMatrixMultiplyAccumulate(m1, n1, acc[5]); | |
| acc[6] = subgroupMatrixMultiplyAccumulate(m1, n2, acc[6]); | |
| acc[7] = subgroupMatrixMultiplyAccumulate(m1, n3, acc[7]); | |
| acc[8] = subgroupMatrixMultiplyAccumulate(m2, n0, acc[8]); | |
| acc[9] = subgroupMatrixMultiplyAccumulate(m2, n1, acc[9]); | |
| acc[10] = subgroupMatrixMultiplyAccumulate(m2, n2, acc[10]); | |
| acc[11] = subgroupMatrixMultiplyAccumulate(m2, n3, acc[11]); | |
| acc[12] = subgroupMatrixMultiplyAccumulate(m3, n0, acc[12]); | |
| acc[13] = subgroupMatrixMultiplyAccumulate(m3, n1, acc[13]); | |
| acc[14] = subgroupMatrixMultiplyAccumulate(m3, n2, acc[14]); | |
| acc[15] = subgroupMatrixMultiplyAccumulate(m3, n3, acc[15]); | |
| } | |
| workgroupBarrier(); | |
| } | |
| let row0 = bm + sg_row * 32u; | |
| let col0 = bn + sg_col * 32u; | |
| for (var f = 0u; f < 16u; f++) { | |
| subgroupMatrixStore(&scratch[sg], 0u, acc[f], false, 8u); | |
| workgroupBarrier(); | |
| let fr = f / 4u; let fc = f % 4u; | |
| for (var e = lane; e < 64u; e += 32u) { | |
| let i = e / 8u; let j = e % 8u; | |
| let gr = row0 + fr * 8u + i; let gc = col0 + fc * 8u + j; | |
| if (gr < p.M && gc < p.N) { C[gr * p.N + gc] = f16(clamp(scratch[sg][e] + f32(bias[gc]), -60000.0, 60000.0)); } | |
| } | |
| workgroupBarrier(); | |
| } | |
| } | |
| `; | |
| const GEMM_SIMT_I4 = ` | |
| struct P { M: u32, N: u32, K: u32, group: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> A: array<f16>; | |
| @group(0) @binding(2) var<storage, read> Pk: array<u32>; | |
| @group(0) @binding(3) var<storage, read> Sc: array<f16>; | |
| @group(0) @binding(4) var<storage, read> bias: array<f16>; | |
| @group(0) @binding(5) var<storage, read_write> C: array<f16>; | |
| const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 32u; | |
| var<workgroup> As: array<f32, 2048>; | |
| var<workgroup> Bs: array<f32, 2048>; | |
| @compute @workgroup_size(16, 16) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let bm = wg.x * BM; let bn = wg.y * BN; | |
| let ty = tid / 16u; let tx = tid % 16u; | |
| var acc: array<vec4<f32>, 4>; | |
| for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); } | |
| let nk = min((p.K + BK - 1u) / BK, 2048u); | |
| for (var kt = 0u; kt < nk; kt++) { | |
| let k0 = kt * BK; | |
| for (var i = 0u; i < 8u; i++) { | |
| let idx = i * 256u + tid; let r = idx / BK; let c = idx % BK; | |
| let gr = bm + r; let gc = k0 + c; | |
| var v: f32 = 0.0; | |
| if (gr < p.M && gc < p.K) { v = f32(A[gr * p.K + gc]); } | |
| As[r * BK + c] = v; | |
| } | |
| // dequant B tile: 256 words (16 kp x 16 words of 4 columns) | |
| let kp = tid / 16u; let n0 = (tid % 16u) * 4u; | |
| let gk2 = k0 / 2u + kp; let gn0 = bn + n0; | |
| var word: u32 = 0u; | |
| if (gk2 < p.K / 2u && gn0 < p.N) { word = Pk[(gk2 * p.N + gn0) / 4u]; } | |
| let grp = (k0 + 2u * kp) / p.group; | |
| for (var j = 0u; j < 4u; j++) { | |
| let n = n0 + j; | |
| if (bn + n < p.N) { | |
| let byte = (word >> (8u * j)) & 0xFFu; | |
| let s = f32(Sc[grp * p.N + bn + n]); | |
| Bs[(2u * kp) * BN + n] = (f32(byte & 0xFu) - 8.0) * s; | |
| Bs[(2u * kp + 1u) * BN + n] = (f32(byte >> 4u) - 8.0) * s; | |
| } else { | |
| Bs[(2u * kp) * BN + n] = 0.0; | |
| Bs[(2u * kp + 1u) * BN + n] = 0.0; | |
| } | |
| } | |
| workgroupBarrier(); | |
| for (var k = 0u; k < BK; k++) { | |
| let a0 = As[(ty * 4u + 0u) * BK + k]; | |
| let a1 = As[(ty * 4u + 1u) * BK + k]; | |
| let a2 = As[(ty * 4u + 2u) * BK + k]; | |
| let a3 = As[(ty * 4u + 3u) * BK + k]; | |
| let b0 = Bs[k * BN + tx * 4u + 0u]; | |
| let b1 = Bs[k * BN + tx * 4u + 1u]; | |
| let b2 = Bs[k * BN + tx * 4u + 2u]; | |
| let b3 = Bs[k * BN + tx * 4u + 3u]; | |
| let bv = vec4<f32>(b0, b1, b2, b3); | |
| acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; | |
| } | |
| workgroupBarrier(); | |
| } | |
| for (var r = 0u; r < 4u; r++) { | |
| let gr = bm + ty * 4u + r; | |
| if (gr < p.M) { | |
| for (var c = 0u; c < 4u; c++) { | |
| let gc = bn + tx * 4u + c; | |
| if (gc < p.N) { C[gr * p.N + gc] = f16(clamp(acc[r][c] + f32(bias[gc]), -60000.0, 60000.0)); } | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const GEMM_SIMT = ` | |
| struct P { M: u32, N: u32, K: u32, pad: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> A: array<f16>; | |
| @group(0) @binding(2) var<storage, read> B: array<f16>; | |
| @group(0) @binding(3) var<storage, read> bias: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> C: array<f16>; | |
| const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 32u; | |
| var<workgroup> As: array<f32, 2048>; | |
| var<workgroup> Bs: array<f32, 2048>; | |
| @compute @workgroup_size(16, 16) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let bm = wg.x * BM; let bn = wg.y * BN; | |
| let ty = tid / 16u; let tx = tid % 16u; | |
| var acc: array<vec4<f32>, 4>; | |
| for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); } | |
| let nk = min((p.K + BK - 1u) / BK, 2048u); | |
| for (var kt = 0u; kt < nk; kt++) { | |
| let k0 = kt * BK; | |
| for (var i = 0u; i < 8u; i++) { | |
| let idx = i * 256u + tid; let r = idx / BK; let c = idx % BK; | |
| let gr = bm + r; let gc = k0 + c; | |
| var v: f32 = 0.0; | |
| if (gr < p.M && gc < p.K) { v = f32(A[gr * p.K + gc]); } | |
| As[r * BK + c] = v; | |
| } | |
| for (var i = 0u; i < 8u; i++) { | |
| let idx = i * 256u + tid; let r = idx / BN; let c = idx % BN; | |
| let gr = k0 + r; let gc = bn + c; | |
| var v: f32 = 0.0; | |
| if (gr < p.K && gc < p.N) { v = f32(B[gr * p.N + gc]); } | |
| Bs[r * BN + c] = v; | |
| } | |
| workgroupBarrier(); | |
| for (var k = 0u; k < BK; k++) { | |
| let a0 = As[(ty * 4u + 0u) * BK + k]; | |
| let a1 = As[(ty * 4u + 1u) * BK + k]; | |
| let a2 = As[(ty * 4u + 2u) * BK + k]; | |
| let a3 = As[(ty * 4u + 3u) * BK + k]; | |
| let b0 = Bs[k * BN + tx * 4u + 0u]; | |
| let b1 = Bs[k * BN + tx * 4u + 1u]; | |
| let b2 = Bs[k * BN + tx * 4u + 2u]; | |
| let b3 = Bs[k * BN + tx * 4u + 3u]; | |
| let bv = vec4<f32>(b0, b1, b2, b3); | |
| acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; | |
| } | |
| workgroupBarrier(); | |
| } | |
| for (var r = 0u; r < 4u; r++) { | |
| let gr = bm + ty * 4u + r; | |
| if (gr < p.M) { | |
| for (var c = 0u; c < 4u; c++) { | |
| let gc = bn + tx * 4u + c; | |
| if (gc < p.N) { C[gr * p.N + gc] = f16(clamp(acc[r][c] + f32(bias[gc]), -60000.0, 60000.0)); } | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const GEMV = ` | |
| struct P { N: u32, K: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> A: array<f16>; | |
| @group(0) @binding(2) var<storage, read> B: array<f16>; | |
| @group(0) @binding(3) var<storage, read> bias: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> C: array<f16>; | |
| var<workgroup> shA: array<f32, 1024>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| for (var i = tid; i < p.K; i += 256u) { shA[i] = f32(A[i]); } | |
| workgroupBarrier(); | |
| let base = wg.x * 1024u + tid * 4u; | |
| var acc = vec4<f32>(0.0); | |
| let KK = min(p.K, 65536u); | |
| for (var k = 0u; k < KK; k++) { | |
| let a = shA[k]; | |
| let o = k * p.N + base; | |
| acc += a * vec4<f32>(f32(B[o]), f32(B[o + 1u]), f32(B[o + 2u]), f32(B[o + 3u])); | |
| } | |
| for (var j = 0u; j < 4u; j++) { | |
| let c = base + j; | |
| if (c < p.N) { C[c] = f16(acc[j] + f32(bias[c])); } | |
| } | |
| } | |
| `; | |
| const GEMV_I4 = ` | |
| struct P { N: u32, K: u32, group: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> A: array<f16>; | |
| @group(0) @binding(2) var<storage, read> Pk: array<u32>; | |
| @group(0) @binding(3) var<storage, read> Sc: array<f16>; | |
| @group(0) @binding(4) var<storage, read> bias: array<f16>; | |
| @group(0) @binding(5) var<storage, read_write> C: array<f16>; | |
| var<workgroup> shA: array<f32, 1024>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| for (var i = tid; i < p.K; i += 256u) { shA[i] = f32(A[i]); } | |
| workgroupBarrier(); | |
| let base = wg.x * 1024u + tid * 4u; | |
| var acc = vec4<f32>(0.0); | |
| let KK = min(p.K, 65536u); | |
| for (var k = 0u; k < KK; k++) { | |
| let a = shA[k]; | |
| let word = Pk[((k / 2u) * p.N + base) / 4u]; | |
| let sh = (k & 1u) * 4u; | |
| let s = vec4<f32>(f32(Sc[(k / p.group) * p.N + base]), f32(Sc[(k / p.group) * p.N + base + 1u]), | |
| f32(Sc[(k / p.group) * p.N + base + 2u]), f32(Sc[(k / p.group) * p.N + base + 3u])); | |
| let q = vec4<f32>(f32((word >> sh) & 0xFu), f32((word >> (8u + sh)) & 0xFu), | |
| f32((word >> (16u + sh)) & 0xFu), f32((word >> (24u + sh)) & 0xFu)); | |
| acc += a * (q - vec4<f32>(8.0)) * s; | |
| } | |
| let bb = vec4<f32>(f32(bias[base]), f32(bias[base + 1u]), f32(bias[base + 2u]), f32(bias[base + 3u])); | |
| for (var j = 0u; j < 4u; j++) { if (base + j < p.N) { C[base + j] = f16(acc[j] + bb[j]); } } | |
| } | |
| `; | |
| const LNM = ` | |
| struct P { M: u32, D: u32, offS: u32, offT: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read> MOD: array<f16>; | |
| @group(0) @binding(3) var<storage, read_write> Y: array<f16>; | |
| var<workgroup> red: array<f32, 256>; | |
| var<workgroup> stats: array<f32, 2>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let row = wg.x; let D = p.D; let off = row * D; | |
| var s = 0.0; | |
| for (var i = tid; i < D; i += 256u) { s += f32(X[off + i]); } | |
| red[tid] = s; workgroupBarrier(); | |
| ${REDUCE8} | |
| if (tid == 0u) { stats[0] = red[0] / f32(D); } | |
| workgroupBarrier(); | |
| let mu = stats[0]; | |
| var vs = 0.0; | |
| for (var i = tid; i < D; i += 256u) { let d = f32(X[off + i]) - mu; vs += d * d; } | |
| red[tid] = vs; workgroupBarrier(); | |
| ${REDUCE8} | |
| if (tid == 0u) { stats[1] = inverseSqrt(red[0] / f32(D) + 1e-6); } | |
| workgroupBarrier(); | |
| let inv = stats[1]; | |
| for (var i = tid; i < D; i += 256u) { | |
| let sc = 1.0 + f32(MOD[p.offS + i]); | |
| let sh = f32(MOD[p.offT + i]); | |
| Y[off + i] = f16((f32(X[off + i]) - mu) * inv * sc + sh); | |
| } | |
| } | |
| `; | |
| const LNA = ` | |
| struct P { M: u32, D: u32, pad0: u32, pad1: u32, eps: f32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read> Wt: array<f16>; | |
| @group(0) @binding(3) var<storage, read> Bs: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> Y: array<f16>; | |
| var<workgroup> red: array<f32, 256>; | |
| var<workgroup> stats: array<f32, 2>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let row = wg.x; let D = p.D; let off = row * D; | |
| var s = 0.0; | |
| for (var i = tid; i < D; i += 256u) { s += f32(X[off + i]); } | |
| red[tid] = s; workgroupBarrier(); | |
| ${REDUCE8} | |
| if (tid == 0u) { stats[0] = red[0] / f32(D); } | |
| workgroupBarrier(); | |
| let mu = stats[0]; | |
| var vs = 0.0; | |
| for (var i = tid; i < D; i += 256u) { let d = f32(X[off + i]) - mu; vs += d * d; } | |
| red[tid] = vs; workgroupBarrier(); | |
| ${REDUCE8} | |
| if (tid == 0u) { stats[1] = inverseSqrt(red[0] / f32(D) + p.eps); } | |
| workgroupBarrier(); | |
| let inv = stats[1]; | |
| for (var i = tid; i < D; i += 256u) { | |
| Y[off + i] = f16((f32(X[off + i]) - mu) * inv * f32(Wt[i]) + f32(Bs[i])); | |
| } | |
| } | |
| `; | |
| const ROPE = ` | |
| struct P { M: u32, N: u32, total: u32, pad: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> FREQ: array<f32>; | |
| @group(0) @binding(2) var<storage, read_write> X: array<f16>; | |
| const SC: f32 = 0.35355339059327379; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.total) { return; } | |
| let pair = i % 32u; let t = i / 32u; let h = t % 16u; let row = t / 16u; | |
| let pos = row % p.N; | |
| let theta = f32(pos) * FREQ[pair]; | |
| let c = cos(theta); let s = sin(theta); | |
| let base = row * 1024u + h * 64u + pair * 2u; | |
| let x0 = f32(X[base]); let x1 = f32(X[base + 1u]); | |
| X[base] = f16((x0 * c - x1 * s) * SC); | |
| X[base + 1u] = f16((x0 * s + x1 * c) * SC); | |
| } | |
| `; | |
| const QK = ` | |
| struct P { N: u32, B: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> Q: array<f16>; | |
| @group(0) @binding(2) var<storage, read> K: array<f16>; | |
| @group(0) @binding(3) var<storage, read_write> S: array<f32>; | |
| var<workgroup> Qs: array<f16, 4096>; | |
| var<workgroup> Ks: array<f16, 4096>; | |
| @compute @workgroup_size(16, 16) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let b = wg.z / 16u; let h = wg.z % 16u; | |
| let qi = wg.x * 64u; let kj = wg.y * 64u; | |
| let N = min(p.N, 8192u); | |
| for (var i = 0u; i < 16u; i++) { | |
| let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u; | |
| let qr = qi + r; var v: f16 = f16(0.0); | |
| if (qr < N) { v = Q[((b * N + qr) * 16u + h) * 64u + c]; } | |
| Qs[idx] = v; | |
| } | |
| for (var i = 0u; i < 16u; i++) { | |
| let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u; | |
| let kr = kj + r; var v: f16 = f16(0.0); | |
| if (kr < N) { v = K[((b * N + kr) * 16u + h) * 64u + c]; } | |
| Ks[idx] = v; | |
| } | |
| workgroupBarrier(); | |
| let ty = tid / 16u; let tx = tid % 16u; | |
| var acc: array<vec4<f32>, 4>; | |
| for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); } | |
| for (var k = 0u; k < 64u; k++) { | |
| let a0 = f32(Qs[(ty * 4u + 0u) * 64u + k]); | |
| let a1 = f32(Qs[(ty * 4u + 1u) * 64u + k]); | |
| let a2 = f32(Qs[(ty * 4u + 2u) * 64u + k]); | |
| let a3 = f32(Qs[(ty * 4u + 3u) * 64u + k]); | |
| let b0 = f32(Ks[(tx * 4u + 0u) * 64u + k]); | |
| let b1 = f32(Ks[(tx * 4u + 1u) * 64u + k]); | |
| let b2 = f32(Ks[(tx * 4u + 2u) * 64u + k]); | |
| let b3 = f32(Ks[(tx * 4u + 3u) * 64u + k]); | |
| let bv = vec4<f32>(b0, b1, b2, b3); | |
| acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; | |
| } | |
| let bidx = wg.z; | |
| for (var r = 0u; r < 4u; r++) { | |
| let gr = qi + ty * 4u + r; | |
| if (gr < N) { | |
| for (var c = 0u; c < 4u; c++) { | |
| let gc = kj + tx * 4u + c; | |
| if (gc < N) { S[(bidx * N + gr) * N + gc] = acc[r][c]; } | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const SOFTMAX = ` | |
| struct P { N: u32, rows: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read_write> S: array<f32>; | |
| var<workgroup> red: array<f32, 256>; | |
| var<workgroup> st: array<f32, 2>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let off = wg.x * p.N; | |
| var m = -3.4e38; | |
| for (var i = tid; i < p.N; i += 256u) { m = max(m, S[off + i]); } | |
| red[tid] = m; workgroupBarrier(); | |
| ${REDUCE8MAX} | |
| if (tid == 0u) { st[0] = red[0]; } | |
| workgroupBarrier(); | |
| let mx = st[0]; | |
| var sum = 0.0; | |
| for (var i = tid; i < p.N; i += 256u) { sum += exp(S[off + i] - mx); } | |
| red[tid] = sum; workgroupBarrier(); | |
| ${REDUCE8} | |
| if (tid == 0u) { st[1] = 1.0 / red[0]; } | |
| workgroupBarrier(); | |
| let inv = st[1]; | |
| for (var i = tid; i < p.N; i += 256u) { S[off + i] = exp(S[off + i] - mx) * inv; } | |
| } | |
| `; | |
| const AV = ` | |
| struct P { N: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> S: array<f32>; | |
| @group(0) @binding(2) var<storage, read> V: array<f16>; | |
| @group(0) @binding(3) var<storage, read_write> O: array<f16>; | |
| var<workgroup> Ss: array<f32, 4096>; | |
| var<workgroup> Vs: array<f16, 4096>; | |
| @compute @workgroup_size(16, 16) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let b = wg.z / 16u; let h = wg.z % 16u; | |
| let qi = wg.x * 64u; | |
| let N = min(p.N, 8192u); let bidx = wg.z; | |
| let ty = tid / 16u; let tx = tid % 16u; | |
| var acc: array<vec4<f32>, 4>; | |
| for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); } | |
| let nk = min((N + 63u) / 64u, 256u); | |
| for (var kt = 0u; kt < nk; kt++) { | |
| let k0 = kt * 64u; | |
| for (var i = 0u; i < 16u; i++) { | |
| let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u; | |
| let gr = qi + r; let skey = k0 + c; let vkey = k0 + r; | |
| var v0: f32 = 0.0; | |
| if (gr < N && skey < N) { v0 = S[(bidx * N + gr) * N + skey]; } | |
| Ss[r * 64u + c] = v0; | |
| var v1: f16 = f16(0.0); | |
| if (vkey < N) { v1 = V[((b * N + vkey) * 16u + h) * 64u + c]; } | |
| Vs[r * 64u + c] = v1; | |
| } | |
| workgroupBarrier(); | |
| for (var k = 0u; k < 64u; k++) { | |
| let a0 = Ss[(ty * 4u + 0u) * 64u + k]; | |
| let a1 = Ss[(ty * 4u + 1u) * 64u + k]; | |
| let a2 = Ss[(ty * 4u + 2u) * 64u + k]; | |
| let a3 = Ss[(ty * 4u + 3u) * 64u + k]; | |
| let b0 = f32(Vs[k * 64u + tx * 4u + 0u]); | |
| let b1 = f32(Vs[k * 64u + tx * 4u + 1u]); | |
| let b2 = f32(Vs[k * 64u + tx * 4u + 2u]); | |
| let b3 = f32(Vs[k * 64u + tx * 4u + 3u]); | |
| let bv = vec4<f32>(b0, b1, b2, b3); | |
| acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; | |
| } | |
| workgroupBarrier(); | |
| } | |
| for (var r = 0u; r < 4u; r++) { | |
| let gr = qi + ty * 4u + r; | |
| if (gr < N) { | |
| for (var c = 0u; c < 4u; c++) { | |
| O[((b * N + gr) * 16u + h) * 64u + tx * 4u + c] = f16(acc[r][c]); | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const ADDGATE = ` | |
| struct P { M: u32, D: u32, gOff: u32, pad: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read_write> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read> Z: array<f16>; | |
| @group(0) @binding(3) var<storage, read> MOD: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.M * p.D) { return; } | |
| let d = i % p.D; | |
| X[i] = f16(f32(X[i]) + f32(MOD[p.gOff + d]) * f32(Z[i])); | |
| } | |
| `; | |
| const GELU = ` | |
| struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read_write> X: array<f16>; | |
| fn erf(x: f32) -> f32 { | |
| let ax = abs(x); | |
| let t = 1.0 / (1.0 + 0.3275911 * ax); | |
| let y = 1.0 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * exp(-ax * ax); | |
| return select(-y, y, x >= 0.0); | |
| } | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| if (gid.x >= p.total) { return; } | |
| let x = f32(X[gid.x]); | |
| X[gid.x] = f16(0.5 * x * (1.0 + erf(x * 0.70710678118654752))); | |
| } | |
| `; | |
| const SILU = ` | |
| struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read_write> X: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| if (gid.x >= p.total) { return; } | |
| let x = f32(X[gid.x]); | |
| X[gid.x] = f16(x / (1.0 + exp(-x))); | |
| } | |
| `; | |
| const MISH = ` | |
| struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read_write> X: array<f16>; | |
| fn tanh_safe(v: f32) -> f32 { | |
| if (v > 15.0) { return 1.0; } | |
| if (v < -15.0) { return -1.0; } | |
| return tanh(v); | |
| } | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| if (gid.x >= p.total) { return; } | |
| let x = f32(X[gid.x]); | |
| var sp = x; | |
| if (x < 20.0) { sp = log(1.0 + exp(x)); } | |
| X[gid.x] = f16(x * tanh_safe(sp)); | |
| } | |
| `; | |
| const CONCAT3 = ` | |
| struct P { N: u32, B: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read> C: array<f16>; | |
| @group(0) @binding(3) var<storage, read> T: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> O: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.B * p.N * 712u) { return; } | |
| let col = i % 712u; let row = i / 712u; | |
| let b = row / p.N; let f = row % p.N; | |
| var v: f16 = f16(0.0); | |
| if (col < 100u) { v = X[f * 100u + col]; } | |
| else if (col < 200u) { if (b == 0u) { v = C[f * 100u + (col - 100u)]; } } | |
| else { if (b == 0u) { v = T[f * 512u + (col - 200u)]; } } | |
| O[i] = v; | |
| } | |
| `; | |
| const GATHER = ` | |
| struct P { N: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> IDS: array<i32>; | |
| @group(0) @binding(2) var<storage, read> EMB: array<f16>; | |
| @group(0) @binding(3) var<storage, read> FRQ: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> O: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.N * 512u) { return; } | |
| let f = i / 512u; let d = i % 512u; | |
| let id = IDS[f] + 1; | |
| if (id <= 0) { O[i] = f16(0.0); return; } | |
| O[i] = f16(f32(EMB[u32(id) * 512u + d]) + f32(FRQ[f * 512u + d])); | |
| } | |
| `; | |
| const DWCONV = ` | |
| struct P { M: u32, D: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read> Wt: array<f16>; | |
| @group(0) @binding(3) var<storage, read> Bs: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> Y: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.M * p.D) { return; } | |
| let d = i32(i % p.D); let f = i32(i / p.D); | |
| var acc = f32(Bs[u32(d)]); | |
| for (var k = 0; k < 7; k++) { | |
| let ff = f - 3 + k; | |
| if (ff >= 0 && ff < i32(p.M)) { acc += f32(Wt[u32(d) * 7u + u32(k)]) * f32(X[u32(ff) * p.D + u32(d)]); } | |
| } | |
| Y[i] = f16(acc); | |
| } | |
| `; | |
| const GRNRED = ` | |
| struct P { M: u32, D: u32, off: u32, pad: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read_write> NM: array<f32>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>) { | |
| let d = wg.x + p.off; | |
| var s = 0.0; | |
| for (var f = 0u; f < p.M; f++) { let v = f32(X[f * p.D + d]); s += v * v; } | |
| NM[d] = sqrt(s); | |
| } | |
| `; | |
| const GRNMEAN = ` | |
| struct P { D: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> NM: array<f32>; | |
| @group(0) @binding(2) var<storage, read_write> MEAN: array<f32>; | |
| var<workgroup> red: array<f32, 256>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(local_invocation_index) tid: u32) { | |
| var acc = 0.0; | |
| for (var c = tid; c < p.D; c += 256u) { acc += NM[c]; } | |
| red[tid] = acc; workgroupBarrier(); | |
| ${REDUCE8} | |
| if (tid == 0u) { MEAN[0] = red[0] / f32(p.D); } | |
| } | |
| `; | |
| const GRNAPPLY = ` | |
| struct P { M: u32, D: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read_write> Y: array<f16>; | |
| @group(0) @binding(3) var<storage, read> G: array<f16>; | |
| @group(0) @binding(4) var<storage, read> B: array<f16>; | |
| @group(0) @binding(5) var<storage, read> NM: array<f32>; | |
| @group(0) @binding(6) var<storage, read> MEAN: array<f32>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.M * p.D) { return; } | |
| let d = i % p.D; | |
| let v = f32(X[i]); | |
| Y[i] = f16(v * (NM[d] / (MEAN[0] + 1e-6)) * f32(G[d]) + f32(B[d]) + v); | |
| } | |
| `; | |
| const ADDRES = ` | |
| struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read_write> Y: array<f16>; | |
| @group(0) @binding(2) var<storage, read> X: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| if (gid.x >= p.total) { return; } | |
| Y[gid.x] = f16(f32(Y[gid.x]) + f32(X[gid.x])); | |
| } | |
| `; | |
| const ZEROFILL = ` | |
| struct P { M: u32, D: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> IDS: array<i32>; | |
| @group(0) @binding(2) var<storage, read_write> X: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.M * p.D) { return; } | |
| if (IDS[i / p.D] < 0) { X[i] = f16(0.0); } | |
| } | |
| `; | |
| const CONVPOS = ` | |
| struct P { N: u32, M: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read> Wt: array<f16>; | |
| @group(0) @binding(3) var<storage, read> Bs: array<f16>; | |
| @group(0) @binding(4) var<storage, read_write> Y: array<f16>; | |
| var<workgroup> Ws: array<f16, 2 * 64 * 31>; | |
| var<workgroup> Xs: array<f32, 2 * 94>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) { | |
| let g = wg.x / 16u; let pt = wg.x % 16u; | |
| let p0 = pt * 64u; | |
| let b = wg.y; | |
| let tx = tid % 16u; let ty = tid / 16u; | |
| let c0 = g * 64u + ty * 4u; | |
| var acc: array<f32, 16>; | |
| for (var i = 0u; i < 16u; i++) { acc[i] = 0.0; } | |
| for (var cc = 0u; cc < 32u; cc++) { | |
| let ci0 = cc * 2u; | |
| for (var i = tid; i < 2u * 64u * 31u; i += 256u) { | |
| let k = i % 31u; let oc = (i / 31u) % 64u; let ic = i / (31u * 64u); | |
| Ws[i] = Wt[((g * 64u + oc) * 64u + (ci0 + ic)) * 31u + k]; | |
| } | |
| for (var i = tid; i < 2u * 94u; i += 256u) { | |
| let ic = i / 94u; let pp = i % 94u; | |
| let pos = i32(p0) - 15 + i32(pp); | |
| var v: f32 = 0.0; | |
| if (pos >= 0 && pos < i32(p.N)) { v = f32(X[(b * p.N + u32(pos)) * 1024u + g * 64u + ci0 + ic]); } | |
| Xs[i] = v; | |
| } | |
| workgroupBarrier(); | |
| for (var ic = 0u; ic < 2u; ic++) { | |
| for (var k = 0u; k < 31u; k++) { | |
| let x0 = Xs[ic * 94u + tx * 4u + k]; | |
| let x1 = Xs[ic * 94u + tx * 4u + 1u + k]; | |
| let x2 = Xs[ic * 94u + tx * 4u + 2u + k]; | |
| let x3 = Xs[ic * 94u + tx * 4u + 3u + k]; | |
| let w0 = f32(Ws[(ic * 64u + ty * 4u + 0u) * 31u + k]); | |
| let w1 = f32(Ws[(ic * 64u + ty * 4u + 1u) * 31u + k]); | |
| let w2 = f32(Ws[(ic * 64u + ty * 4u + 2u) * 31u + k]); | |
| let w3 = f32(Ws[(ic * 64u + ty * 4u + 3u) * 31u + k]); | |
| acc[0] += x0 * w0; acc[1] += x0 * w1; acc[2] += x0 * w2; acc[3] += x0 * w3; | |
| acc[4] += x1 * w0; acc[5] += x1 * w1; acc[6] += x1 * w2; acc[7] += x1 * w3; | |
| acc[8] += x2 * w0; acc[9] += x2 * w1; acc[10] += x2 * w2; acc[11] += x2 * w3; | |
| acc[12] += x3 * w0; acc[13] += x3 * w1; acc[14] += x3 * w2; acc[15] += x3 * w3; | |
| } | |
| } | |
| workgroupBarrier(); | |
| } | |
| for (var oc = 0u; oc < 4u; oc++) { | |
| for (var j = 0u; j < 4u; j++) { | |
| let pos = p0 + tx * 4u + j; | |
| if (pos < p.N) { | |
| Y[(b * p.N + pos) * 1024u + c0 + oc] = f16(clamp(acc[j * 4u + oc] + f32(Bs[c0 + oc]), -60000.0, 60000.0)); | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const FLOW = ` | |
| struct P { total: u32, N: u32, B: u32, pad1: u32, dt: f32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read_write> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read> V: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| if (gid.x >= p.total) { return; } | |
| let i = gid.x; | |
| let d = i % 100u; let f = i / 100u; | |
| let v0 = f32(V[f * 100u + d]); | |
| var v = v0; | |
| if (p.B == 2u) { let v1 = f32(V[(p.N + f) * 100u + d]); v = v0 + (v0 - v1) * 2.0; } | |
| X[i] = f16(f32(X[i]) + p.dt * v); | |
| } | |
| `; | |
| const TEB = ` | |
| struct P { N: u32, B: u32, pad0: u32, pad1: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> T: array<f16>; | |
| @group(0) @binding(2) var<storage, read_write> O: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| let i = gid.x; | |
| if (i >= p.B * p.N * 512u) { return; } | |
| let row = i / 512u; let d = i % 512u; | |
| if (row < p.N) { O[i] = T[row * 512u + d]; } else { O[i] = f16(0.0); } | |
| } | |
| `; | |
| const COPY = ` | |
| struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; | |
| @group(0) @binding(0) var<uniform> p: P; | |
| @group(0) @binding(1) var<storage, read> X: array<f16>; | |
| @group(0) @binding(2) var<storage, read_write> Y: array<f16>; | |
| @compute @workgroup_size(256) | |
| fn main(@builtin(global_invocation_id) gid: vec3<u32>) { | |
| if (gid.x >= p.total) { return; } | |
| Y[gid.x] = X[gid.x]; | |
| } | |
| `; | |
| export const KERNELS = { | |
| gemm: GEMM, gemv: GEMV, lnm: LNM, lna: LNA, rope: ROPE, qk: QK, softmax: SOFTMAX, | |
| av: AV, addgate: ADDGATE, gelu: GELU, silu: SILU, mish: MISH, concat3: CONCAT3, | |
| gather: GATHER, dwconv: DWCONV, grnred: GRNRED, grnmean: GRNMEAN, grnapply: GRNAPPLY, | |
| addres: ADDRES, zerofill: ZEROFILL, convpos: CONVPOS, flow: FLOW, teb: TEB, copy: COPY, | |
| }; | |
| function f16tof32(h) { | |
| const sign = (h & 0x8000) ? -1 : 1, exp = (h >> 10) & 0x1f, frac = h & 0x3ff; | |
| if (exp === 0) return sign * Math.pow(2, -14) * (frac / 1024); | |
| if (exp === 31) return frac ? NaN : sign * Infinity; | |
| return sign * Math.pow(2, exp - 15) * (1 + frac / 1024); | |
| } | |
| export class F5Engine { | |
| static async create(device, opts = {}) { | |
| const e = new F5Engine(device, opts); | |
| await e._init(); | |
| return e; | |
| } | |
| constructor(device, opts) { this.device = device; this.opts = opts; this.f32 = opts.dtype === 'f32'; this.es = this.f32 ? 4 : 2; } | |
| async _init() { | |
| const dev = this.device; | |
| const [mr, br] = await Promise.all([ | |
| fetch(this.opts.metaUrl || '/webgpu/f5w.json'), | |
| fetch(this.opts.binUrl || '/webgpu/f5w.bin'), | |
| ]); | |
| this.meta = await mr.json(); | |
| this.binSize = Number(br.headers.get('content-length')) || 0; | |
| this.wsize = Math.ceil((this.f32 ? this.binSize * 2 : this.binSize) / 256) * 256; | |
| this._planChunks(); | |
| this.wbufs = this.chunkStarts.map((st, i) => { | |
| const end = i + 1 < this.chunkStarts.length ? this.chunkStarts[i + 1] : this.wsize; | |
| return dev.createBuffer({ size: Math.ceil((end - st) / 256) * 256, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST }); | |
| }); | |
| await this._streamWeights(br); | |
| this.ubufs = []; | |
| this.ubNext = 0; | |
| this.compileErrors = []; | |
| const cre = (name, code) => { | |
| if (this.f32) code = code.replace(/enable f16;\n/g, '').replace(/\bf16\b/g, 'f32'); | |
| const mod = dev.createShaderModule({ code: (this.f32 ? '' : PRELUDE) + code }); | |
| mod.getCompilationInfo().then((ci) => { | |
| for (const m of ci.messages) if (m.type === 'error') this.compileErrors.push(name + ': ' + m.message + ' @' + m.lineNum); | |
| }); | |
| return dev.createComputePipelineAsync({ layout: 'auto', compute: { module: mod, entryPoint: 'main' } }); | |
| }; | |
| this.gemmKind = this.opts.gemm || (this.device.features.has('chromium-experimental-subgroup-matrix') ? 'sgmat' : 'simt'); | |
| console.log('DBG gemmKind', this.gemmKind, 'opts', JSON.stringify(Object.keys(this.opts || {}))); | |
| const defs = { | |
| gemm: this.gemmKind === 'sgmat' ? GEMM : GEMM_SIMT, gemm_i4: GEMM_SIMT_I4, gemv: GEMV, gemv_i4: GEMV_I4, lnm: LNM, lna: LNA, rope: ROPE, | |
| qk: QK, softmax: SOFTMAX, av: AV, addgate: ADDGATE, gelu: GELU, | |
| silu: SILU, mish: MISH, concat3: CONCAT3, gather: GATHER, | |
| dwconv: DWCONV, grnred: GRNRED, grnmean: GRNMEAN, | |
| grnapply: GRNAPPLY, addres: ADDRES, zerofill: ZEROFILL, | |
| convpos: CONVPOS, flow: FLOW, teb: TEB, copy: COPY, | |
| }; | |
| this.pipe = {}; | |
| for (const [name, code] of Object.entries(defs)) { | |
| this.pipe[name] = await cre(name, code); | |
| } | |
| this.pipeNames = new Map(Object.entries(this.pipe).map(([k, v]) => [v, k])); | |
| } | |
| _planChunks() { | |
| const mult = this.f32 ? 2 : 1; | |
| const ents = Object.entries(this.meta).map(([n, t]) => { | |
| if (t.q) return { n, off: t.packed * mult, len: (t.scales + t.scale_bytes) - t.packed }; | |
| const len = Math.ceil(t.shape.reduce((a, b) => a * b, 1) * this.es / 4) * 4; | |
| return { n, off: t.offset * mult, len }; | |
| }).sort((a, b) => a.off - b.off); | |
| const CHUNK = 640 << 20; | |
| const starts = [0]; | |
| let cur = 0; | |
| this.bufOf = new Map(); | |
| for (const e of ents) { | |
| if (cur > 0 && cur + e.len > CHUNK) { starts.push(e.off); cur = 0; } | |
| this.bufOf.set(e.n, starts.length - 1); | |
| cur = e.off + e.len - starts[starts.length - 1]; | |
| } | |
| this.chunkStarts = starts; | |
| } | |
| _writeAt(globalOff, data, byteLen) { | |
| let off = globalOff, src = 0, remain = byteLen; | |
| while (remain > 0) { | |
| let bi = this.chunkStarts.length - 1; | |
| for (let i = 0; i < this.chunkStarts.length; i++) { | |
| const end = i + 1 < this.chunkStarts.length ? this.chunkStarts[i + 1] : this.wsize; | |
| if (off < end) { bi = i; break; } | |
| } | |
| const base = this.chunkStarts[bi]; | |
| const bo = off - base; | |
| const end = bi + 1 < this.chunkStarts.length ? this.chunkStarts[bi + 1] : this.wsize; | |
| const n = Math.min(remain, end - off); | |
| if (data instanceof Float32Array) { | |
| this.device.queue.writeBuffer(this.wbufs[bi], bo, data.subarray(src / 4, (src + n) / 4)); | |
| } else { | |
| this.device.queue.writeBuffer(this.wbufs[bi], bo, data.subarray(src, src + n)); | |
| } | |
| off += n; src += n; remain -= n; | |
| } | |
| } | |
| async _streamWeights(res) { | |
| // Stream the bundle to the GPU in ~32 MB slices; convert f16 -> f32 if needed. | |
| const dev = this.device; | |
| const reader = res.body.getReader(); | |
| const CH = 32 << 20; | |
| let stage = new Uint8Array(CH + 8); | |
| let staged = 0, gpuOff = 0; | |
| const flush = async (final) => { | |
| if (!staged) return; | |
| let n = staged; | |
| const extra = final ? (4 - (n % 4)) % 4 : 0; | |
| if (extra) { stage.fill(0, n, n + extra); n += extra; } | |
| if (this.f32) { | |
| const elems = n >> 1; | |
| const out = new Float32Array(elems); | |
| out.set(new Float16Array(stage.buffer, 0, elems)); | |
| this._writeAt(gpuOff, out, elems * 4); | |
| gpuOff += elems * 4; | |
| } else { | |
| this._writeAt(gpuOff, stage.subarray(0, n), n); | |
| gpuOff += n; | |
| } | |
| staged = 0; | |
| if (this.opts.onProgress) this.opts.onProgress(gpuOff / (this.f32 ? this.binSize * 2 : this.binSize)); | |
| }; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| let off = 0; | |
| while (off < value.length) { | |
| const take = Math.min(value.length - off, stage.length - staged); | |
| stage.set(value.subarray(off, off + take), staged); | |
| staged += take; off += take; | |
| if (staged >= CH) await flush(false); | |
| } | |
| } | |
| await flush(true); | |
| const pad = Math.ceil(gpuOff / 256) * 256 - gpuOff; | |
| if (pad > 0) this._writeAt(gpuOff, new Uint8Array(pad), pad); | |
| } | |
| _t(name) { const t = this.meta[name]; if (!t) throw new Error('missing ' + name); return t; } | |
| _wslice(name) { | |
| const t = this._t(name); | |
| const n = t.shape.reduce((a, b) => a * b, 1); | |
| const mult = this.f32 ? 2 : 1; | |
| const bi = this.bufOf.get(name); | |
| const base = this.chunkStarts[bi]; | |
| return { buffer: this.wbufs[bi], offset: t.offset * mult - base, size: Math.ceil(n * this.es / 4) * 4 }; | |
| } | |
| _alc(bytes) { | |
| const size = Math.ceil(bytes / 256) * 256; | |
| return this.device.createBuffer({ size, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC }); | |
| } | |
| _ub(vals, floats) { | |
| const ab = new ArrayBuffer(256); | |
| const dv = new DataView(ab); | |
| for (let i = 0; i < vals.length; i++) { | |
| if (floats && floats.indexOf(i) >= 0) dv.setFloat32(i * 4, vals[i], true); | |
| else dv.setUint32(i * 4, vals[i] >>> 0, true); | |
| } | |
| const buf = this.device.createBuffer({ size: 256, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); | |
| this.device.queue.writeBuffer(buf, 0, ab); | |
| this.ubNext++; | |
| this.ubufs.push(buf); | |
| return { buffer: buf, offset: 0, size: 256 }; | |
| } | |
| _bg(pipe, u, bufs) { | |
| const wrap = (b) => (b && b.buffer) ? b : { buffer: b, offset: 0, size: b.size }; | |
| return this.device.createBindGroup({ | |
| layout: pipe.getBindGroupLayout(0), | |
| entries: [{ binding: 0, resource: wrap(u) }, ...bufs.map((b, i) => ({ binding: i + 1, resource: wrap(b) }))], | |
| }); | |
| } | |
| setup(N) { | |
| const dev = this.device; | |
| this.N = N; this.B = this.opts.branches || 2; this.M2 = this.B * N; | |
| const A = (name, elems) => (this[name] = this._alc(elems * this.es)); | |
| A('xBuf', N * 100); A('condBuf', N * 100); | |
| A('teBuf', N * 512); A('tebBuf', 2 * N * 512); A('catBuf', 2 * N * 712); | |
| A('hBuf', 2 * N * 1024); A('tmpBuf', 2 * N * 1024); A('qBuf', 2 * N * 1024); | |
| A('kBuf', 2 * N * 1024); A('vBuf', 2 * N * 1024); A('attnBuf', 2 * N * 1024); | |
| A('ffBuf', 2 * N * 2048); A('velBuf', 2 * N * 100); | |
| A('timeIn', 256); A('tembBuf', 1024); A('temb2Buf', 1024); | |
| A('modBuf', 137216); this.scoresBuf = this._alc(this.B * 16 * N * N * 4); | |
| A('tBuf', N * 1024); A('tBuf2', N * 1024); A('tBuf3', N * 2048); | |
| A('tnmBuf', 1024 * 4); A('tmeanBuf', 4); | |
| this.idsBuf = this._alc(N * 4); | |
| this.ubNext = 0; | |
| this.ropeFreq = dev.createBuffer({ size: 256, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST }); | |
| const f = new Float32Array(64); | |
| for (let j = 0; j < 32; j++) f[j] = Math.pow(10000, -j / 32); | |
| dev.queue.writeBuffer(this.ropeFreq, 0, f); | |
| this.calls = []; | |
| this._buildStepCalls(); | |
| const stepCalls = this.calls; | |
| this.calls = []; | |
| this._buildTextCalls(); | |
| this.textCalls = this.calls; | |
| this.calls = stepCalls; | |
| } | |
| _add(pipe, bg, x, y = 1, z = 1, meta) { | |
| const c = { pipe, bg, x: Math.ceil(x), y, z, name: this.pipeNames.get(pipe), meta }; | |
| this.calls.push(c); | |
| if (this.debug) this.debug.push(c); | |
| return c; | |
| } | |
| _qdesc(name) { | |
| const t = this._t(name); | |
| const mult = this.f32 ? 2 : 1; | |
| const bi = this.bufOf.get(name); | |
| const base = this.chunkStarts[bi]; | |
| return { | |
| packed: { buffer: this.wbufs[bi], offset: t.packed * mult - base, size: t.packed_bytes }, | |
| scales: { buffer: this.wbufs[bi], offset: t.scales * mult - base, size: t.scale_bytes }, | |
| group: t.group, | |
| }; | |
| } | |
| _gemm(A, wn, bn, C, M, K, N) { | |
| const t = this._t(wn); | |
| if (t.q) { | |
| if (!(M > 0 && N > 0 && K > 0 && M < 1e5 && N < 1e6 && K < 1e6)) throw new Error(`bad gemm args ${M},${K},${N} (${wn})`); | |
| const q = this._qdesc(wn); | |
| if (q.packed.offset + q.packed.size > q.packed.buffer.size) throw new Error(`q4 packed OOB ${wn}`); | |
| const u = this._ub([M, N, K, q.group]); | |
| this._add(this.pipe.gemm_i4, this._bg(this.pipe.gemm_i4, u, [A, q.packed, q.scales, this._wslice(bn), C]), | |
| Math.ceil(M / 64), Math.ceil(N / 64)); | |
| return; | |
| } | |
| const wa = this._wslice(wn), ba = this._wslice(bn); | |
| if (!(M > 0 && N > 0 && K > 0 && M < 1e5 && N < 1e6 && K < 1e6)) throw new Error(`bad gemm args ${M},${K},${N} (${wn})`); | |
| if (wa.offset + wa.size > wa.buffer.size) throw new Error(`gemm weight OOB ${wn}`); | |
| if (ba.offset + ba.size > ba.buffer.size) throw new Error(`gemm bias OOB ${bn}`); | |
| const u = this._ub([M, N, K, 0]); | |
| this._add(this.pipe.gemm, this._bg(this.pipe.gemm, u, [A, wa, ba, C]), Math.ceil(M / (this.gemmKind === 'sgmat' ? 128 : 64)), Math.ceil(N / 64), 1, | |
| { vals: [M, N, K], wa: [wa.offset, wa.size, wa.buffer.size], ba: [ba.offset, ba.size, ba.buffer.size] }); | |
| } | |
| _gemv(A, wn, bn, C, K, N) { | |
| const t = this._t(wn); | |
| if (t.q) { | |
| const q = this._qdesc(wn); | |
| const u = this._ub([N, K, q.group, 0]); | |
| this._add(this.pipe.gemv_i4, this._bg(this.pipe.gemv_i4, u, [A, q.packed, q.scales, this._wslice(bn), C]), Math.ceil(N / 1024)); | |
| return; | |
| } | |
| const u = this._ub([N, K, 0, 0]); | |
| this._add(this.pipe.gemv, this._bg(this.pipe.gemv, u, [A, this._wslice(wn), this._wslice(bn), C]), Math.ceil(N / 1024)); | |
| } | |
| _elem(pipe, X, total) { | |
| const u = this._ub([total, 0, 0, 0]); | |
| this._add(pipe, this._bg(pipe, u, [X]), Math.ceil(total / 256)); | |
| } | |
| _buildStepCalls() { | |
| const N = this.N, M2 = this.M2; | |
| this._gemm(this.timeIn, 'time.0', 'time.0.b', this.tembBuf, 1, 256, 1024); | |
| this._elem(this.pipe.silu, this.tembBuf, 1024); | |
| this._gemm(this.tembBuf, 'time.2', 'time.2.b', this.temb2Buf, 1, 1024, 1024); | |
| this._elem(this.pipe.silu, this.temb2Buf, 1024); | |
| this._gemv(this.temb2Buf, 'ada.w', 'ada.b', this.modBuf, 1024, 137216); | |
| this._add(this.pipe.concat3, this._bg(this.pipe.concat3, this._ub([N, this.B, 0, 0]), [this.xBuf, this.condBuf, this.tebBuf, this.catBuf]), Math.ceil(this.B * N * 712 / 256)); | |
| this._gemm(this.catBuf, 'in.proj', 'in.proj.b', this.hBuf, M2, 712, 1024); | |
| const cp = (X, wn, bn, Y) => { | |
| const u = this._ub([N, M2, 0, 0]); | |
| this._add(this.pipe.convpos, this._bg(this.pipe.convpos, u, [X, this._wslice(wn), this._wslice(bn), Y]), 16 * 16, this.B); | |
| }; | |
| cp(this.hBuf, 'convpos.0.w', 'convpos.0.b', this.tmpBuf); | |
| this._elem(this.pipe.mish, this.tmpBuf, M2 * 1024); | |
| cp(this.tmpBuf, 'convpos.2.w', 'convpos.2.b', this.qBuf); | |
| this._elem(this.pipe.mish, this.qBuf, M2 * 1024); | |
| { | |
| const u = this._ub([M2 * 1024, 0, 0, 0]); | |
| this._add(this.pipe.addres, this._bg(this.pipe.addres, u, [this.hBuf, this.qBuf]), Math.ceil(M2 * 1024 / 256)); | |
| } | |
| for (let L = 0; L < 22; L++) { | |
| const base = L * 6144; | |
| { | |
| const u = this._ub([M2, 1024, base + 1024, base]); | |
| this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2); | |
| } | |
| this._gemm(this.tmpBuf, `b${L}.q`, `b${L}.q.b`, this.qBuf, M2, 1024, 1024); | |
| this._gemm(this.tmpBuf, `b${L}.k`, `b${L}.k.b`, this.kBuf, M2, 1024, 1024); | |
| this._gemm(this.tmpBuf, `b${L}.v`, `b${L}.v.b`, this.vBuf, M2, 1024, 1024); | |
| for (const buf of [this.qBuf, this.kBuf]) { | |
| const u = this._ub([M2, N, M2 * 16 * 32, 0]); | |
| this._add(this.pipe.rope, this._bg(this.pipe.rope, u, [this.ropeFreq, buf]), Math.ceil(M2 * 16 * 32 / 256)); | |
| } | |
| { | |
| const u = this._ub([N, 0, 0, 0]); | |
| this._add(this.pipe.qk, this._bg(this.pipe.qk, u, [this.qBuf, this.kBuf, this.scoresBuf]), Math.ceil(N / 64), Math.ceil(N / 64), this.B * 16); | |
| const u2 = this._ub([N, this.B * 16 * N, 0, 0]); | |
| this._add(this.pipe.softmax, this._bg(this.pipe.softmax, u2, [this.scoresBuf]), this.B * 16 * N); | |
| const u3 = this._ub([N, 0, 0, 0]); | |
| this._add(this.pipe.av, this._bg(this.pipe.av, u3, [this.scoresBuf, this.vBuf, this.attnBuf]), Math.ceil(N / 64), 1, this.B * 16); | |
| } | |
| this._gemm(this.attnBuf, `b${L}.o`, `b${L}.o.b`, this.tmpBuf, M2, 1024, 1024); | |
| { | |
| const u = this._ub([M2, 1024, base + 2048, 0]); | |
| this._add(this.pipe.addgate, this._bg(this.pipe.addgate, u, [this.hBuf, this.tmpBuf, this.modBuf]), Math.ceil(M2 * 1024 / 256)); | |
| } | |
| { | |
| const u = this._ub([M2, 1024, base + 4096, base + 3072]); | |
| this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2); | |
| } | |
| this._gemm(this.tmpBuf, `b${L}.up`, `b${L}.up.b`, this.ffBuf, M2, 1024, 2048); | |
| this._elem(this.pipe.gelu, this.ffBuf, M2 * 2048); | |
| this._gemm(this.ffBuf, `b${L}.down`, `b${L}.down.b`, this.tmpBuf, M2, 2048, 1024); | |
| { | |
| const u = this._ub([M2, 1024, base + 5120, 0]); | |
| this._add(this.pipe.addgate, this._bg(this.pipe.addgate, u, [this.hBuf, this.tmpBuf, this.modBuf]), Math.ceil(M2 * 1024 / 256)); | |
| } | |
| } | |
| const fbase = 22 * 6144; | |
| { | |
| const u = this._ub([M2, 1024, fbase, fbase + 1024]); | |
| this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2); | |
| } | |
| this._gemm(this.tmpBuf, 'out.w', 'out.b', this.velBuf, M2, 1024, 100); | |
| this.flowUniform = this._ub([N * 100, N, this.B, 0]); | |
| this._add(this.pipe.flow, this._bg(this.pipe.flow, this.flowUniform, [this.xBuf, this.velBuf]), Math.ceil(N * 100 / 256)); | |
| } | |
| _buildTextCalls() { | |
| const N = this.N; | |
| { | |
| const u = this._ub([N, 0, 0, 0]); | |
| this._add(this.pipe.gather, this._bg(this.pipe.gather, u, [this.idsBuf, this._wslice('txt.emb'), this._wslice('txt.freqs'), this.teBuf]), Math.ceil(N * 512 / 256)); | |
| } | |
| for (let b = 0; b < 4; b++) { | |
| { | |
| const u = this._ub([N, 512, 0, 0]); | |
| this._add(this.pipe.dwconv, this._bg(this.pipe.dwconv, u, [this.teBuf, this._wslice(`tb${b}.dw.w`), this._wslice(`tb${b}.dw.b`), this.tBuf]), Math.ceil(N * 512 / 256)); | |
| } | |
| { | |
| const u = this._ub([N, 512, 0, 0, 1e-5, 0, 0, 0], [4]); | |
| this._add(this.pipe.lna, this._bg(this.pipe.lna, u, [this.tBuf, this._wslice(`tb${b}.ln.w`), this._wslice(`tb${b}.ln.b`), this.tBuf3]), N); | |
| } | |
| this._gemm(this.tBuf3, `tb${b}.p1`, `tb${b}.p1.b`, this.tBuf2, N, 512, 1024); | |
| this._elem(this.pipe.gelu, this.tBuf2, N * 1024); | |
| { | |
| const u = this._ub([N, 1024, 0, 0]); | |
| this._add(this.pipe.grnred, this._bg(this.pipe.grnred, u, [this.tBuf2, this.tnmBuf]), 1024); | |
| const u2 = this._ub([1024, 0, 0, 0]); | |
| this._add(this.pipe.grnmean, this._bg(this.pipe.grnmean, u2, [this.tnmBuf, this.tmeanBuf]), 1); | |
| const u3 = this._ub([N, 1024, 0, 0]); | |
| this._add(this.pipe.grnapply, this._bg(this.pipe.grnapply, u3, [this.tBuf2, this.tBuf, this._wslice(`tb${b}.grn.g`), this._wslice(`tb${b}.grn.b`), this.tnmBuf, this.tmeanBuf]), Math.ceil(N * 1024 / 256)); | |
| } | |
| this._gemm(this.tBuf, `tb${b}.p2`, `tb${b}.p2.b`, this.tBuf2, N, 1024, 512); | |
| { | |
| const u = this._ub([N * 512, 0, 0, 0]); | |
| this._add(this.pipe.addres, this._bg(this.pipe.addres, u, [this.tBuf2, this.teBuf]), Math.ceil(N * 512 / 256)); | |
| const u2 = this._ub([N, 512, 0, 0]); | |
| this._add(this.pipe.zerofill, this._bg(this.pipe.zerofill, u2, [this.idsBuf, this.tBuf2]), Math.ceil(N * 512 / 256)); | |
| const u3 = this._ub([N * 512, 0, 0, 0]); | |
| this._add(this.pipe.copy, this._bg(this.pipe.copy, u3, [this.tBuf2, this.teBuf]), Math.ceil(N * 512 / 256)); | |
| } | |
| } | |
| { | |
| const u = this._ub([N, this.B, 0, 0]); | |
| this._add(this.pipe.teb, this._bg(this.pipe.teb, u, [this.teBuf, this.tebBuf]), Math.ceil(this.B * N * 512 / 256)); | |
| } | |
| } | |
| textNames() { return this.textCalls.map((c) => c.name); } | |
| async testGemm2(M, K, N, mode) { | |
| const dev = this.device; | |
| const mk = (bytes) => dev.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC }); | |
| const A = mode === 'dedicated' ? mk(M * K * 2) : this.tBuf3; | |
| const C = mode === 'dedicated' ? mk(M * N * 2) : this.tBuf2; | |
| const W = mode === 'dedicated' ? { buffer: mk(K * N * 2), offset: 0, size: K * N * 2 } : this._wslice('tb0.p1'); | |
| const Bs = mode === 'dedicated' ? { buffer: mk(N * 2), offset: 0, size: N * 2 } : this._wslice('tb0.p1.b'); | |
| const u = this._ub([M, N, K, 0]); | |
| const bg = this._bg(this.pipe.gemm, u, [A, W, Bs, C]); | |
| this.encodeCalls([{ pipe: this.pipe.gemm, bg, x: Math.ceil(M / 128), y: Math.ceil(N / 64), z: 1 }]); | |
| await this.device.queue.onSubmittedWorkDone(); | |
| return mode + ' ok'; | |
| } | |
| async testGemm(M, K, N) { | |
| const u = this._ub([M, N, K, 0]); | |
| const bg = this._bg(this.pipe.gemm, u, [this.tBuf3, this._wslice('tb0.p1'), this._wslice('tb0.p1.b'), this.tBuf2]); | |
| this.encodeCalls([{ pipe: this.pipe.gemm, bg, x: Math.ceil(M / 64), y: Math.ceil(N / 64), z: 1 }]); | |
| await this.device.queue.onSubmittedWorkDone(); | |
| return M + 'x' + K + 'x' + N; | |
| } | |
| debugText() { return this.textCalls.map((c) => ({ name: c.name, x: c.x, y: c.y, meta: c.meta })); } | |
| async runTextPrefix(k) { | |
| this.encodeCalls(this.textCalls.slice(0, k)); | |
| await this.device.queue.onSubmittedWorkDone(); | |
| return k; | |
| } | |
| async runOne(list, k) { | |
| this.encodeCalls([list[k]]); | |
| await this.device.queue.onSubmittedWorkDone(); | |
| return list[k].name; | |
| } | |
| async setCond(condMelF32, ids, limit) { | |
| const dev = this.device, N = this.N; | |
| const ca = this.f32 ? new Float32Array(N * 100) : new Float16Array(N * 100); | |
| ca.set(condMelF32.subarray(0, N * 100)); | |
| dev.queue.writeBuffer(this.condBuf, 0, ca); | |
| const idarr = new Int32Array(N).fill(-1); | |
| for (let i = 0; i < Math.min(ids.length, N); i++) idarr[i] = ids[i]; | |
| dev.queue.writeBuffer(this.idsBuf, 0, idarr); | |
| const list = (limit === undefined) ? this.textCalls : this.textCalls.slice(0, limit); | |
| if (list.length) this.encodeCalls(list); | |
| if (limit !== 0) await dev.queue.onSubmittedWorkDone(); | |
| } | |
| setX(xF32) { | |
| const f = this.f32 ? new Float32Array(this.N * 100) : new Float16Array(this.N * 100); | |
| f.set(xF32.subarray(0, this.N * 100)); | |
| this.device.queue.writeBuffer(this.xBuf, 0, f); | |
| } | |
| encodeCalls(list) { | |
| const enc = this.device.createCommandEncoder(); | |
| const p = enc.beginComputePass(); | |
| for (const c of list) { | |
| p.setPipeline(c.pipe); | |
| p.setBindGroup(0, c.bg); | |
| p.dispatchWorkgroups(c.x, c.y, c.z); | |
| } | |
| p.end(); | |
| this.device.queue.submit([enc.finish()]); | |
| } | |
| writeTime(t) { | |
| const half = 128; | |
| const tb = this.f32 ? new Float32Array(256) : new Float16Array(256); | |
| for (let j = 0; j < 128; j++) { | |
| const fr = Math.exp(-Math.log(10000) / (half - 1) * j); | |
| const a = t * 1000 * fr; | |
| tb[j] = Math.sin(a); tb[128 + j] = Math.cos(a); | |
| } | |
| this.device.queue.writeBuffer(this.timeIn, 0, tb); | |
| } | |
| async step(t, dt) { | |
| this.writeTime(t); | |
| this.device.queue.writeBuffer(this.flowUniform.buffer, this.flowUniform.offset + 16, new Float32Array([dt])); | |
| this.encodeCalls(this.calls); | |
| await this.device.queue.onSubmittedWorkDone(); | |
| } | |
| async runStepPrefix(k, t) { | |
| this.writeTime(t === undefined ? 0.5 : t); | |
| this.encodeCalls(this.calls.slice(0, k)); | |
| await this.device.queue.onSubmittedWorkDone(); | |
| } | |
| async readBuf(buf, elems) { | |
| const bytes = elems * this.es; | |
| const staging = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST }); | |
| const enc = this.device.createCommandEncoder(); | |
| enc.copyBufferToBuffer(buf, 0, staging, 0, bytes); | |
| this.device.queue.submit([enc.finish()]); | |
| await staging.mapAsync(GPUMapMode.READ); | |
| const ab = staging.getMappedRange().slice(0); | |
| staging.unmap(); | |
| return this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab)); | |
| } | |
| async readVel() { | |
| const bytes = this.M2 * 100 * this.es; | |
| const buf = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST }); | |
| const enc = this.device.createCommandEncoder(); | |
| enc.copyBufferToBuffer(this.velBuf, 0, buf, 0, bytes); | |
| this.device.queue.submit([enc.finish()]); | |
| await buf.mapAsync(GPUMapMode.READ); | |
| const ab = buf.getMappedRange().slice(0); | |
| const out = this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab)); | |
| buf.unmap(); | |
| return out; | |
| } | |
| async readX() { | |
| const bytes = this.N * 100 * this.es; | |
| const buf = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST }); | |
| const enc = this.device.createCommandEncoder(); | |
| enc.copyBufferToBuffer(this.xBuf, 0, buf, 0, bytes); | |
| this.device.queue.submit([enc.finish()]); | |
| await buf.mapAsync(GPUMapMode.READ); | |
| const ab = buf.getMappedRange().slice(0); | |
| const out = this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab)); | |
| buf.unmap(); | |
| return out; | |
| } | |
| } | |