mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Improve ActiveSupport::Duration
performance about 25% ~ 50%
`ActiveSupport::Duration` expects `parts` as a Hash in the internal, but
sometimes that is passed as nested array.
(e.g. `parts.map { |type, number| [type, number * other] }`)
By avoiding the extra array allocation, it makes
`ActiveSupport::Duration` performance faster about 25% ~ 50%.
```ruby
Benchmark.ips do |x|
x.report("4.seconds + 2.seconds") { 4.seconds + 2.seconds }
x.report("4.seconds - 2.seconds") { 4.seconds - 2.seconds }
x.report("4.seconds * 2") { 4.seconds * 2 }
x.report("4.seconds / 2") { 4.seconds / 2 }
end
```
Before (e36d4a03
):
```
Warming up --------------------------------------
4.seconds + 2.seconds 27.515k i/100ms
4.seconds - 2.seconds 20.783k i/100ms
4.seconds * 2 40.286k i/100ms
4.seconds / 2 41.717k i/100ms
Calculating -------------------------------------
4.seconds + 2.seconds 299.133k (±13.4%) i/s - 1.486M in 5.053953s
4.seconds - 2.seconds 222.476k (± 9.8%) i/s - 1.122M in 5.088532s
4.seconds * 2 479.650k (±10.5%) i/s - 2.377M in 5.008644s
4.seconds / 2 473.768k (±11.3%) i/s - 2.378M in 5.082413s
```
After (this change):
```
Warming up --------------------------------------
4.seconds + 2.seconds 35.856k i/100ms
4.seconds - 2.seconds 28.657k i/100ms
4.seconds * 2 51.744k i/100ms
4.seconds / 2 51.417k i/100ms
Calculating -------------------------------------
4.seconds + 2.seconds 445.972k (±21.6%) i/s - 2.080M in 5.018570s
4.seconds - 2.seconds 317.972k (±18.2%) i/s - 1.519M in 5.006564s
4.seconds * 2 608.695k (±12.8%) i/s - 3.001M in 5.014321s
4.seconds / 2 611.488k (±12.0%) i/s - 3.034M in 5.035211s
```
This commit is contained in:
parent
e36d4a0381
commit
fa0587b56d
1 changed files with 20 additions and 22 deletions
|
@ -39,7 +39,7 @@ module ActiveSupport
|
|||
|
||||
def +(other)
|
||||
if Duration === other
|
||||
seconds = value + other.parts[:seconds]
|
||||
seconds = value + other.parts.fetch(:seconds, 0)
|
||||
new_parts = other.parts.merge(seconds: seconds)
|
||||
new_value = value + other.value
|
||||
|
||||
|
@ -51,8 +51,8 @@ module ActiveSupport
|
|||
|
||||
def -(other)
|
||||
if Duration === other
|
||||
seconds = value - other.parts[:seconds]
|
||||
new_parts = other.parts.map { |part, other_value| [part, -other_value] }.to_h
|
||||
seconds = value - other.parts.fetch(:seconds, 0)
|
||||
new_parts = other.parts.transform_values(&:-@)
|
||||
new_parts = new_parts.merge(seconds: seconds)
|
||||
new_value = value - other.value
|
||||
|
||||
|
@ -64,7 +64,7 @@ module ActiveSupport
|
|||
|
||||
def *(other)
|
||||
if Duration === other
|
||||
new_parts = other.parts.map { |part, other_value| [part, value * other_value] }.to_h
|
||||
new_parts = other.parts.transform_values { |other_value| value * other_value }
|
||||
new_value = value * other.value
|
||||
|
||||
Duration.new(new_value, new_parts)
|
||||
|
@ -147,31 +147,31 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
def seconds(value) #:nodoc:
|
||||
new(value, [[:seconds, value]])
|
||||
new(value, seconds: value)
|
||||
end
|
||||
|
||||
def minutes(value) #:nodoc:
|
||||
new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
|
||||
new(value * SECONDS_PER_MINUTE, minutes: value)
|
||||
end
|
||||
|
||||
def hours(value) #:nodoc:
|
||||
new(value * SECONDS_PER_HOUR, [[:hours, value]])
|
||||
new(value * SECONDS_PER_HOUR, hours: value)
|
||||
end
|
||||
|
||||
def days(value) #:nodoc:
|
||||
new(value * SECONDS_PER_DAY, [[:days, value]])
|
||||
new(value * SECONDS_PER_DAY, days: value)
|
||||
end
|
||||
|
||||
def weeks(value) #:nodoc:
|
||||
new(value * SECONDS_PER_WEEK, [[:weeks, value]])
|
||||
new(value * SECONDS_PER_WEEK, weeks: value)
|
||||
end
|
||||
|
||||
def months(value) #:nodoc:
|
||||
new(value * SECONDS_PER_MONTH, [[:months, value]])
|
||||
new(value * SECONDS_PER_MONTH, months: value)
|
||||
end
|
||||
|
||||
def years(value) #:nodoc:
|
||||
new(value * SECONDS_PER_YEAR, [[:years, value]])
|
||||
new(value * SECONDS_PER_YEAR, years: value)
|
||||
end
|
||||
|
||||
# Creates a new Duration from a seconds value that is converted
|
||||
|
@ -210,8 +210,7 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
def initialize(value, parts) #:nodoc:
|
||||
@value, @parts = value, parts.to_h
|
||||
@parts.default = 0
|
||||
@value, @parts = value, parts
|
||||
@parts.reject! { |k, v| v.zero? } unless value == 0
|
||||
end
|
||||
|
||||
|
@ -240,13 +239,12 @@ module ActiveSupport
|
|||
# are treated as seconds.
|
||||
def +(other)
|
||||
if Duration === other
|
||||
parts = @parts.dup
|
||||
other.parts.each do |(key, value)|
|
||||
parts[key] += value
|
||||
parts = @parts.merge(other.parts) do |_key, value, other_value|
|
||||
value + other_value
|
||||
end
|
||||
Duration.new(value + other.value, parts)
|
||||
else
|
||||
seconds = @parts[:seconds] + other
|
||||
seconds = @parts.fetch(:seconds, 0) + other
|
||||
Duration.new(value + other, @parts.merge(seconds: seconds))
|
||||
end
|
||||
end
|
||||
|
@ -260,9 +258,9 @@ module ActiveSupport
|
|||
# Multiplies this Duration by a Numeric and returns a new Duration.
|
||||
def *(other)
|
||||
if Scalar === other || Duration === other
|
||||
Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
|
||||
Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
|
||||
elsif Numeric === other
|
||||
Duration.new(value * other, parts.map { |type, number| [type, number * other] })
|
||||
Duration.new(value * other, parts.transform_values { |number| number * other })
|
||||
else
|
||||
raise_type_error(other)
|
||||
end
|
||||
|
@ -271,11 +269,11 @@ module ActiveSupport
|
|||
# Divides this Duration by a Numeric and returns a new Duration.
|
||||
def /(other)
|
||||
if Scalar === other
|
||||
Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
|
||||
Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
|
||||
elsif Duration === other
|
||||
value / other.value
|
||||
elsif Numeric === other
|
||||
Duration.new(value / other, parts.map { |type, number| [type, number / other] })
|
||||
Duration.new(value / other, parts.transform_values { |number| number / other })
|
||||
else
|
||||
raise_type_error(other)
|
||||
end
|
||||
|
@ -294,7 +292,7 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
def -@ #:nodoc:
|
||||
Duration.new(-value, parts.map { |type, number| [type, -number] })
|
||||
Duration.new(-value, parts.transform_values(&:-@))
|
||||
end
|
||||
|
||||
def is_a?(klass) #:nodoc:
|
||||
|
|
Loading…
Reference in a new issue