Repository navigation
perf(gguf): parse metadata through a buffered cursor - #1446
Merged
Merged
Conversation
StreamingGGUFReader read every metadata value with its own RandomAccessSource.readAt (a fresh byte array each, one FileChannel read each on the file sources) and allocated a Pair and a boxed offset per array element. A tokenizer GGUF carries arrays of ~262k strings, floats and ints, so opening it cost over a million source calls. GgufMetadataCursor reads the source in 64 KB blocks and decodes little-endian values from the block; string and 32-bit arrays are decoded block by block straight into pre-sized lists. `fields` keeps its keys, order, value classes and element classes, and stays plain lists (a lazily boxing view was slower for consumers that walk the lists several times on an interpreted runtime). Truncated input and array counts larger than the file fail with IllegalArgumentException before allocating. StreamingGGUFReaderMetadataParityTest compares against the previous per-value parse (kept in the test) on every value type, a long string over the block size, a 262k-entry tokenizer-shaped file, a source that returns 7 bytes per read, the file source, truncated input and a GGUFWriter round trip; SKAINET_GGUF_FILE adds a real file. GgufTokenizerLoadBenchmarkTest times parse and tokenizer build. Gemma-3 tokenizer GGUF, JVM: file source 1,002 -> 21-45 ms, in-memory 55 -> 21-34 ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-metadata # Conflicts: # CHANGELOG.md
Merged
michalharakal
added a commit
to michalharakal/SKaiNET-core-upstream
that referenced
this pull request
Oct 25, 2026
… build, AVX2/FMA SGEMM CHANGELOG [0.62.3] - 2026-10-25: the buffered metadata cursor in StreamingGGUFReader (SKaiNET-developers#1446), the one-pass GGUF SentencePiece tokenizer build (SKaiNET-developers#1447), the AVX2/FMA lane of the dense FP32 SGEMM and the sub-block output fix of NativeFp32MatmulKernel (SKaiNET-developers#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 <noreply@anthropic.com>
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
StreamingGGUFReaderread the header, KV and tensor-info sections one value at a time: oneRandomAccessSource.readAtper primitive (a fresh byte array each; onJvmRandomAccessSource/AndroidRandomAccessSourceoneFileChannelread each), plus aPairand a boxed offset per array element. A tokenizer GGUF carries arrays of ~262k strings, floats and ints, so opening one was well over a million source calls.GgufMetadataCursor: pulls the source in 64 KB blocks through the buffer form ofreadAt, decodes little-endian values from the block (commonMain, nojava.nio), grows the block for a single long value.ArrayLists.fieldsis unchanged: same keys and order, same value and element classes (UIntfor UINT32,Float,Int,String, …), still plain lists, so every consumer (as? List<*>,GGUFWriter,GgufMemoryPlan, the tokenizer builders) works as before.IllegalArgumentExceptionbefore allocating.RandomAccessSourceuntouched.An earlier variant exposed the primitive arrays as lazily boxing
AbstractListviews. It parsed faster but made the tokenizer build ~2× slower on an interpreted runtime (the build walks those lists several times), so values are boxed once into plain lists.Numbers (Gemma-3 tokenizer GGUF, 6.5 MB, 262,172 tokens)
-Xint, file / in memoryTests
StreamingGGUFReaderMetadataParityTest: the previous parser kept in the test asLegacyFieldParser, compared with the new one per key, value class and element class: every value type incl. nested and empty arrays, a 120k-char string, a 262k-entry tokenizer-shaped file, a source that returns 7 bytes per read, the file source, truncated input, an oversized array count, aGGUFWriterround trip, and a real GGUF viaSKAINET_GGUF_FILE(env-gated).GgufTokenizerLoadBenchmarkTest(io-core, env-gated timing harness, no assertions).:skainet-io:skainet-io-gguf:jvmTest,:skainet-io:skainet-io-core:jvmTest,:skainet-frontend:skainet-frontend-text:jvmTestgreen; io-gguf compiles for linuxX64, androidNativeArm32, js, wasmJs, android.Follow-up: #1447 (tokenizer build, stacked on this branch).
🤖 Generated with Claude Code