Conversation
Three micro-optimizations on the hot construction path:
- Cache zero Money instances in a private ZERO_MONEY constant instead
of re-testing @@zero_money ||= {} on every zero-valued call.
- initialize: a single finite? check replaces separate nan? and
infinite? calls, and rounding is skipped entirely when the value is
already within the currency's minor units (value.scale check), which
avoids one BigDecimal allocation per call.
- new_from_money: passing the money's own ISO code string (a common
no-op conversion) returns the existing instance without any currency
lookup.
Benchmark (mixed-input Money.new): 787.6 -> 706.0 ns/op (-10.4%),
allocations 3.80 -> 2.87 per call (-24.6%).
This branch has not been deployed
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.
What
Three micro-optimizations on the
Money.newconstruction path. No behavior change.ZERO_MONEY(private constant) replaces the@@zero_money ||= {}re-test that ran on every zero-valued call.initialize: a singlefinite?check replaces separatenan?+infinite?calls, and rounding is skipped entirely whenvalue.scale <= currency.minor_units— the value is already within precision, soroundwould just allocate an identical BigDecimal. (When rounding does happen, theBigDecimal()wrap stays:BigDecimal#round(0)returns anInteger, which would break zero-minor-unit currencies like JPY.)new_from_money: passing a money's own ISO code string (a common no-op conversion, e.g.Money.new(money, "USD")) returns the existing instance without any currency lookup.Performance
Measured on Ruby 4.0.1 (arm64-darwin), median of 7 trials, mixed-input
Money.newworkload:The Money-passthrough case alone improves ~65%. Part of a series of independent
Money.newoptimizations; combined they reach ~-38% wall time and -45% allocations on this workload.Correctness
Money#valuecan be a rounding-produced-0.0; the scale fast path preserves that behavior.Benchmark script (save as
bench.rb, runruby -Ilib bench.rbon each ref)