From 1334286ed4a5860decd6d844508c5141ee6247ac Mon Sep 17 00:00:00 2001 From: Michal Harakal Date: Mon, 10 Aug 2026 21:31:10 +0200 Subject: [PATCH] release: prepare 0.39.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump VERSION_NAME 0.38.0 -> 0.39.0 and update all version-carrying docs: - CHANGELOG: consolidate [Unreleased] under [0.39.0] with a headline summary (includes the #947 AAR-publishing CI entry, now merged to develop). - README: BOM snippet -> 0.39.0, What's New in 0.39.0, Contributors (0.39.0). - docs/antora.yml: skainet_version attribute 0.38.0 -> 0.39.0 (used in the docs' dependency snippets). - kernel-support-matrix.adoc: regenerated via generateKernelMatrix — the Android column now shows native-jni for Q8_0/Q4_0/Q4_K/Q5_K/Q6_K (the new JNI backend), replacing panama-vector; version stamp -> 0.39.0. 0.39.0 headline: on-device AI on Android becomes real — the skainet-backend-jni-cpu JNI NEON backend (~24 tok/s SmolLM2-135M Q8_0 on a Pixel 8a vs ~3.8 scalar), plus Android streaming GGUF loads (#922), linkable K/N kernel klibs (#942), the Q4_0 NEON kernel (#939), GGUF loader fail-fast (#919), and a tensor-storage correctness pass (#927-#931). Local prep only — not pushed/tagged. Cut off develop after #947 merged, so the branch already carries the AAR-publishing workflow + pinned NDK. --- CHANGELOG.md | 10 ++++++++++ README.md | 15 +++++++++++++-- docs/antora.yml | 2 +- .../pages/reference/kernel-support-matrix.adoc | 12 ++++++------ gradle.properties | 2 +- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ade5b228b..e03c6ac4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## [Unreleased] +## [0.39.0] - 2026-08-10 + +Headline: **on-device AI on Android becomes real.** A JNI NEON kernel backend +(`skainet-backend-jni-cpu`) brings hand-tuned ARM matmul to Android — where the +FFM provider can never run — measured at ~24 tok/s SmolLM2-135M Q8_0 decode on a +Pixel 8a versus ~3.8 scalar (6.4x), clearing the on-device usability bar. The +release also hardens the GGUF load path on Android (streaming instead of +full-file heap loads), makes published Kotlin/Native kernel klibs linkable, and +lands a batch of tensor-storage correctness fixes. + ### CI - **Release workflow publishes the `skainet-backend-jni-cpu` AAR.** `./gradlew publish` now diff --git a/README.md b/README.md index c3710e60d..fb1db1056 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Add the core dependencies (Gradle Kotlin DSL): ```kotlin dependencies { // Recommended: import the umbrella BOM and drop versions on the engine modules. - implementation(platform("sk.ainet:skainet-bom:0.38.0")) + implementation(platform("sk.ainet:skainet-bom:0.39.0")) implementation("sk.ainet.core:skainet-lang-core") implementation("sk.ainet.core:skainet-backend-cpu") @@ -288,7 +288,14 @@ val withoutLabel = dataPipeline() --- -## What's New in 0.38.0 +## What's New in 0.39.0 + +- **On-device AI on Android — a NEON kernel backend.** New `skainet-backend-jni-cpu` module: the hand-tuned ARM matmul kernels reach Android through a JNI bridge (ART has no `java.lang.foreign`, so the FFM provider can never run there). Two `.so` tiers are built from the same sources and selected at load time from `/proc/cpuinfo` — a baseline `armv8-a` build that runs on every 64-bit core, and an `armv8.2-a+dotprod` build for the `vdotq_s32` Q4_K/Q6_K paths — so a single artifact is safe from Cortex-A53 up. Measured on a Pixel 8a: **~24 tok/s** SmolLM2-135M Q8_0 decode versus ~3.8 scalar (6.4x), clearing the on-device usability bar. The provider auto-registers via `ServiceLoader`; an app just adds the AAR. +- **Android GGUF loading no longer OOMs.** `createRandomAccessSource` returned `null` on Android, forcing every model load through a full-file heap read that exhausted the ART heap on real devices. It now streams via positional `FileChannel` reads across `skainet-io-gguf` / `-safetensors` / `-onnx`. +- **Published Kotlin/Native kernel klibs are linkable.** The static kernel archive is now embedded into the cinterop klib, so downstream K/N consumers of `skainet-backend-native-cpu` (`-linuxx64` / `-linuxarm64`, and the path future Apple targets will use) link with no manual setup. A NEON body was also added for the Q4_0 matmul kernel. +- **Tensor-storage correctness pass.** Fail-fast on unsupported GGUF quant types instead of silently dropping weights; truthful ownership labels and real byte counts in the storage layer; a materializable `FileBacked`/`Aliased` transfer path; and a rank-safe default `copyToFloatArray`. + +### Previously, in 0.38.0 - **Streaming KV-cache decode (dynamic dimensions)** — a first-class `Dim` vocabulary makes "dynamic extent" explicit instead of an overloaded `-1`, and the StableHLO emitter renders it as an MLIR `?`. One compiled vmfb now serves every autoregressive decode step with a growing cache, instead of one fixed cache length. Verified end-to-end: the full FunctionGemma `with_past` decode graph and the Moonshine v2 decoder (dynamic self *and* cross caches) self-compile from the DSL to a CPU vmfb — graphs that could not be compiled before. Static graphs are emitted byte-for-byte unchanged. - **Narrow-float (BF16 + FP16) weights kept packed** — SafeTensors F16 and GGUF F16/BF16 weights load `KEEP_NATIVE`, two bytes per element at rest instead of widening to FP32, and reach format-specific matmul kernels still packed. Narrow floats are a storage width only: kernels widen to f32 lanes and accumulate in f32. @@ -326,6 +333,10 @@ We love contributions! Whether it's a new operator, documentation, or a bug fix: Browse the full codebase documentation on [DeepWiki](https://deepwiki.com/SKaiNET-developers/SKaiNET). +### Contributors (0.39.0) + +- **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — Android JNI NEON kernel backend with runtime dotprod dispatch (#943, #945), Android `createRandomAccessSource` streaming loads (#922), cinterop klib archive embedding (#942), Q4_0 NEON kernel (#939), GGUF loader fail-fast (#919), tensor-storage correctness fixes (#927, #928, #929, #930, #931), AAR release publishing (#947) + ### Contributors (0.38.0) - **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — dynamic tensor dimensions for streaming KV-cache decode (#891), shared narrow-float BF16/FP16 layer (#886), zero-copy transpose for input-major narrow weights (#895), native FP16 matmul kernel (#896), read-once weight tiling in the native narrow kernels (#897) diff --git a/docs/antora.yml b/docs/antora.yml index 0830d93b8..08a0a93f3 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -15,7 +15,7 @@ asciidoc: framework_name: SKaiNET # Current SKaiNET release — bump once per release; referenced as # {skainet_version} in dependency snippets (blocks need subs="attributes+"). - skainet_version: 0.38.0 + skainet_version: 0.39.0 ksp_version: 2.2.21-2.0.5 dokka_version: 2.1.0 asciidoctorj_version: 3.0.0 diff --git a/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc b/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc index 0a1f44f87..c79a502d9 100644 --- a/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc +++ b/docs/modules/ROOT/pages/reference/kernel-support-matrix.adoc @@ -1,7 +1,7 @@ = Kernel × platform support matrix :description: Which compute-kernel provider serves each weight format on each KMP target. -Generated from `kernel-support.json` (version `0.31.2`) by `KernelSupportMatrixTest` — registry introspection of the registered `KernelProvider` implementations. Do not edit by hand; run `./gradlew generateKernelMatrix` to refresh. +Generated from `kernel-support.json` (version `0.39.0`) by `KernelSupportMatrixTest` — registry introspection of the registered `KernelProvider` implementations. Do not edit by hand; run `./gradlew generateKernelMatrix` to refresh. Each cell is the best (highest-priority) provider that serves `Float32 × format` `matmul` on that platform: *native-ffm* (100) → *panama-vector* (50) → *scalar* (0). An empty cell (`—`) means no provider carries a kernel there (the format is dequant-to-FP32 only). @@ -11,11 +11,11 @@ Each cell is the best (highest-priority) provider that serves `Float32 × format | `Float32` | native-ffm | panama-vector | scalar | scalar | scalar | `BFloat16` | native-ffm | panama-vector | scalar | scalar | scalar -| `Q8_0` | native-ffm | panama-vector | scalar | scalar | scalar -| `Q4_0` | native-ffm | panama-vector | scalar | scalar | scalar -| `Q4_K` | native-ffm | panama-vector | scalar | scalar | scalar -| `Q6_K` | panama-vector | panama-vector | scalar | scalar | scalar -| `Q5_K` | native-ffm | panama-vector | scalar | scalar | scalar +| `Q8_0` | native-ffm | native-jni | scalar | scalar | scalar +| `Q4_0` | native-ffm | native-jni | scalar | scalar | scalar +| `Q4_K` | native-ffm | native-jni | scalar | scalar | scalar +| `Q6_K` | panama-vector | native-jni | scalar | scalar | scalar +| `Q5_K` | native-ffm | native-jni | scalar | scalar | scalar | `Q5_1` | panama-vector | panama-vector | scalar | scalar | scalar | `Q5_0` | panama-vector | panama-vector | scalar | scalar | scalar |=== diff --git a/gradle.properties b/gradle.properties index 7b3711810..54e0d8c0d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ GROUP=sk.ainet.core -VERSION_NAME=0.38.0 +VERSION_NAME=0.39.0 POM_DESCRIPTION=SKaiNET POM_URL=https://github.com/SKaiNET-developers/skainet/