Skip to content

Optimize BigDecimal#to_s#519

Merged
mrkn merged 1 commit intoruby:masterfrom
byroot:optimize-to-s
Apr 6, 2026
Merged

Optimize BigDecimal#to_s#519
mrkn merged 1 commit intoruby:masterfrom
byroot:optimize-to-s

Conversation

@byroot
Copy link
Copy Markdown
Member

@byroot byroot commented Apr 6, 2026

Most of the time was spent in two calls to snprintf, by a simpler integer to ASCII function, it can be made several time faster.

The code is largely adapted from an earlier version of ruby/json. ruby/json now use a much more optimized algorithm, but there are licensing consideration so not sure it's worth optimizing that much.

Before:

ruby 4.0.2 (2026-03-17 revision d3da9fec82) +YJIT +PRISM [arm64-darwin25]
Warming up --------------------------------------
               22.99   408.215k i/100ms
               large   230.802k i/100ms
Calculating -------------------------------------
               22.99      4.214M (± 2.2%) i/s  (237.32 ns/i) -     21.227M in   5.040152s
               large      2.384M (± 2.6%) i/s  (419.45 ns/i) -     12.002M in   5.037698s

After:

ruby 4.0.2 (2026-03-17 revision d3da9fec82) +YJIT +PRISM [arm64-darwin25]
Warming up --------------------------------------
               22.99     1.026M i/100ms
               large   846.057k i/100ms
Calculating -------------------------------------
               22.99     10.882M (± 0.8%) i/s   (91.89 ns/i) -     55.426M in   5.093603s
               large      9.094M (± 1.0%) i/s  (109.97 ns/i) -     45.687M in   5.024549s
require "bundler/inline"

gemfile do
  source 'https://rubygems.org'
  gem "benchmark-ips"
  gem "bigdecimal", path: "/Users/byroot/src/github.com/byroot/bigdecimal"
end

small = BigDecimal("29.99")
large = BigDecimal("32423094234234.23423432")
Benchmark.ips do |x|
  x.report("22.99") { small.to_s }
  x.report("large") { large.to_s }
end

Most of the time was spent in two calls to `snprintf`, by a
simpler integer to ASCII function, it can be made several time faster.

The code is largely adapted from an earlier version of ruby/json.
ruby/json now use a much more optimized algorithm, but there are
licensing consideration so not sure it's worth optimizing that much.

Before:

```
ruby 4.0.2 (2026-03-17 revision d3da9fec82) +YJIT +PRISM [arm64-darwin25]
Warming up --------------------------------------
               22.99   408.215k i/100ms
               large   230.802k i/100ms
Calculating -------------------------------------
               22.99      4.214M (± 2.2%) i/s  (237.32 ns/i) -     21.227M in   5.040152s
               large      2.384M (± 2.6%) i/s  (419.45 ns/i) -     12.002M in   5.037698s
```

After:

```
ruby 4.0.2 (2026-03-17 revision d3da9fec82) +YJIT +PRISM [arm64-darwin25]
Warming up --------------------------------------
               22.99     1.026M i/100ms
               large   846.057k i/100ms
Calculating -------------------------------------
               22.99     10.882M (± 0.8%) i/s   (91.89 ns/i) -     55.426M in   5.093603s
               large      9.094M (± 1.0%) i/s  (109.97 ns/i) -     45.687M in   5.024549s
```

```ruby
require "bundler/inline"

gemfile do
  source 'https://rubygems.org'
  gem "benchmark-ips"
  gem "bigdecimal", path: "/Users/byroot/src/github.com/byroot/bigdecimal"
end

small = BigDecimal("29.99")
large = BigDecimal("32423094234234.23423432")
Benchmark.ips do |x|
  x.report("22.99") { small.to_s }
  x.report("large") { large.to_s }
end
```
@mrkn
Copy link
Copy Markdown
Member

mrkn commented Apr 6, 2026

@byroot Thanks a lot.

@mrkn mrkn merged commit b66add1 into ruby:master Apr 6, 2026
84 checks passed
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.

2 participants