Skip to content

perf(sdk): skip the rent rounding emulation at thresholds 2.0 and 1.0 - #30

Draft
paulbalaji wants to merge 4 commits into
CrocSwap:mainfrom
paulbalaji:opt-rent-fast-path
Draft

paulbalaji wants to merge 4 commits into
CrocSwap:mainfrom
paulbalaji:opt-rent-fast-path

Conversation

@paulbalaji

@paulbalaji paulbalaji commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Problem

system.MinimumBalanceFor reproduces Rust's u64 -> f64 conversion, f64 multiply and saturating cast with integer code, so SBF needs no float helpers. That costs about 500 to 770 CU per call, mostly in the bit-length loops and the two rounding stages, and every rent-exempt account creation pays it. The pinned runners and current clusters all use a threshold of 2.0 (Fogo, Solana before SIMD-0194) or 1.0 (after it).

Solution

When the threshold bits are exactly 2.0 or 1.0 and n = (128+size)*rate is below 2^53, the conversion is exact and so is the product, so MinimumBalanceFor returns 2n or n. The size and overflow checks still run first. Every other input takes the unchanged emulation, now minimumBalanceExact. MinimumBalance gets it through MinimumBalanceFor. Hyperlane's Go library ships the same shortcut (hyperlane-xyz/hyperlane-monorepo#9852).

Benchmarks

TestRentSBF, whole transaction against main 939b215. The probe's own decoding and output cost ~260 CU of each total.

call fogo-v0 main this PR solana-v3 main this PR
MinimumBalance(165), runner sysvar (2.0 / 1.0) 889 399 897 396
MinimumBalanceFor(3480, 2.0, 165) 763 273 763 273
MinimumBalanceFor at 2.0, n = 2^53 - 128 1,029 273 1,029 273
MinimumBalanceFor(3480, 2.5, 165), exact path 759 767 759 767

482 to 759 CU less on every fast-path input with a nonzero product. The exact path costs 2 to 8 CU more (the threshold compares and a repeated bounds check). Errors caught before the threshold check are unchanged.

Tests

  • TestRentFastPathMatchesExact (native): MinimumBalanceFor equals the emulation alone on both sides of n = 2^53 and of uint64 overflow for every size limit, both thresholds and their neighbouring bit patterns, negative zeros, other thresholds and NaN, plus a million random inputs near the 2^53 boundary, errors included. The existing IEEE oracle test passes unchanged.
  • TestRentSBF on fogo-v0 and solana-v3: 532 cases (thresholds 2.0, 1.0, their neighbours, 2.5, 0.5, zeros, -1, infinity, NaN; sizes 0 to 10 MiB + 1 and 2^64 - 1; rates 0, 3,480, 6,960, the 2^53 and overflow limits and one past each) plus MinimumBalance from each runner's Rent sysvar, every result and error code equal to the host's.
  • SDK-2 example snapshots of sdk/system/rent.go refreshed, so existing SDK-2 projects need re-scaffolding. Corpus C hashes unchanged.
  • make test: same failures as main 939b215 on this machine (stdlib provenance/features tests with Go 1.23.2, TestFullSwapFacts under /tmp).
  • verify_rebuild.py + check_rebuild_reference.py pass locally: the full-swap reference ELFs change (clock2 doesn't), so results/sdk/2026-10-19-rent-fast-path/rebuild/metrics.json is the new default reference.

Linked work

Closes #26 (the MinimumBalanceFor row). #27, #28, #29 and #31 cover the other rows; if this merges first, #26 should stay open until the rest land.

Used by hyperlane-xyz/hyperlane-monorepo#9852 through hyperlane-integration-4.

MinimumBalanceFor emulates Rust's u64 -> f64 conversion and f64
multiply with integer code, about 500 to 770 CU per call on SBF. With
a threshold of exactly 2.0 or 1.0 and (128 + size) * rate below 2^53,
both steps are exact, so the result is 2n or n. Other inputs keep the
emulation, and a test checks both paths agree at every boundary,
errors included. The SDK-2 example snapshots of sdk/system/rent.go are
refreshed.
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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Byte-at-a-time runtime and codegen add 0.3k to 4k CU to small instructions

1 participant