From 717c4f929d66206df10978f7d943357ddb1054f6 Mon Sep 17 00:00:00 2001 From: michalharakal Date: Sun, 25 Oct 2026 14:33:21 +0100 Subject: [PATCH] release: prepare 0.62.3 -- buffered GGUF metadata, one-pass tokenizer build, AVX2/FMA SGEMM CHANGELOG [0.62.3] - 2026-10-25: the buffered metadata cursor in StreamingGGUFReader (#1446), the one-pass GGUF SentencePiece tokenizer build (#1447), the AVX2/FMA lane of the dense FP32 SGEMM and the sub-block output fix of NativeFp32MatmulKernel (#1445). VERSION_NAME, CITATION, Antora version, README bom coordinate, What's New and contributors, the Android tutorial coordinates; operator and kernel-support matrices (version stamp). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 12 +++++++++++ CITATION.cff | 2 +- README.md | 21 ++++++++++++++++--- docs/antora.yml | 2 +- .../reference/kernel-support-matrix.adoc | 2 +- .../reference/operators/generated/index.adoc | 2 +- .../pages/reference/ops-status-matrix.adoc | 2 +- .../android-classifier-getting-started.adoc | 12 +++++------ gradle.properties | 2 +- 9 files changed, 42 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2310deb..bc0e8519 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +## [0.62.3] - 2026-10-25 + +Headline: **opening a tokenizer GGUF and building its tokenizer is several times cheaper, and the dense FP32 `A · B` +product uses AVX2/FMA on x86-64** (#1446, #1447, #1445, see *Changed*). `StreamingGGUFReader` read the metadata one +value at a time (one `readAt` per primitive, one `FileChannel` read each on a file source); it now decodes from 64 KB +blocks, with `fields` unchanged. `TextFrontEnds.fromGguf` copied the ~262k-entry token, token-type and score lists +three to four times per build; it now takes each once, with token ids identical on 26,803 texts. Gemma-3 tokenizer +GGUF on a 32-bit ARM Android device (debuggable app): file-source parse 23.3 → 1.2 s, read + build on the first load +in a process 8.4–11.2 → 6.0–6.4 s; JVM file-source parse 1,002 → 21–45 ms. The native `skainet_fp32_matmul` gets a +6 × 16 register-tiled AVX2/FMA lane selected at runtime (63 × 1024 × 1024: 21 → 281 GFLOP/s on a six-core laptop), +and its heap face no longer zeroes the rest of a sub-block caller's output buffer. + ### Changed - **Dense FP32 SGEMM uses AVX2/FMA on x86-64** (`skainet-backend-native-cpu`): `skainet_fp32_matmul`, the kernel behind every `A · B` product the JVM hands to the native lane (a weight stored `[in, out]`, attention scores and values, training), had register-tiled bodies for wasm SIMD128 only; the x86-64 library is built for baseline x86-64, so the kernel ran a scalar/SSE2 outer-product loop without FMA that loaded and stored a row of C for every row of A. Hosts with AVX2 and FMA now select, through the cached `cpuid` probe the `x · Wᵀ` kernel introduced, a lane compiled per function for that target: a 6 × 16 tile of C in twelve eight-lane FMA accumulators over a 256-deep k-block, each sixteen-column panel of B packed once so a power-of-two `n` no longer maps every row of the panel to one L1 set, 2 × 16 and 1 × 16 tiles for the rows left over and a lane-masked stripe for the columns left over. Every output takes one fused multiply-add per `p` in ascending `p` whichever tile holds it, so sliced, threaded and whole calls stay bit-identical to each other; against the previous x86 numbers an output moves within rounding, as the NEON and wasm lanes already do. Machines without AVX2, and every other architecture, run the previous code. Measured single call on a six-core laptop, develop → this change: 63 × 1024 × 1024 21 → 281 GFLOP/s, 1024³ 48 → 311, 512³ 59 → 358, 192 × 192 × 64 (one thread) 18 → 99. diff --git a/CITATION.cff b/CITATION.cff index 25858088..3c087979 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,7 +12,7 @@ authors: orcid: https://orcid.org/0009-0006-7981-4075 - name: SKaiNET-developers contributors website: https://github.com/SKaiNET-developers -version: "0.62.2" +version: "0.62.3" date-released: "2026-10-21" license: MIT repository-code: https://github.com/SKaiNET-developers/SKaiNET diff --git a/README.md b/README.md index c5236bf3..3d892c3f 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,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.62.2")) + implementation(platform("sk.ainet:skainet-bom:0.62.3")) implementation("sk.ainet.core:skainet-lang-core") implementation("sk.ainet.core:skainet-backend-cpu") @@ -308,7 +308,17 @@ val withoutLabel = dataPipeline() --- -## What's New in 0.62.2 +## What's New in 0.62.3 + +A load-time and kernel release. `StreamingGGUFReader` decodes the GGUF metadata from 64 KB blocks instead of one +source read per value, and `TextFrontEnds.fromGguf` builds a SentencePiece tokenizer in one pass over the vocabulary +instead of three to four copies of it; `fields`, the public API and the token ids stay the same. On a Gemma-3 +tokenizer GGUF a file-source parse drops from 1.0 s to 21–45 ms on the JVM and from 23.3 s to 1.2 s on a 32-bit ARM +Android device, where read + build on the first load in a process goes from 8.4–11.2 s to 6.0–6.4 s. The native +dense FP32 `A · B` kernel gets an AVX2/FMA register-tiled lane on x86-64 (63 × 1024 × 1024: 21 → 281 GFLOP/s), and +no longer overwrites the parts of a sub-block caller's output buffer outside the block. + +### Carried from 0.62.2 A performance release for the JVM native lane. The dense FP32 `x · Wᵀ` kernel — what every resident dense weight resolves to once `skainet-backend-native-cpu` is on the classpath — now splits its work across the @@ -381,6 +391,11 @@ 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.62.3) + +- **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — the buffered GGUF metadata reader, the + one-pass tokenizer build, the AVX2/FMA dense FP32 SGEMM lane with their tests and measurements, and the release + ### Contributors (0.62.2) - **Michal Harakal** ([@michalharakal](https://github.com/michalharakal)) — the schedule-driven and AVX2/FMA @@ -512,7 +527,7 @@ from it). Ready-to-paste BibTeX: author = {Harakal, Michal and {SKaiNET-developers contributors}}, title = {{SKaiNET}: A {Kotlin} Multiplatform {AI} Framework}, year = {2026}, - note = {Version 0.62.2. MIT License}, + note = {Version 0.62.3. MIT License}, howpublished = {\url{https://github.com/SKaiNET-developers/SKaiNET}} } ``` diff --git a/docs/antora.yml b/docs/antora.yml index e1e4bb7c..c4a2d69a 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.62.2 + skainet_version: 0.62.3 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 c65f1541..629f0102 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.62.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.62.3`) 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* / *native-jni* / *native-cinterop* (100) → *panama-vector* / *wasm-simd128* (50) → *scalar* (0). An empty cell (`—`) means no provider carries a kernel there (the format is dequant-to-FP32 only). The JS/WASM column assumes `WasmSimd.install()` has run; without the sidecar it is the scalar floor. diff --git a/docs/modules/ROOT/pages/reference/operators/generated/index.adoc b/docs/modules/ROOT/pages/reference/operators/generated/index.adoc index 25298070..c1c0e029 100644 --- a/docs/modules/ROOT/pages/reference/operators/generated/index.adoc +++ b/docs/modules/ROOT/pages/reference/operators/generated/index.adoc @@ -1,6 +1,6 @@ = AI-NET Operators Reference -Generated from version `0.62.2` on 2026-10-24 +Generated from version `0.62.3` on 2026-10-25 == Operators by Modality diff --git a/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc b/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc index a3e4642f..4d3943fe 100644 --- a/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc +++ b/docs/modules/ROOT/pages/reference/ops-status-matrix.adoc @@ -1,7 +1,7 @@ = Operator Coverage Matrix :description: Cross-backend status for every operator function in SKaiNET. -Generated from `operators.json` version `0.62.2` on 2026-10-24. +Generated from `operators.json` version `0.62.3` on 2026-10-25. Rows are `Operator.function` pairs. A gap is marked in source with `@NotImplemented(reason = …, issue = …)` (or `@InProgress`) and shows up here as ❌ (or 🚧) linked to its tracking issue; the PR that closes the gap removes the annotation. See xref:contributing/issue-taxonomy.adoc[Issue taxonomy] to pick one up. diff --git a/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc b/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc index c89c854d..499ecc2f 100644 --- a/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc +++ b/docs/modules/ROOT/pages/tutorials/android-classifier-getting-started.adoc @@ -19,12 +19,12 @@ An Android project with Kotlin. Add the SKaiNET modules: [source,kotlin] ---- dependencies { - implementation("sk.ainet.core:skainet-lang-core:0.62.2") // tensors, DSL, training - implementation("sk.ainet.core:skainet-backend-cpu:0.62.2") // CPU ops - implementation("sk.ainet.core:skainet-compile-dag:0.62.2") // autograd (training context) - implementation("sk.ainet.core:skainet-data-api:0.62.2") // Dataset / DataBatch - implementation("sk.ainet.core:skainet-data-simple:0.62.2") // embedded Iris - runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.62.2") // NEON kernels (see below) + implementation("sk.ainet.core:skainet-lang-core:0.62.3") // tensors, DSL, training + implementation("sk.ainet.core:skainet-backend-cpu:0.62.3") // CPU ops + implementation("sk.ainet.core:skainet-compile-dag:0.62.3") // autograd (training context) + implementation("sk.ainet.core:skainet-data-api:0.62.3") // Dataset / DataBatch + implementation("sk.ainet.core:skainet-data-simple:0.62.3") // embedded Iris + runtimeOnly("sk.ainet.core:skainet-backend-jni-cpu:0.62.3") // NEON kernels (see below) } ---- diff --git a/gradle.properties b/gradle.properties index acdf54fe..1b2cfc5e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ GROUP=sk.ainet.core -VERSION_NAME=0.62.2 +VERSION_NAME=0.62.3 POM_DESCRIPTION=SKaiNET POM_URL=https://github.com/SKaiNET-developers/skainet/