Repository navigation
perf: int64 fast path for integer conversions (1.65x, math/big eliminated) - #1
Merged
Merged
Conversation
…ated)
Every integer conversion (%d/%i/%u/%x/%X/%o/%b/%B) routed its operand
through math/big.Int, even a small int64 like 12345: the arg was boxed
into a *big.Int, then Abs + Text allocated two more big values per
directive. A CPU/heap profile of a mixed sprintf workload showed the
work was dominated by GC pressure from these allocations, not by parsing
the format string (parsing is <1% of CPU).
Add an allocation-free int64 path:
* intFast, an optional Value fast-path (Int64Fast) that reports an
int64-range integer without allocating a *big.Int. renderInt uses it
and only falls back to the *big.Int path for Bignums, Floats, String
operands, and the negative-in-unsigned-base two's-complement case
(which genuinely needs arbitrary precision).
* goValue stores small integers as an int64, building a *big.Int only
for magnitudes that exceed int64.
* Pre-size the output Builder to avoid its early re-growths.
renderIntBig is the former renderInt body verbatim, so the arbitrary-
precision output is byte-identical; the shared precision/width/pad tail
is factored into emitInt. The live MRI 4.0.5 differential and golden
suites stay green, and new coverage cases pin the Bignum slow path to
CRuby byte-for-byte.
Benchmark (mixed "%05d|%-8s|%8.3f|%x|%o|%+d|%e"):
before 865 ns/op, 39 allocs/op
after 543 ns/op, 16 allocs/op
100% statement coverage retained.
Co-Authored-By: Claude Opus 4.8 <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
A CPU + heap profile of a mixed sprintf workload (
"%05d|%-8s|%8.3f|%x|%o|%+d|%e") showed the formatter was GC-bound:runtime.kevent/runtime.madviseaccounted for ~65% of CPU, all driven by allocations frommath/big. Every integer conversion (%d/%i/%u/%x/%X/%o/%b/%B) routed its operand through a*big.Int— even a smallint64like12345— boxing the arg into a*big.Intand then allocating two more big values (Abs,Text) per directive. Parsing the format string was <1% of CPU, so the re-parse was never the bottleneck.Change
An allocation-free
int64path:intFast— an optionalValuefast-path (Int64Fast() (int64, bool)) reporting an int64-range integer without allocating a*big.Int.renderIntuses it, falling back to the*big.Intpath only for Bignums, Floats, String operands, and the negative-in-unsigned-base two's-complement case (which genuinely needs arbitrary precision).goValuestores small integers as anint64, building a*big.Intonly for magnitudes that exceedint64.strings.Builderto avoid its early re-growths.renderIntBigis the formerrenderIntbody verbatim, so arbitrary-precision output is byte-identical; the shared precision/width/pad tail is factored intoemitInt.Correctness
TestDifferentialAgainstMRI) and golden suites stay green — small integers now exercise the new fast path and are validated byte-for-byte against CRuby.TestBignumSlowPathpins the Bignum path to CRuby 4.0.5 output.go test -race ./...green; cross-builds green on all 6 64-bit arches (amd64/arm64/riscv64/loong64/ppc64le/s390x) + windows.Benchmark
Mixed
"%05d|%-8s|%8.3f|%x|%o|%+d|%e":Measured on the rbgo object path (bypassing the interpreter): 688 -> 418 ns/op, 32 -> 11 allocs/op (1.65x).
🤖 Generated with Claude Code