Context
A fully offline Android transcription app built on LiteRT (Whisper large-v3-turbo split encoder/decoder on OpenCL FP16, Parakeet TDT 0.6B, Silero VAD on ONNX Runtime) was evaluated as a SKaiNET consumer. Its author's own measurements on a Pixel 7 Pro are the bar: LiteRT CPU XNNPACK transcribes a 14 s memo in 82.2 s (RTF 5.9); the GPU path needs 34.4 s.
Since 0.50.0 the Android JNI tier (skainet-backend-jni-cpu) serves every GGML quant format from mapped weights on NEON, and 0.52.0 made the dispatch self-installing. That covers the weight side of a Whisper encoder (Q8_0 from whisper.cpp GGUFs, 874 MB). What it does not cover is the activation side.
Gap
The generated kernel support matrix (docs/.../reference/kernel-support-matrix.adoc, 0.54.0) lists Float32 and BFloat16 as scalar on Android, and native/skainet_jni.c exports only q40/q4k/q50/q51/q5k/q6k/q80 matmul entries plus the ternary gemv. A Whisper large-v3-turbo encoder issues, per layer, 20 heads of QKᵀ ([1500,64]×[64,1500]) and AV ([1500,1500]×[1500,64]) as FP32 activation × activation matmuls, 32 layers deep, plus two conv1d stem layers. On Android all of that runs through the scalar Kotlin path, so the quantized weight kernels cannot make the encoder fast on their own.
fp32_matmul.c already exists in skainet-backend-native-cpu (aarch64-verified, see #920) and the JVM reaches it through FFM; Android does not.
Scope
Acceptance
- Bit-identical results to the scalar path on device for the attention shapes above.
- whisper-tiny.en encoder on a Pixel 7 Pro / 8a class phone at least 5× faster than the scalar Android baseline.
Related
Context
A fully offline Android transcription app built on LiteRT (Whisper large-v3-turbo split encoder/decoder on OpenCL FP16, Parakeet TDT 0.6B, Silero VAD on ONNX Runtime) was evaluated as a SKaiNET consumer. Its author's own measurements on a Pixel 7 Pro are the bar: LiteRT CPU XNNPACK transcribes a 14 s memo in 82.2 s (RTF 5.9); the GPU path needs 34.4 s.
Since 0.50.0 the Android JNI tier (
skainet-backend-jni-cpu) serves every GGML quant format from mapped weights on NEON, and 0.52.0 made the dispatch self-installing. That covers the weight side of a Whisper encoder (Q8_0 from whisper.cpp GGUFs, 874 MB). What it does not cover is the activation side.Gap
The generated kernel support matrix (
docs/.../reference/kernel-support-matrix.adoc, 0.54.0) listsFloat32andBFloat16as scalar on Android, andnative/skainet_jni.cexports onlyq40/q4k/q50/q51/q5k/q6k/q80matmul entries plus the ternary gemv. A Whisper large-v3-turbo encoder issues, per layer, 20 heads ofQKᵀ([1500,64]×[64,1500]) andAV([1500,1500]×[1500,64]) as FP32 activation × activation matmuls, 32 layers deep, plus two conv1d stem layers. On Android all of that runs through the scalar Kotlin path, so the quantized weight kernels cannot make the encoder fast on their own.fp32_matmul.calready exists inskainet-backend-native-cpu(aarch64-verified, see #920) and the JVM reaches it through FFM; Android does not.Scope
A × Bᵀ) so attention does not pay a transpose copy per head, using the existingskainet_row_threadspool.fp16_matmul.cis in tree (FP16: native FFM matmul kernel (priority 100) to match BF16's tier #885 tracks the FFM side of the same kernel).JniKernelProviderregistrations soKernelDispatchselects them on Android at the native priority.ScalarFp32MatmulKernelinJniKernelParityTest, including offset/strided operands (the JVM Panama vector kernels: accept slab-backed (offset) FP32 operands #1173 class of bug).KernelSupportMatrixTestregenerated:Float32andFloat16shownative-jnion Android.Acceptance
Related