Skip to content

Instantly share code, notes, and snippets.

@wojtha
Last active October 30, 2019 17:47
Show Gist options
  • Save wojtha/89eac31193e108bc79cd2fd24f1ae5f3 to your computer and use it in GitHub Desktop.
Save wojtha/89eac31193e108bc79cd2fd24f1ae5f3 to your computer and use it in GitHub Desktop.
Ruby library to measure time using the Process::CLOCK_MONOTONIC.
# Library to measure time using the Process::CLOCK_MONOTONIC.
#
# @example Usage
# timer = MicroTimer.start
# sleep 0.1 # some process to measure
# puts timer.duration.milliseconds
# sleep 0.1 # another process to measure
# puts timer.duration.seconds
#
# @example Usage with block
# duration = MicroTimer.duration do
# sleep 0.1 # some process to measure
# end
# puts duration.to_f
#
class MicroTimer
attr_reader :start
# Using Time.now to measure elapsed time is wrong.
# https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/
def self.uptime
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
def self.start
new
end
def self.duration(&block)
timer = new
block.call
timer.stop
end
def initialize
@start = MicroTimer.uptime
end
def duration
Duration.new(start, MicroTimer.uptime)
end
def stop
Duration.new(start, MicroTimer.uptime)
end
class Duration
MILLISECOND = 1_000
SECOND = 1_000_000
MINUTE = 60_000_000
HOUR = 3_600_000_000
def self.timer
start = MicroTimer.uptime
yield
finish = MicroTimer.uptime
new(start, finish)
end
attr_reader :start, :finish, :value
def initialize(start, finish)
@start = start
@finish = finish
@value = (finish - start) * 1_000_000.0
end
def hours
value / HOUR
end
alias :h :hours
def minutes
value / MINUTE
end
alias :m :minutes
def seconds
value / SECOND
end
alias :s :seconds
def milliseconds
value / MILLISECOND
end
alias :ms :milliseconds
def microseconds
value.to_i
end
alias :us :microseconds
# Fixes Duration can't be coerced into Integer (TypeError)
# see https://www.mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby/
def coerce(other)
[value, other]
end
# Fixes no implicit conversion of Duration into Integer (TypeError)
def to_i
milliseconds
end
# Fixes no implicit conversion of Duration into String (TypeError)
def to_s
milliseconds.to_s
end
alias :to_str :to_s
end
end
require 'micro_timer'
describe MicroTimer do
describe '.uptime' do
subject { MicroTimer.uptime }
it 'returns Float' do
is_expected.to be_instance_of Float
end
it 'returns uptime in seconds' do
is_expected.to be > 0
end
end
describe '.duration' do
it 'yields control' do
expect { |b| MicroTimer.duration(&b) }.to yield_control
end
it 'returns instance of MicroTimer::Duration' do
duration = MicroTimer.duration { sleep 0.05 }
expect( duration ).to be_instance_of(MicroTimer::Duration)
expect( duration.seconds ).to be_within(0.01).of(0.05)
end
end
describe '.start' do
it 'does not yield control' do
expect { |b| MicroTimer.start(&b) }.not_to yield_control
end
it 'returns an instance of MicroTimer' do
expect( MicroTimer.start ).to be_instance_of MicroTimer
end
end
describe '.stop' do
it 'does not yield control' do
expect { |b| MicroTimer.new.stop(&b) }.not_to yield_control
end
it 'returns instance of MicroTimer::Duration' do
timer = MicroTimer.start
expect( timer.stop ).to be_instance_of(MicroTimer::Duration)
end
it 'returns new instance of MicroTimer::Duration each time' do
timer = MicroTimer.start
duration1 = timer.stop
sleep 0.01
duration2 = timer.stop
expect( duration1.start ).to eq duration2.start
expect( duration1.finish ).to be < duration2.finish
expect( duration1.value ).to be < duration2.value
end
end
end
describe MicroTimer::Duration do
describe 'time conversion' do
let(:duration) { MicroTimer::Duration.new(0.000, 3_600.900) }
specify '#microseconds (Integer)' do
expect( duration.microseconds ).to be_instance_of Integer
expect( duration.microseconds ).to eq 3_600_900_000
end
specify '#milliseconds (Float)' do
expect( duration.milliseconds ).to be_instance_of Float
expect( duration.milliseconds ).to eq 3_600_900.0
end
specify '#seconds (Float)' do
expect( duration.seconds ).to be_instance_of Float
expect( duration.seconds ).to eq 3_600.900
end
specify '#minutes (Float)' do
expect( duration.minutes ).to be_instance_of Float
expect( duration.minutes ).to eq 60.015
end
specify '#hours (Float)' do
expect( duration.hours ).to be_instance_of Float
expect( duration.hours ).to eq 1.000_25
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment