Skip to content

Commit b55f22f

Browse files
committed
Refactored monotonic clock.
1 parent 7881ac9 commit b55f22f

File tree

4 files changed

+79
-117
lines changed

4 files changed

+79
-117
lines changed

examples/test_clock_variations.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env ruby
2+
3+
$: << File.expand_path('./lib', __FILE__)
4+
5+
require 'benchmark'
6+
require 'thread'
7+
8+
class MonotonicClock
9+
def initialize
10+
@mutex = Mutex.new
11+
@last_time = Time.now.to_f
12+
end
13+
14+
def get_time_native
15+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
16+
end
17+
18+
def get_interval_native(since)
19+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - since.to_f
20+
end
21+
22+
def get_time_ruby
23+
@mutex.synchronize do
24+
now = Time.now.to_f
25+
if @last_time < now
26+
@last_time = now
27+
else # clock has moved back in time
28+
@last_time += 0.000_001
29+
end
30+
end
31+
end
32+
33+
def get_interval_ruby(since)
34+
get_time_ruby - since.to_f
35+
end
36+
end
37+
38+
COUNT = 2_000_000
39+
CLOCK = MonotonicClock.new
40+
41+
native_now = CLOCK.get_time_native
42+
ruby_now = CLOCK.get_time_ruby
43+
44+
puts "Native: #{native_now}, Ruby: #{ruby_now}"
45+
46+
Benchmark.bmbm do |bm|
47+
48+
bm.report('Native time') do
49+
COUNT.times{ CLOCK.get_time_native }
50+
end
51+
52+
bm.report('Ruby time') do
53+
COUNT.times{ CLOCK.get_time_ruby }
54+
end
55+
56+
bm.report('Native interval') do
57+
COUNT.times{ CLOCK.get_interval_native(native_now) }
58+
end
59+
60+
bm.report('Ruby interval') do
61+
COUNT.times{ CLOCK.get_interval_ruby(ruby_now) }
62+
end
63+
end
64+
65+
puts "Native: #{CLOCK.get_time_native}, Ruby: #{CLOCK.get_time_ruby}"

lib/concurrent/utility/monotonic_time.rb

Lines changed: 6 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,19 @@ def get_time
2222
# @!visibility private
2323
def initialize
2424
@mutex = Mutex.new
25-
@correction = 0
2625
@last_time = Time.now.to_f
2726
end
2827

2928
# @!visibility private
3029
def get_time
31-
@mutex.synchronize {
32-
@correction ||= 0 # compensating any back time shifts
30+
@mutex.synchronize do
3331
now = Time.now.to_f
34-
corrected_now = now + @correction
35-
if @last_time < corrected_now
36-
@last_time = corrected_now
37-
else
38-
@correction += @last_time - corrected_now + 0.000_001
39-
@last_time = @correction + now
32+
if @last_time < now
33+
@last_time = now
34+
else # clock has moved back in time
35+
@last_time += 0.000_001
4036
end
41-
}
37+
end
4238
end
4339
end
4440
}.new
@@ -55,109 +51,4 @@ def monotonic_time
5551
GLOBAL_MONOTONIC_CLOCK.get_time
5652
end
5753
module_function :monotonic_time
58-
59-
# Runs the given block and returns the number of seconds that elapsed.
60-
#
61-
# @yield the block to run and time
62-
# @return [Float] the number of seconds the block took to run
63-
#
64-
# @raise [ArgumentError] when no block given
65-
#
66-
# @!macro monotonic_clock_warning
67-
def monotonic_interval
68-
raise ArgumentError.new('no block given') unless block_given?
69-
start_time = GLOBAL_MONOTONIC_CLOCK.get_time
70-
yield
71-
GLOBAL_MONOTONIC_CLOCK.get_time - start_time
72-
end
73-
module_function :monotonic_interval
7454
end
75-
76-
__END__
77-
78-
#!/usr/bin/env ruby
79-
80-
# $ ./time_test.rb
81-
# Native: 1735.94062338, Ruby: 1425391307.2322402
82-
# user system total real
83-
# Native time...
84-
# 0.310000 0.000000 0.310000 ( 0.306102)
85-
# Ruby time...
86-
# 1.750000 0.000000 1.750000 ( 1.757991)
87-
# Native interval...
88-
# 0.360000 0.010000 0.370000 ( 0.358779)
89-
# Ruby interval...
90-
# 1.850000 0.000000 1.850000 ( 1.857620)
91-
# Native: 1740.221591108, Ruby: 1425391312.2985182
92-
93-
$: << File.expand_path('./lib', __FILE__)
94-
95-
require 'benchmark'
96-
require 'thread'
97-
98-
class MonotonicClock
99-
def initialize
100-
@mutex = Mutex.new
101-
@correction = 0
102-
@last_time = Time.now.to_f
103-
end
104-
105-
def get_time_native
106-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
107-
end
108-
109-
def get_interval_native(since)
110-
Process.clock_gettime(Process::CLOCK_MONOTONIC) - since.to_f
111-
end
112-
113-
def get_time_ruby
114-
@mutex.synchronize do
115-
@correction ||= 0 # compensating any back time shifts
116-
now = Time.now.to_f
117-
corrected_now = now + @correction
118-
if @last_time < corrected_now
119-
return @last_time = corrected_now
120-
else
121-
@correction += @last_time - corrected_now + 0.000_001
122-
return @last_time = @correction + now
123-
end
124-
end
125-
end
126-
127-
def get_interval_ruby(since)
128-
get_time_ruby - since.to_f
129-
end
130-
end
131-
132-
COUNT = 2_000_000
133-
CLOCK = MonotonicClock.new
134-
135-
native_now = CLOCK.get_time_native
136-
ruby_now = CLOCK.get_time_ruby
137-
138-
puts "Native: #{native_now}, Ruby: #{ruby_now}"
139-
140-
Benchmark.bm do |bm|
141-
142-
puts "Native time..."
143-
bm.report do
144-
COUNT.times{ CLOCK.get_time_native }
145-
end
146-
147-
puts "Ruby time..."
148-
bm.report do
149-
COUNT.times{ CLOCK.get_time_ruby }
150-
end
151-
152-
puts "Native interval..."
153-
bm.report do
154-
COUNT.times{ CLOCK.get_interval_native(native_now) }
155-
end
156-
157-
puts "Ruby interval..."
158-
bm.report do
159-
COUNT.times{ CLOCK.get_interval_ruby(ruby_now) }
160-
end
161-
end
162-
163-
puts "Native: #{CLOCK.get_time_native}, Ruby: #{CLOCK.get_time_ruby}"

spec/concurrent/exchanger_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ module Concurrent
5656
context 'with timeout' do
5757

5858
it 'should block until timeout' do
59-
duration = Concurrent.monotonic_interval do
59+
duration = Concurrent::TestHelpers.monotonic_interval do
6060
subject.exchange(2, 0.1)
6161
end
6262
expect(duration).to be_within(0.05).of(0.1)

spec/support/example_group_extensions.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
module Concurrent
55
module TestHelpers
6+
extend self
67

78
def delta(v1, v2)
89
if block_given?
@@ -62,7 +63,12 @@ def kill_rogue_threads(warning = true)
6263
@@killed = true
6364
end
6465

65-
extend self
66+
def monotonic_interval
67+
raise ArgumentError.new('no block given') unless block_given?
68+
start_time = GLOBAL_MONOTONIC_CLOCK.get_time
69+
yield
70+
GLOBAL_MONOTONIC_CLOCK.get_time - start_time
71+
end
6672
end
6773
end
6874

0 commit comments

Comments
 (0)