WebGPU-oriented AI inference foundation library: WebGPU runtime + WGSL kernel library, 4D batched FlashAttention (MHA/GQA/MQA, causal, cross-attention) on wasm/native/WebGPU, with verified numerics.
Dependencies
moon add starAndHonor/flashmoon| Import | What you get | Targets |
|---|---|---|
| starAndHonor/flashmoon/flash | 4D batched attention (MHA/GQA/MQA, causal, cross-attention, any head dim) + naive oracle | wasm (f32x4 SIMD), native, js |
| starAndHonor/flashmoon/flash/gpu | the same 4D contract, dispatched on WebGPU | js |
| starAndHonor/flashmoon/gpu | WebGPU runtime (device, pipeline cache, buffers, submit) + the WGSL kernel library | js |
q [B, H, Sq, D ] out [B, H, Sq, DV]
k [B, HKV, Skv, D ]
v [B, HKV, Skv, DV] out[b,h,i,:] = softmax(q[b,h,i,:] · k[b,kvh,:,:]ᵀ · scale) · v[b,kvh,:,:]import { "starAndHonor/flashmoon/flash" }
let cfg = @flash.AttnConfig::new(
heads=8, // query heads H
kv_heads=2, // key/value heads HKV: H -> MHA, H/n -> GQA, 1 -> MQA
causal=true, // bottom-right-aligned causal mask
scale=None, // None = 1/sqrt(D); Some(x) to override
)
let out = @flash.flash_attention(q, k, v, cfg, block_rows=64, block_cols=64)| API | Notes |
|---|---|
| AttnConfig::new(heads~, kv_heads?=heads, causal?=false, scale?=None) | kv_heads must divide heads; aborts otherwise |
| flash_attention(q, k, v, cfg, block_rows?=64, block_cols?=64) | tiled online softmax; returns a new NpArray |
| naive_attention(q, k, v, cfg) | materializes the score matrix; same result, used as the test oracle |
import {
"starAndHonor/flashmoon/flash",
"starAndHonor/flashmoon/flash/gpu" @flashgpu,
"starAndHonor/flashmoon/gpu",
}
@gpu.Gpu::init(fn(g) {
@flashgpu.flash_attention(g, q, k, v, cfg, fn(out) {
// out : NpArray [B, H, Sq, DV]
})
}, log=println)let qb = g.upload(q_data) // FixedArray[Float] -> GPUBuffer
let outb = g.alloc(b * h * sq * dv) // f32 storage buffer
let scale = 1.0 / d.to_double().sqrt()
g.attn_4d(qb, kb, vb, outb, b, h, hkv, sq, skv, d, dv, /* causal */ true, scale)
g.sync(fn(_) { ... }) // wait for the queue
g.readback(outb, n, fn(data) { ... }) // GPUBuffer -> FixedArray[Float]| Kernel | Purpose |
|---|---|
| attn_4d / attn_naive_4d | 4D flash attention / materialized-score baseline |
| attn_prefill / attn_decode | runner-shaped attention: prefill + split-KV flash-decoding |
| matvec, matvec_silu, gemv2_fused | bf16 GEMV (weights read from storage buffers in place) |
| matmul | f32 GEMM |
| rmsnorm, add_rmsnorm | normalization (optionally fused with a residual add) |
| rope, qknorm_rope | RoPE, optionally fused with QK-norm and KV-cache write |
| silu_mul, add, embed_rows | elementwise / embedding gather |
| argmax | full-logit GPU argmax (greedy decode without a logits readback) |
| copy, set_u32, alloc_u32, upload_u32 | plumbing (offsets, parameters, splits) |
| Command | Shows |
|---|---|
| moon run examples/attn_cpu --target wasm | 4D GQA + causal on the CPU, flash vs naive |
| moon build --target js && deno run --allow-read scripts/attn_gpu_host.js | the same on WebGPU (flash/gpu wrapper and device-level attn_4d), checked against the CPU oracle |
| moon run cmd/fa --target wasm | slightly larger demo, prints both outputs |
| moon run bench --target wasm --release | naive-vs-flash scenario suite + tile/length/head-dim sweeps |
| moon build --target js && deno run --allow-read scripts/bench_gpu_host.js | naive-vs-flash on the GPU |
| moon build --target js && deno run --allow-read scripts/webgpu_host.js | per-kernel correctness + throughput checks |
| Scenario | Shape (B×H/Hkv Sq×Skv×D) | naive | flash | speedup | max diff |
|---|---|---|---|---|---|
| chat prompt | 1×8/8 256×256×64 | 1.38 ms | 0.99 ms | 1.40× | 5.4e-7 |
| long prefill | 1×8/8 2048×2048×64 | 22.38 ms | 16.40 ms | 1.36× | 1.6e-6 |
| decode step (KV 2k) | 1×8/8 1×2048×64 | 3.31 ms | 1.38 ms | 2.41× | 1.4e-6 |
| GQA decode (KV 4k) | 1×8/2 1×4096×64 | 6.37 ms | 2.52 ms | 2.52× | 3.6e-6 |
| MQA decode (KV 4k) | 1×8/1 1×4096×64 | 5.31 ms | 2.52 ms | 2.11× | 3.7e-6 |
| batched prefill b4 | 4×8/8 256×256×64 | 4.51 ms | 3.85 ms | 1.17× | 6.0e-7 |
| cached cross-attn | 1×8/8 64×1024×64 | 2.55 ms | 1.36 ms | 1.88× | 9.8e-7 |
| wide head d=128 | 1×8/8 1024×1024×128 | 47.80 ms | 16.29 ms | 2.93× | 1.7e-6 |
| odd dims (d80/dv96) | 1×4/2 512×512×80 | 3.70 ms | 2.33 ms | 1.59× | 7.2e-7 |
| Scenario | Shape (B×H/Hkv Sq×Skv×D) | naive | flash | speedup | max diff |
|---|---|---|---|---|---|
| chat prompt | 1×8/8 256×256×64 | 7.98 ms | 8.36 ms | 0.96× | 6.6e-7 |
| long prefill | 1×8/8 2048×2048×64 | 497.98 ms | 488.79 ms | 1.02× | 3.2e-6 |
| decode step (KV 2k) | 1×8/8 1×2048×64 | 2.95 ms | 3.06 ms | 0.96× | 2.1e-6 |
| GQA decode (KV 4k) | 1×8/2 1×4096×64 | 2.20 ms | 2.28 ms | 0.97× | 4.6e-6 |
| MQA decode (KV 4k) | 1×8/1 1×4096×64 | 1.48 ms | 1.61 ms | 0.92× | 4.6e-6 |
| batched prefill b4 | 4×8/8 256×256×64 | 33.86 ms | 34.94 ms | 0.97× | 6.6e-7 |
| cached cross-attn | 1×8/8 64×1024×64 | 16.11 ms | 16.66 ms | 0.97× | 1.7e-6 |
| wide head | 1×8/8 1024×1024×128 | 213.19 ms | 217.23 ms | 0.98× | 2.4e-6 |
| odd dims (d80/dv96) | 1×4/2 512×512×80 | 20.04 ms | 20.31 ms | 0.99× | 1.0e-6 |
| Kernel | Result | Shape |
|---|---|---|
| Tiled attention (WebGPU) | 322 GFLOP/s | 4096×4096, d=64, dispatch-only (gpubench) |
| bf16 GEMV | 2.7 ms (116 GB/s) | 151936×1024 |
| wasm tiled attention (f32x4 SIMD) | ~5.4 ms | 256×256, d=64, native-wasm |
moon build --target js
deno run --allow-read scripts/webgpu_host.js # kernel checks + throughput
deno run --allow-read scripts/bench_gpu_host.js # naive vs flash scenario benchmoon test # full suite, incl. property tests
moon run examples/attn_cpu --target wasm # minimal 4D GQA + causal exampleflowchart LR
subgraph host["Hosts (demo)"]
B["🌐 Browser page<br/>cmd/webchat"]
D["🖥️ Deno REPL<br/>cmd/qwengpu"]
K["🔬 Bench host<br/>cmd/gpubench"]
end
subgraph lib["flashmoon library (pure MoonBit)"]
G["gpu<br/>WebGPU runtime + WGSL kernels<br/>pipelines · buffers · submit"]
F["flash + flash/gpu<br/>4D batched attention (MHA/GQA/MQA)<br/>wasm SIMD · native · WebGPU"]
end
subgraph demo["demo/ — model components"]
Q["demo/qwen<br/>safetensors + BPE tokenizer"]
R["demo/qwenrun<br/>host-agnostic runner<br/>weights · KV cache · decode loop"]
end
B & D --> R
K --> G & F
R --> G & Q
F --> GOn the name. The flash library and the WebGPU inference kernels implement the FlashAttention-family algorithm — online softmax with running max/sum and deferred 1/l normalization (prefill: tiled K/V staging; decode: flash-decoding split-KV + combine). FA2's headline contributions are CUDA grid/warp scheduling (Q-parallel thread blocks, per-warp Q partitioning), which don't translate to WGSL — so we claim the family, not the version.
| Check | Result |
|---|---|
| 4D library property tests (GQA/MQA, causal, cross, odd dims; wasm/native) | max diff < 1e-4 |
| WebGPU flash4d vs naive oracle (4 shape configs, real GPU) | max diff ≤ 3.0e-7 |
| WebGPU GEMV / GEMM vs CPU oracle | max diff ≤ 1.6e-5 |
| Kernel unit checks (rmsnorm / rope / silu+add / attn_prefill / attn_decode, f64 oracle) | max diff ≤ 1.2e-7 |
| bf16 storage vs byte loads | equal (exposed byte/bf16 rounding drift, now read-side converted) |
flash/ 4D batched flash attention library (wasm/native, f32x4 SIMD)
flash/gpu/ WebGPU backend for the flash library (js target)
gpu/ WebGPU runtime + compute kernels (js target)
demo/ downstream model components + LLM demo (see demo/README.md)
demo/qwen/ safetensors parser + Qwen2 byte-level BPE tokenizer
demo/qwenrun/ Qwen3-0.6B runner core (host-agnostic: read/log injected)
examples/ standalone library usage examples (CPU + WebGPU)
bench/ naive-vs-flash scenario suite + sweeps (wasm/native)
bench/gpu/ naive-vs-flash GPU bench (js/WebGPU, Deno host)
cmd/fa/ naive-vs-flash attention demo (wasm/native)
cmd/gpubench/ WebGPU kernel checks + benchmarks (Deno host)
cmd/qwencpu/ Qwen3 CPU reference runner + tokenizer oracle test
cmd/qwengpu/ Deno REPL chat (MATCH gate + slash commands)
cmd/webchat/ browser chat page (chat.html + DOM frontend)
test/ blackbox tests (flash attention, benchmarks, tokenizer oracle)
refs/ model + HF reference data (gitignored, ~1.5 GB)
scripts/ Deno host shims (webgpu_host.js, bench_gpu_host.js, attn_gpu_host.js, qwengpu_host.js)| Source | Used for | License |
|---|---|---|
| FlashAttention / FlashAttention-2 papers (Dao et al., 2022/2023, arXiv:2205.14135, arXiv:2307.08691) | online-softmax tiling algorithm and kernel structure (flash, gpu attention kernels) | — |
| Dao-AILab/flash-attention | algorithm reference for the CUDA kernel layout | BSD-3-Clause |
| Qwen/Qwen3-0.6B | model weights + tokenizer config used by the demo/ runner | Apache-2.0 |
| moonxi-net | NpArray tensor type backing the flash package | Apache-2.0 |
Install
Download zipWebGPU-oriented AI inference foundation library: WebGPU runtime + WGSL kernel library, 4D batched FlashAttention (MHA/GQA/MQA, causal, cross-attention) on wasm/native/WebGPU, with verified numerics.
Dependencies