Repository navigation
perf(runtime): read, write and compare whole words on SBF - #27
Draft
paulbalaji wants to merge 4 commits into
Draft
paulbalaji wants to merge 4 commits into
paulbalaji wants to merge 4 commits into
Conversation
The C runtime moved and compared bytes one at a time: sol_read64 and the data-length stores, the 32-byte owner/key comparisons behind ResizeAccount, CloseAccount, IsPDA and IsTokenProgram, and a staging copy of every CreateAddress result. On SBF these cost ~10 instructions per byte. SBF builds now use fixed-size __builtin_memcpy, which clang lowers to one ldxdw/stxdw per word, fold key comparisons over four words, and let sol_create_program_address write the caller's output directly (it reads every seed first and writes only on success). Host C keeps the loops. Results, codes and check order are unchanged.
paulbalaji
force-pushed
the
opt-runtime-word-access
branch
from
October 19, 2026 16:47
51ed89e to
22c5c6d
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
The C runtime moves and compares bytes one at a time:
sol_read64and the data-length stores, the 32-byte owner/key checks behindResizeAccount,CloseAccount,IsPDAandIsTokenProgram, and a staging copy of everyCreateAddressresult. On SBF that is ~9 instructions per copied byte and ~12 per compared byte. In the Hyperlane Go programs aCreateAddresscost 1,944 CU: 1,500 for the syscall, 288 for the copy. Programs that search for a bump pay the copy on every try.Solution
On SBF builds only (
__bpf__):sol_read64/sol_write64and 32-bit forms are a fixed-size__builtin_memcpy, which clang lowers to oneldxdw/stxdw. SBF allows any alignment.sol_CreateAddresspasses the caller's output straight tosol_create_program_address. The syscall reads every seed before it writes and writes only on success, so a failed derivation still leaves the output untouched, and an output that aliases the seeds gets the same result.Results, error codes and check order don't change. Host C keeps the byte loops.
Benchmarks
TestRuntimeWordsSBF, whole transaction against main939b215, same on both profiles:CreateAddresssuccessCreateAddresssuccess, output aliasing the seedsResizeAccount+CloseAccount+Rent+IsPDA14% to 26% less. The savings are within 11 CU of the pre-#25 numbers; #22's syscalls only moved the test program's own copies.
Hyperlane Go programs (hyperlane-xyz/hyperlane-monorepo#9852), measured before #25: the same source built with
hyperlane-integration-2with and without this branch, all 23 Rust functional suites (584/584 both ways): per-call median 6% less CU, total 4% to 10% less per program, none more expensive. Details inresults/compiler/2026-10-19-runtime-word-access/.Tests
TestRuntimeWordsSBFon fogo-v0 and solana-v3, 49 cases against host-computed results:CreateAddresssuccess and on-curve failure (output must survive) under 16 program keys, output aliasing the seed buffer,ResizeAccount,CloseAccountonto itself (2013),Rent(per-runner values) andIsPDA.TestCorpusMatchesPreCheckCompilerundoes exactly these template edits (and perf(compiler): lower bulk copies and zeroing to sol_memmove_ and sol_memset_ #22's memory syscall blocks) and still matches every recorded C hash, so nothing else in the emitted C moved.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 andclock2reference ELFs change, soresults/compiler/2026-10-19-runtime-word-access/rebuild/metrics.jsonis the new default reference.Linked work
Closes #26, together with #28, #29, #30 and #31. If this merges first, #26 should stay open until the rest land.
Rebased onto
mainafter #25. Complements #22 (merged in #25), which moved Go-level copies tosol_memmove_/sol_memset_; this covers the runtime's own C. Used by hyperlane-xyz/hyperlane-monorepo#9852 throughhyperlane-integration-4.