0.40.0 closure: native Q5 kernels + pre-transposed default (closes #170, #184) - #307
Merged
Merged
Conversation
…, #184) Engine 0.40.0 is tagged and on Maven Central (q5_0_matmul.c / q5_1_matmul.c native kernels confirmed via `git ls-tree -r 0.40.0`); transformers 0.39.1 is tagged/published. This lands the closure step PR #294 deferred: - Bump skainet 0.39.1 -> 0.40.0 in gradle/libs.versions.toml. - Flip GemmaQuantLayout.packGemmaKQuant / LlamaQuantLayout.packLlamaKQuant and GemmaMemSegConverter's JVM MemSeg path to call BlockQuantPacking.packPreTransposed by default wherever the existing hasPackedMatmulKernel() gate confirms a packed kernel exists (Q4_K/Q5_K/ Q6_K/Q8_0 unconditionally, Q4_0/Q5_0/Q5_1 gated as before). linearProject then skips its per-forward ops.transpose for these weights. Deprecate-don't-delete: both pack functions keep a `preTransposed` parameter (default true) so the classic BlockQuantPacking.pack path stays reachable for fallback / parity comparison; nothing was deleted. - Update GemmaQuantLayoutTest for the new default and add explicit coverage of the preTransposed=false fallback. Real-checkpoint parity (functiongemma-physical-ai-v10-Q5_K_M.gguf, 12g heap): GemmaQ5KPackedParityTest / GemmaQ5xPackedParityTest greedy decode is byte/token-identical across FP32 baseline, NATIVE_OPTIMIZED (MemSeg), and load(NATIVE_OPTIMIZED) (K/N board path) - all three produce the same 8-token generation, and all 81 Q5_1 tensors stay packed post-conversion. Speed, measured on :llm-inference:gemma's own jvmTest classpath (scalar/ Panama kernels only - this module doesn't wire in skainet-backend-native-cpu, see build.gradle.kts comment): before (0.39.1, non-pre-transposed pack): 2.88 tok/s after (0.40.0, pre-transposed default): 2.94-2.99 tok/s (~3-5%) Separately, temporarily wiring skainet-backend-nativeCpu onto this module's test classpath (mirroring :llm-inference:llama, not shipped in this PR) confirmed the real native-kernel effect: 4.39 tok/s (~53% faster than the 2.88 baseline), parity still held. That same experiment also surfaced a pre-existing, out-of-scope engine issue: engine 0.40.0's native Q5_0/Q5_1 kernel returns all-zero output when reached through linearProject's classic lazy ops.transpose path (GemmaQ5xPackedParityTest's synthetic byte-level tests, which construct Q5_1BlockTensorData directly - code this PR does not touch). The new pre-transposed path does NOT hit this: it skips lazy transpose entirely and the real-checkpoint decode matched byte-for-byte with the native kernel active. So defaulting converters to pre-transposed is not just a performance win here, it also routes around this gap - documented in llm-inference/gemma/build.gradle.kts and the PR description rather than silently worked around. ./gradlew build apiCheck green on transformer-core, llm-inference/gemma, llm-inference/llama. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This was referenced Aug 12, 2026
Closed
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
Engine 0.40.0 is tagged and on Maven Central (
q5_0_matmul.c/q5_1_matmul.cnative kernels confirmed viagit ls-tree -r 0.40.0, andsk.ainet.core:skainet-backend-native-cpu-jvm:0.40.0is live on Central). This lands the step PR #294 explicitly deferred: flipping the gemma/llama converters to emit pre-transposed weights by default now that a native packed kernel is confirmed to exist.skainet0.39.1→0.40.0ingradle/libs.versions.toml.GemmaQuantLayout.packGemmaKQuant/LlamaQuantLayout.packLlamaKQuant(commonMain, shared by the JVM MemSeg path and the Kotlin/Native board path) andGemmaMemSegConverter's JVMconvertOnenow callBlockQuantPacking.packPreTransposedinstead ofBlockQuantPacking.packby default, wherever the existinghasPackedMatmulKernel()gate confirms a packed kernel exists (Q4_K/Q5_K/Q6_K/Q8_0 unconditionally — long-standing kernels; Q4_0/Q5_0/Q5_1 gated as before, unchanged gating logic).linearProjectthen dispatchesops.matmul(x, W)directly and skips its per-forwardops.transposefor these weights.pack*Quantfunctions keep apreTransposed: Boolean = trueparameter so the classic non-transposedBlockQuantPacking.packpath stays reachable for fallback / parity comparison. Nothing was deleted.GemmaQuantLayoutTestupdated for the new default, plus explicit new coverage of thepreTransposed = falsefallback path.Parity evidence (real checkpoint)
functiongemma-physical-ai-v10-Q5_K_M.gguf, 12g heap,-PincludeIntegration:GemmaQ5KPackedParityTest.q5kPackedMatchesFp32: greedy decode is byte/token-identical across the FP32 baseline,NATIVE_OPTIMIZED(JVM MemSeg, now pre-transposed), andload(NATIVE_OPTIMIZED)(K/N board path, also now pre-transposed) — all three produce the same 8-token generation[262146, 236769, 3255, 718, 498, 1373, 262152, 106].GemmaQ5xPackedParityTest.functionGemma_q5_1_tensors_stay_packed_after_conversion: all 81 Q5_1 tensors in the checkpoint stay packed post-conversion (none fall back to FP32 dequant).GemmaQ5xPackedParityTest's synthetic byte-level Q5_0/Q5_1 tests: pass (these exercise the classic non-pre-transposed path directly, unaffected by the default flip — see note below).Speed: before vs after
Measured on
:llm-inference:gemma's ownjvmTestclasspath, which only wires inskainet-backend-cpu(scalar + Panama Vector API kernels — this module does not depend onskainet-backend-native-cpu, unlike:llm-inference:llama):That modest delta is the honest, CI-reproducible number for this module as shipped — it reflects only the transpose-wrapper-allocation removal, not the native kernel tier, because the native kernel provider was never on this module's classpath to begin with (confirmed by inspecting
build.gradle.kts: only:llm-inference:llamawiresskainet.backend.nativeCpu).Diagnostic-only (not shipped): temporarily adding
skainet.backend.nativeCputo this module'sjvmTestdependencies (mirroring llama) and re-running:GemmaQ5xPackedParityTest's two synthetic Q5_0/Q5_1 byte-level tests failed with all-zero output. Root-caused: those tests constructQ5_0BlockTensorData/Q5_1BlockTensorDatadirectly and drive them throughlinearProject's classic lazy-ops.transposebranch — code this PR does not touch. This reproduces identically regardless of the pre-transposed default flip, so it's a pre-existing engine 0.40.0 issue (native Q5_0/Q5_1 kernel × lazy transpose dispatch), not something introduced or fixable in this repo.:llm-runtime:kgemma(the actual runnable app) already depends onskainet.backend.nativeCpp/jniCpudirectly, so production Gemma inference gets both the real native-kernel speedup and automatically avoids the lazy-transpose gap via this PR's new default.llm-inference/gemma/build.gradle.kts(comment, not a dependency change) rather than silently worked around. Flagging for a separate engine-side bug report — happy to file if wanted.Build
./gradlew build apiCheckgreen ontransformer-core,llm-inference/gemma,llm-inference/llama. No public API surface changed (packGemmaKQuant/packLlamaKQuantareinternal).Test plan
:transformer-core:jvmTest—BlockQuantPackingTest,LinearProjectionPreTransposedTestgreen:llm-inference:gemma:jvmTest—GemmaQuantLayoutTestgreen (incl. new pre-transposed-default + fallback coverage):llm-inference:gemma:jvmTest -PincludeIntegrationw/GEMMA_GGUF—GemmaQ5KPackedParityTest,GemmaQ5xPackedParityTestgreen, parity confirmed./gradlew build apiCheckgreen on touched modulesCloses #170, closes #184.
🤖 Generated with Claude Code