Skip to content

Conversation

flavorjones
Copy link
Member

@flavorjones flavorjones commented Aug 26, 2021

Summary

Improves the performance of ActiveSupport::NumberHelper and ActionView::Helpers::NumberHelper formatters by avoiding the use of exceptions as flow control.

Other Information

Note that Float(..., exception: false) is only available as of Ruby 2.7.

Benchmarks

I benchmarked this by calling ActiveSupport::NumberHelper.number_to_rounded with:

  • a Float
  • a valid number string
  • an invalid number string

Here are the results:

Calculating -------------------------------------
           old float     13.865k (±22.7%) i/s -    123.120k in   9.884256s
Calculating -------------------------------------
           new float     13.700k (±24.1%) i/s -    119.756k in   9.890457s

Comparison:
           old float:    13865.4 i/s
           new float:    13700.1 i/s - same-ish: difference falls within error
Calculating -------------------------------------
           old valid     13.731k (±24.8%) i/s -    120.903k in   9.884648s
Calculating -------------------------------------
           new valid     13.801k (±24.0%) i/s -    121.491k in   9.894559s

Comparison:
           new valid:    13801.3 i/s
           old valid:    13731.4 i/s - same-ish: difference falls within error
Calculating -------------------------------------
         old invalid    204.829k (±20.9%) i/s -      1.437M in   9.384316s
Calculating -------------------------------------
         new invalid    624.895k (±20.5%) i/s -      4.312M in   8.345591s

Comparison:
         new invalid:   624895.1 i/s
         old invalid:   204829.2 i/s - 3.05x  (± 0.00) slower

The new signature is comparable or slightly faster for Floats and valid strings, and much wow faster on invalid strings.

@flavorjones
Copy link
Member Author

Tagging @p8 whose idea this was. 🙌

@ghiculescu ghiculescu added the ready PRs ready to merge label Aug 26, 2021
@flavorjones flavorjones marked this pull request as draft August 26, 2021 01:29
@flavorjones flavorjones force-pushed the flavorjones-activesupport-number-converter-valid-float branch from fd1e2da to 2b831a1 Compare August 26, 2021 01:42
@rails-bot rails-bot bot added the actionview label Aug 26, 2021
@flavorjones
Copy link
Member Author

Note that I've updated this PR to make a similar modification to ActionView::Helpers::NumberHelper.

Use Kernel::Float(..., exceptions:false) instead of a rescue block in
ActionView::Helpers::NumberHelper and
ActiveSupport::NumberHelper::NumberConverter to slightly improve
performance.

Also remove documentation that incorrectly states
ActiveSupport::NumberHelper supports the `raise:` option.
@flavorjones flavorjones force-pushed the flavorjones-activesupport-number-converter-valid-float branch from 2b831a1 to 900ce92 Compare August 26, 2021 01:45
@flavorjones flavorjones changed the title Use Kernel::Float(..., exception: false) in NumberConverter#valid_float? Avoid use of exceptions to detect invalid floats Aug 26, 2021
@flavorjones flavorjones marked this pull request as ready for review August 26, 2021 02:25
@p8
Copy link
Member

p8 commented Aug 26, 2021

Nice! 😄🙌

@byroot byroot merged commit 4d3a2c0 into rails:main Aug 26, 2021
Float(number)
rescue ArgumentError, TypeError
false
Float(number, exception: false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, the return will be nil instead of false, but I don't think it's worth adding a || false.

@byroot
Copy link
Member

byroot commented Aug 26, 2021

Thanks, also the perf impact is dependant on the stack size, so that 3X could be way larger in real word scenarios.

rescue ArgumentError, TypeError
raise InvalidNumberError, number if raise_error
result = Float(number, exception: false)
raise InvalidNumberError, number if result.nil? && raise_error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi #43853

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants