Repository navigation
perf: 1031: cheaper nested decoding: oneofs in order, lists sized once, inlined varints - #1035
Open
jasperpotts wants to merge 12 commits into
Open
jasperpotts wants to merge 12 commits into
jasperpotts wants to merge 12 commits into
Conversation
…ts once parseArray's in-order fast path includes oneofs of at most four alternatives, so an id's account number no longer falls to the tag switch. A run of repeated delimited elements with a one-byte tag is counted first (ArrayCursor.run, at most 4096 fields in 64 KB, as counting a block's items was slower than growing) and its list allocated once at that size. Synthetic block decode allocates 414 B less per transaction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
ArrayCursor.varintWide was 417 bytes of bytecode, over FreqInlineSize, so it was never inlined. It now reads two to eight bytes with the XOR chain and sends everything else, and every error, to varintSlow: 9-10 byte values, the last bytes of the array, and varints that end past the limit. Prototyped on the external workload by the validation session. Tests: every length near the limit and near the array's end, cut short, and a bad tenth byte before and past the limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com> # Conflicts: # pbj-core/pbj-runtime/src/test/java/com/hedera/pbj/runtime/io/buffer/ArrayCursorTest.java
ArrayCursor.varint64 is varint with the multi-byte read in the same method (304 bytes, under FreqInlineSize): generated parsers and views use it for int64, uint64 and sint64 fields, whose values are often large, so the JIT can inline the whole read there. Suggested by the validation session. Mac: block decode and views scan faster, transaction views scan slower; to be decided on EPYC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
JUnit Test Report 559 files 559 suites 1m 25s ⏱️ Results for commit ce64ed7. ♻️ This comment has been updated with latest results. |
Signed-off-by: Jasper Potts <1466205+jasperpotts@users.noreply.github.com>
jasperpotts
force-pushed
the
1030-view-child-cost
branch
from
November 2, 2026 20:33
99cc427 to
dadf12c
Compare
jasperpotts
force-pushed
the
1031-array-parser-nested
branch
from
November 2, 2026 20:33
79543f4 to
ce64ed7
Compare
This was referenced Nov 2, 2026
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.
Closes #1031. Stacked on #1034 (
1030-view-child-cost).Summary
The byte-array parser (
pbj.fast_parse, part ofperformance = "max") and the views shareArrayCursor. This PR makes decoding nested HAPI messages cheaper in both: oneofs no longer leave the in-order fast path, short repeated fields are sized once, and the common varints are read by code the JIT inlines. Nothing changes without the options on (tier B).Performance (AMD EPYC 9275F): on the synthetic HAPI block, decoding is 3.5% faster (1,417 → 1,367 ns per transaction, 414 B less), decode-then-read-every-field 11.5% faster, and reading every field through views 5.9% faster. Tier B, on with
performance = "max".273e4af3): up to four alternatives are tried in order, as views already do (perf: 1030: views pay less per nested message, allocate nothing, and read bytes in place #1034), instead of falling through to the tag switch.273e4af3): before reading a run of a repeated message field,ArrayCursor.run(tag)counts its consecutive one-byte-tag elements without decoding them, and the list is created at that size (ProtoParserTools.presize). It gives up past 4,096 elements or 64 KiB, as counting a list that spans megabytes costs more in cache misses than growing it.fe149849): two to eight bytes are read with a branch-light XOR chain, and everything else (nine and ten bytes, the end of the input, malformed values) goes to a separate byte-at-a-time method. The common path became small enough to inline everywhere.6adb96ad):ArrayCursor.varint64()andvarint64ZigZag(), used forint64,uint64andsint64fields by the parser and the views.EPYC results
AMD EPYC 9275F, JDK 25.0.4, one CCD (
taskset -c 0-2,24-26) of an idle machine, 3 forks × 5 iterations of 2 s,-prof gc. ns per transaction of the synthetic HAPI block (#1028), every option on.Step by step, in one run:
decodescanViewstxDecodetxScanViewsvarint64Against the parent PR, in the full benchmark at each head:
decodescanObjects: decode, then read every fieldscanViewstxDecodetxScanObjectstxScanViewsvarint64is a trade-off: it made the block view scan 7.5% faster with transaction scans flat here, and on a second, external block workload block scans 12% faster and transaction scans 12 ns slower. For a block stream, the block side matters far more, so it stays.Decode is now 1.38× the hand-written reference (989 ns in the same run), from 1.46× at #1028, and transactions 1.31×.
Tests
ArrayCursorTest: runs of delimited fields are counted without reading them, and the count gives up at its limits; varints read the same near the limit and near the array's end, for every length; a bad tenth byte past the limit is an underflow, as the stream parser reports it;varint64reads exactly asvarintdoes.🤖 Generated with Claude Code