Repository navigation
perf(compiler): compare byte arrays a word at a time on SBF - #29
Draft
paulbalaji wants to merge 4 commits into
Draft
paulbalaji wants to merge 4 commits into
paulbalaji wants to merge 4 commits into
Conversation
== on [N]byte/[N]uint8/[N]int8 compiled to a byte loop of ~11 SBF instructions per byte, so a [32]byte key comparison cost ~350 CU. For N >= 8 the SBF build now folds the XOR of whole words (fixed-size __builtin_memcpy, one unaligned ldxdw per 8 bytes) plus any tail bytes. Byte comparisons have no side effects, so dropping the early exit is unobservable. Host C, bool and wider elements keep the element loop.
paulbalaji
force-pushed
the
opt-byte-array-equal
branch
from
October 19, 2026 16:47
65c641b to
5a45b9f
Compare
This was referenced Oct 19, 2026
The change moves the full-swap reference ELFs, so CI's fresh-rebuild check now compares against this branch's rebuild/metrics.json, the default reference in scripts/verify_rebuild.py.
This was referenced Oct 19, 2026
paulbalaji
added a commit
to hyperlane-xyz/hyperlane-monorepo
that referenced
this pull request
Oct 19, 2026
hyperlane-integration-4 (3ddeff0) is upstream main 939b215, which merged our earlier PRs #9 to #24 in CrocSwap/goSVM#25, plus the open draft PRs: whole-word runtime loads, stores and key comparisons (CrocSwap/goSVM#27), solana.FindAddress on sol_try_find_program_address, also behind pda.Seeds.FindAddress (CrocSwap/goSVM#28), word-at-a-time == on byte arrays (CrocSwap/goSVM#29), the exact MinimumBalanceFor fast path at thresholds 2.0 and 1.0 (CrocSwap/goSVM#30), and word compares in front of hand-written byte compare loops (CrocSwap/goSVM#31). The SDK snapshot is copied from the new CLI.
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.
Problem
==on[N]bytecompiles to an element loop that costs ~11 SBF instructions per byte, so an equal[32]bytekey pair costs ~350 CU. Keys and hashes are exactly what programs compare.Solution
For arrays of at least 8 one-byte integers (
byte,uint8,int8), the SBF build ofarrayN_equalXORs whole words (fixed-size__builtin_memcpy, one unalignedldxdwper 8 bytes) plus any tail bytes and checks the fold once. Comparing bytes has no side effects, so dropping the early exit can't be observed. Struct equality picks it up through the same helper.[N]bool, wider elements, shorter arrays and host C keep the element loop.Benchmarks
Whole transaction against main
939b215, equal inputs, same on both profiles:[32]byte, plus a struct holding one[64]byte[32]int8[8]byteAbout 400 CU saved per equal
[32]bytepair, the same as before #25. The totals are lower than on79cf25cbecause #22 turned the test's own copies into syscalls. The Hyperlane Go programs had already moved their key checks to an unrolled XOR, so there the effect is small: measured before #25, up to 1.6% less total CU per program, 584/584 tests pass.Tests
TestByteArrayEqualSBFon fogo-v0 and solana-v3: nine array types (byte at five lengths, int8 at two, plus[32]booland[8]uint16), equal and with bit 0 and bit 7 flipped at every position, 437 comparisons equal to Go.TestByteArrayEqualC: only[32]bytegets the word compare in a program that also compares[7]byte,[32]booland[8]uint16.TestCorpusMatchesPreCheckCompilerkeeps only the#elseloop (and strips perf(compiler): lower bulk copies and zeroing to sol_memmove_ and sol_memset_ #22's memory syscall blocks) and matches every recorded hash.make test: same failures asmain939b215on this machine (stdlib provenance/features tests with Go 1.23.2,TestFullSwapFactsunder/tmp).verify_rebuild.py+check_rebuild_reference.pypass locally: the full-swap reference ELFs change (clock2doesn't), soresults/compiler/2026-10-19-byte-array-equal/rebuild/metrics.jsonis the new default reference.Linked work
Closes #26, together with #27, #28, #30 and #31. If this merges first, #26 should stay open until the rest land.
Rebased onto
mainafter #25. Same family as #5 and #22 (byte loops on SBF); #31 does the same for hand-written compare loops. Used by hyperlane-xyz/hyperlane-monorepo#9852 throughhyperlane-integration-4.