Skip to content

Instantly share code, notes, and snippets.

@Envek
Last active January 8, 2017 23:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Envek/d0763db337d34ef78bf8d0a1aceca9aa to your computer and use it in GitHub Desktop.
Save Envek/d0763db337d34ef78bf8d0a1aceca9aa to your computer and use it in GitHub Desktop.
Benchmark various variants of parsing durations in Rails' ActiveSupport
require 'benchmark/ips'
require 'active_support/duration'
EPOCH = ::Time.utc(2000)
PARTS_IN_SECONDS = {
seconds: 1, # Used in parse method for ease of handling part hashes with seconds
minutes: 60,
hours: 60 * 60,
days: 24 * 60 * 60,
weeks: 7 * 24 * 60 * 60,
months: 30 * 24 * 60 * 60,
years: 365.25 * 24 * 60 * 60,
}.freeze
def parse_initial(iso8601duration)
parts = ActiveSupport::Duration::ISO8601Parser.new(iso8601duration).parse!
time = ::Time.current
ActiveSupport::Duration.new(time.advance(parts) - time, parts)
end
def parse_epoch(iso8601duration)
parts = ActiveSupport::Duration::ISO8601Parser.new(iso8601duration).parse!
ActiveSupport::Duration.new(EPOCH.advance(parts) - EPOCH, parts)
end
def parse_inject_parts(iso8601duration)
parts = ActiveSupport::Duration::ISO8601Parser.new(iso8601duration).parse!
total_seconds = parts.inject(0) do |total, (part, value)|
total + value * PARTS_IN_SECONDS[part]
end
ActiveSupport::Duration.new(total_seconds, parts)
end
def year_initial
ActiveSupport::Duration.new(1 * 365.25.days.to_i, [[:years, 1]])
end
def year_with_parts
ActiveSupport::Duration.new(1 * ActiveSupport::Duration::PARTS_IN_SECONDS[:years].to_i, [[:years, 1]])
end
ISO8601_SAMPLE = 'P1Y1M1DT1H1M1.0S'
Benchmark.ips do |x|
x.report("Initial parse with TimeWithZone#advance") { parse_initial(ISO8601_SAMPLE) }
x.report("Parse with Time#advance from fixed EPOCH") { parse_epoch(ISO8601_SAMPLE) }
x.report("Parse using sum of mapping parts to seconds") { parse_inject_parts(ISO8601_SAMPLE) }
x.compare!
end
Benchmark.ips do |x|
x.report("Initial 1.year with many intermediate durations") { year_initial }
x.report("1.year2 with mapping duration to seconds") { year_with_parts }
x.compare!
end
Warming up --------------------------------------
Initial parse with TimeWithZone#advance
1.050k i/100ms
Parse with Time#advance from fixed EPOCH
1.683k i/100ms
Parse using sum of mapping parts to seconds
2.695k i/100ms
Calculating -------------------------------------
Initial parse with TimeWithZone#advance
10.651k (± 2.8%) i/s - 53.550k in 5.031816s
Parse with Time#advance from fixed EPOCH
17.252k (± 5.2%) i/s - 87.516k in 5.088071s
Parse using sum of mapping parts to seconds
27.991k (± 2.3%) i/s - 140.140k in 5.009231s
Comparison:
Parse using sum of mapping parts to seconds: 27991.0 i/s
Parse with Time#advance from fixed EPOCH: 17252.5 i/s - 1.62x slower
Initial parse with TimeWithZone#advance: 10650.5 i/s - 2.63x slower
Warming up --------------------------------------
Initial 1.year with many intermediate durations
27.250k i/100ms
1.year2 with mapping duration to seconds
44.715k i/100ms
Calculating -------------------------------------
Initial 1.year with many intermediate durations
325.580k (± 4.6%) i/s - 1.635M in 5.032719s
1.year2 with mapping duration to seconds
672.955k (± 4.7%) i/s - 3.398M in 5.060281s
Comparison:
1.year2 with mapping duration to seconds: 672955.2 i/s
Initial 1.year with many intermediate durations: 325579.6 i/s - 2.07x slower
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment