-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Duration#negative?
without calling public_send
#50136
Conversation
Signed-off-by: Alexandre Terrasa <alexandre.terrasa@shopify.com>
8ed0079
to
aa6d3b6
Compare
def negative? | ||
@value < 0 | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also benchmark using delegate :negative?, to: :@value
?
This will be slower than your solution, but I have an idea how to optimize it that would allow to more easily eagerly define much more than just negative?
without lots of extra code.
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'benchmark-ips'
gem 'activesupport'
end
require 'active_support/all'
class ActiveSupport::Duration
delegate :negative?, to: :@value, prefix: :delegate
def negative_native?
@value < 0
end
def raw_delegate_negative?
@value.negative?
end
end
values = 1000.times.map do |i|
rand(-100..100).hours
end
puts RUBY_DESCRIPTION
Benchmark.ips do |x|
x.report("negative?") do
values.each do |v|
v.negative?
end
end
x.report("delegate_negative?") do
values.each do |v|
v.delegate_negative?
end
end
x.report("raw_delegate") do
values.each do |v|
v.raw_delegate_negative?
end
end
x.report("negative_native?") do
values.each do |v|
v.negative_native?
end
end
x.compare!(order: :baseline)
end
So answering my own question, right now So we could implement a |
assert_not 1.hours.negative? | ||
assert_not 1.days.negative? | ||
assert_not (1.weeks - 1.days).negative? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no rubocop rule for that but generally for the same reason why we prefer assert_predicate
over assert
we should be using assert_not_predicate
as it produces a better error message. Even though assert_not_predicate
may not sound very natural I think having better test failure message overweights
assert_not 1.hours.negative? | |
assert_not 1.days.negative? | |
assert_not (1.weeks - 1.days).negative? | |
assert_not_predicate 1.hours, :negative? | |
assert_not_predicate 1.days, :negative? | |
assert_not_predicate (1.weeks - 1.days), :negative? |
(In case you're suggesting we delegate all Numeric methods without a way to |
Yeah, we can perfectly pick and chose the ones that make sense. |
Fix: rails#50136 Using delegate saves on the `method_missing + public_send` combo. I chose to delegate to all the methods I saw called in Rails own test suite, but there is likely a handful more candidates for explicit delegation. But also this rely on a new (private) parameter in `Module#delegate` to provide the expected delegated type and use that to define delegators with the extact required signature. This saves on array and hash allocations caused by splatting. In some ways it's a continuation of rails#46875 Note that I didn't make the new `as:` parameter public, as I fear it's a bit too brittle to be used. For the same reason I'm considering reverting the optimized path behavior on `to: :class` and requiring to explictly pass `as: self` for that optimized path. Co-Authored-By: Alexandre Terrasa <alexandre.terrasa@shopify.com>
Fix: rails#50136 Using delegate saves on the `method_missing + public_send` combo. I chose to delegate to all the methods I saw called in Rails own test suite, but there is likely a handful more candidates for explicit delegation. But also this rely on a new (private) parameter in `Module#delegate` to provide the expected delegated type and use that to define delegators with the extact required signature. This saves on array and hash allocations caused by splatting. In some ways it's a continuation of rails#46875 Note that I didn't make the new `as:` parameter public, as I fear it's a bit too brittle to be used. For the same reason I'm considering reverting the optimized path behavior on `to: :class` and requiring to explictly pass `as: self` for that optimized path. Co-Authored-By: Alexandre Terrasa <alexandre.terrasa@shopify.com>
Fix: rails#50136 Using delegate saves on the `method_missing + public_send` combo. I chose to delegate to all the methods I saw called in Rails own test suite, but there is likely a handful more candidates for explicit delegation. But also this rely on a new (private) parameter in `Module#delegate` to provide the expected delegated type and use that to define delegators with the extact required signature. This saves on array and hash allocations caused by splatting. In some ways it's a continuation of rails#46875 Note that I didn't make the new `as:` parameter public, as I fear it's a bit too brittle to be used. For the same reason I'm considering reverting the optimized path behavior on `to: :class` and requiring to explictly pass `as: self` for that optimized path. Co-Authored-By: Alexandre Terrasa <alexandre.terrasa@shopify.com>
Fix: rails#50136 Using delegate saves on the `method_missing + public_send` combo. I chose to delegate to all the methods I saw called in Rails own test suite, but there is likely a handful more candidates for explicit delegation. But also this rely on a new (private) parameter in `Module#delegate` to provide the expected delegated type and use that to define delegators with the extact required signature. This saves on array and hash allocations caused by splatting. In some ways it's a continuation of rails#46875 Note that I didn't make the new `as:` parameter public, as I fear it's a bit too brittle to be used. For the same reason I'm considering reverting the optimized path behavior on `to: :class` and requiring to explictly pass `as: self` for that optimized path. Co-Authored-By: Alexandre Terrasa <alexandre.terrasa@shopify.com>
Fix: rails#50136 Using delegate saves on the `method_missing + public_send` combo. I chose to delegate to all the methods I saw called in Rails own test suite, but there is likely a handful more candidates for explicit delegation. But also this rely on a new (private) parameter in `Module#delegate` to provide the expected delegated type and use that to define delegators with the extact required signature. This saves on array and hash allocations caused by splatting. In some ways it's a continuation of rails#46875 Note that I didn't make the new `as:` parameter public, as I fear it's a bit too brittle to be used. For the same reason I'm considering reverting the optimized path behavior on `to: :class` and requiring to explictly pass `as: self` for that optimized path. Co-Authored-By: Alexandre Terrasa <alexandre.terrasa@shopify.com>
Motivation / Background
Inspired by @casperisfine change in #49909.
For codebases using
ActiveSupport::Duration
extensively, checking if the duration is negative currently requires going through themethod_missing
mechanism and a `public_send`` call which can be quite slow.Instead, we can implement the method natively and get the check executed almost 4x faster.
Here's some benchmark:
Detail
This pull request implements
ActiveSupport::Duration#negative?
without relying onmethod_missing
andpublic_send
.Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]