Repository navigation
feat(kernels): ternary kernels on the wasm sidecar; direct x·Wᵀ kernel on the JVM - #1368
Merged
michalharakal merged 1 commit intoOct 20, 2026
Conversation
…l on the JVM Wasm: the sidecar exports skainet_ternary_f32_gemv, skainet_ternary_lmhead_stage1 and skainet_bitnet_gemv_tq2_0. The portable branch of skainet_ternary_f32.c (MSVC, Emscripten) holds the vendored decode table as a compile-time constant with a SIMD128 body and threads over output rows through the shared row pool; bitnet_gemv.c gains a SIMD128 block body and the same row threading on every tier. WasmTernaryKernelPack (installed by WasmSimd.install) registers the three native faces under the dispatch keys the JVM FFM and Android JNI ternary packs serve. JVM: Fp32MatmulWtKernel / KernelProvider.matmulFp32Wt() and the JVM-only Fp32MemSegMatmulWtKernel / MemSegKernelProvider.matmulFp32WtMemSeg() expose skainet_fp32_matmul_wt; NativeFp32MatmulWtKernel binds it with a heap and a segment face. DefaultCpuOpsJvm.matmulWeightTransposed multiplies a dense [out, in] FP32 weight in place over a resident off-heap weight instead of transposing and caching a copy. WasmKernelProvider exposes the same accessor. Kernel support matrix: a fourth table, "Kernel packs (view dispatch)". Bench: engine-fp32-linear on both harnesses. Docs: eager-execution ternary rows and x·Wᵀ row; the wasm ternary gap is closed.
This was referenced Oct 19, 2026
feat(wasm): resident activations in the sidecar; elementwise, activation and reduction kernels
#1369
Merged
This was referenced Oct 20, 2026
aharakal
approved these changes
Oct 20, 2026
michalharakal
added a commit
that referenced
this pull request
Oct 21, 2026
CHANGELOG [0.62.0] - 2026-10-21 with the parity headline over #1363, #1364, #1368, #1369, #1426, #1427, #1428 and #1430, plus entries for the operator coverage matrix (#1370), the BitNet decode allocation and threading fixes (#1359, #1360, #1361, #1362), the ClassificationSupport extraction (#1429) and the toolchain bumps (Kotlin 2.4.21, kotest 6.2.6, Gradle 9.8.1). VERSION_NAME, CITATION, Antora version, README bom coordinate, What's New and contributors, Android tutorial coordinates; regenerated operator and kernel-support matrices; SKEEP-007 rollout step 3 marked shipped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Third step of the JVM ↔ wasm compute parity series (after #1363 and #1364): the ternary kernels join the browser sidecar, and the JVM gets the direct
x · Wᵀkernel the sidecar has had since 0.61.0. Both directions of parity, both through the shared C tree.Wasm: ternary kernels in the sidecar.
native/build-wasm.shcompilesskainet_ternary_f32.candbitnet_gemv.cand exportsskainet_ternary_f32_gemv,skainet_ternary_lmhead_stage1,skainet_bitnet_gemv_tq2_0.skainet_ternary_f32.c(what MSVC and Emscripten build — the vendored NeoGPU file needs native pthreads and NEON) now holds the vendored kernel's 256-entry decode table as a compile-time constant (no init guard to race on), has a SIMD128 body for both the gemv and the fused four-plane lm_head (one LUT row is onef32x4of decoded codes against four activations), and threads over output rows through the shared row pool. The scalar MSVC body computes exactly what it did before.bitnet_gemv.c: a SIMD128 body for the TQ2_0 block (same unpack as the NEON one,i32x4.dot_i16x8_son widened halves) and row threading over output rows on every tier; the NEON and scalar block bodies are unchanged.WasmTernaryKernelPack(installed byWasmSimd.install()) registers three native faces —WasmTernaryF32Gemv,WasmTernaryLmhead,WasmBitNetGemv— through the sameTernaryF32KernelPack/TernaryPlanesKernelPack/TernaryKernelPacksinstalls the JVM FFM and Android JNI packs use, so the dispatch keys and the kernel names (ternary_f32_gemv/wasm-simd128, …) are the same shape. Weights are uploaded once and stay resident; activation and output cross per call.JVM: direct
x · Wᵀ.Fp32MatmulWtKernelandKernelProvider.matmulFp32Wt()(common),Fp32MemSegMatmulWtKernelandMemSegKernelProvider.matmulFp32WtMemSeg()(JVM) exposeskainet_fp32_matmul_wt.NativeFp32MatmulWtKernelbinds it with a heap face and a segment face;NativeKernelProvidercarries both;KernelProvider.supports("matmul_wt", ["Float32", "Float32"])reports it.DefaultCpuOpsJvm.matmulWeightTransposedmultiplies a dense[out, in]FP32 weight in place through the segment face: a segment-backed weight is read where it lies, a heap weight is made resident off-heap once (bounded cache keyed on theTensorData, replacing the transposed-copy cache for that case). No[k, n]transpose is materialised; the C kernel threads over output rows. Without the native library the cached-transpose path serves as before.WasmKernelProvider.matmulFp32Wt()(WasmFp32MatmulWtKernel) exposes the same accessor on wasm;WasmCpuOps.matmulWeightTransposedroutes through it.Matrix, bench, docs.
BitNet-b1.58(FP32 activations),BitNet-planes,TQ2_0(int8 activations) andFloat32 x·Wᵀ directper platform; the JVM cells are gated against the FFM provider and pack on the runner.engine-fp32-linearinjvm-cpu-publishandwasm-bench: aLinearlayer'sx · Wᵀ(16 tokens, 768 → 3072) throughctx.ops.matmulWeightTransposed;--provider panamameasures the cached-transpose path,--provider ffmthe direct kernel.x · Wᵀdirect row, the "Wasm ternary / BitNet" gap is removed.Measured (Node, 12-core laptop; JVM on the same machine)
engine-ternary-f32-gemv(2560×6912)--provider ffm)engine-fp32-linear(16×768 · (3072×768)ᵀ viactx.ops.matmulWeightTransposed)panama(cached transpose + Vector GEMM) 5.0 GFLOPS →ffmdirect 30.8 GFLOPS;scalar1.1The other
engine-*scenarios are unchanged against #1364 within noise. On the JVM the direct kernel is 6× the cached-transpose path for this shape and allocates no transposed copy.Verification
:skainet-backends:skainet-backend-wasm-simd:wasmJsNodeTest— new: ternary f32 gemv (two activation rows, 600 output rows so the mt sidecar splits them), planes lm_head and TQ2_0 bitnet gemv throughKernelDispatchagainst the Kotlin reference kernels with the serving kernel name asserted; the directx · Wᵀprovider kernel resident and windowed. Plus the existing eight tests.:skainet-backends:skainet-backend-native-cpu:jvmTest— newNativeFp32MatmulWtKernelTest(heap face vs row-dot reference incl. the threaded regime, offsets,k == 0, segment face bit-identical to the heap face, provider accessors) andJvmDirectWeightTransposedTest(ops layer, heap and segment-backed weights, activation ranks 1–3, threaded rows, vs transpose-then-matmul);KernelSupportMatrixTestwith the new table; the existing ternary FFM tests cover the vendored branch (unchanged).bitnet_gemv.c, syntax check); CImacos-arm64runs the NEONbitnet_gemvbody with its row threading through the JNI-shaped C.generateKernelMatrix,apiDump,scripts/pr-gate.sh --quick, full gate.Next in the series: resident activations (
Storage.WasmLinear) and sidecar elementwise/activation kernels, then fused softmax/norms/SDPA on both tiers, then the browser IO gaps.