Skip to content

Commit 360ef10

Browse files
committed
Safer Async, with benchmarks.
1 parent 5287826 commit 360ef10

File tree

3 files changed

+99
-68
lines changed

3 files changed

+99
-68
lines changed

examples/benchmark_async.rb

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env ruby
22

3-
$: << File.expand_path('../../lib', __FILE__)
3+
#$: << File.expand_path('../../lib', __FILE__)
44

55
require 'benchmark'
66
require 'benchmark/ips'
@@ -10,48 +10,117 @@
1010

1111
class CelluloidClass
1212
include Celluloid
13-
def foo
14-
end
15-
def bar(latch)
16-
latch.count_down
13+
def foo(latch = nil)
14+
latch.count_down if latch
1715
end
1816
end
1917

2018
class AsyncClass
2119
include Concurrent::Async
22-
def foo
23-
end
24-
def bar(latch)
25-
latch.count_down
20+
def foo(latch = nil)
21+
latch.count_down if latch
2622
end
2723
end
2824

2925
IPS_NUM = 100
3026
BMBM_NUM = 100_000
3127

3228
Benchmark.ips do |bm|
33-
latch = Concurrent::CountDownLatch.new(1)
3429
celluloid = CelluloidClass.new
3530
bm.report('celluloid') do
36-
IPS_NUM.times { celluloid.async.foo }
37-
celluloid.bar(latch)
31+
latch = Concurrent::CountDownLatch.new(IPS_NUM)
32+
IPS_NUM.times { celluloid.async.foo(latch) }
3833
latch.wait
3934
end
4035

4136
async = AsyncClass.new
42-
latch = Concurrent::CountDownLatch.new(1)
4337
bm.report('async') do
44-
IPS_NUM.times { async.async.foo }
45-
async.bar(latch)
38+
latch = Concurrent::CountDownLatch.new(IPS_NUM)
39+
IPS_NUM.times { async.async.foo(latch) }
4640
latch.wait
4741
end
4842

4943
bm.compare!
5044
end
5145

52-
#Benchmark.bmbm do |bm|
53-
#bm.report('celluloid') do
54-
#end
55-
#bm.report('async') do
56-
#end
57-
#end
46+
Benchmark.bmbm do |bm|
47+
celluloid = CelluloidClass.new
48+
bm.report('celluloid') do
49+
latch = Concurrent::CountDownLatch.new(BMBM_NUM)
50+
BMBM_NUM.times { celluloid.async.foo(latch) }
51+
latch.wait
52+
end
53+
54+
async = AsyncClass.new
55+
bm.report('async') do
56+
latch = Concurrent::CountDownLatch.new(BMBM_NUM)
57+
BMBM_NUM.times { async.async.foo(latch) }
58+
latch.wait
59+
end
60+
end
61+
62+
__END__
63+
64+
===========================================================
65+
Async Benchmarks
66+
===========================================================
67+
68+
Computer:
69+
70+
* OS X Yosemite
71+
- Version 10.10.4
72+
* MacBook Pro
73+
- Retina, 13-inch, Early 2015
74+
* Processor 3.1 GHz Intel Core i7
75+
* Memory 16 GB 1867 MHz DDR3
76+
* Physical Volumes:
77+
- Apple SSD SM0512G
78+
- 500 GB
79+
80+
===========================================================
81+
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
82+
===========================================================
83+
84+
Calculating -------------------------------------
85+
celluloid 26.000 i/100ms
86+
async 47.000 i/100ms
87+
-------------------------------------------------
88+
celluloid 279.912 (± 6.1%) i/s - 1.404k
89+
async 478.932 (± 2.1%) i/s - 2.397k
90+
91+
Comparison:
92+
async: 478.9 i/s
93+
celluloid: 279.9 i/s - 1.71x slower
94+
95+
Rehearsal ---------------------------------------------
96+
celluloid 4.080000 0.620000 4.700000 ( 4.695271)
97+
async 2.280000 0.100000 2.380000 ( 2.345327)
98+
------------------------------------ total: 7.080000sec
99+
100+
user system total real
101+
celluloid 3.910000 0.580000 4.490000 ( 4.503884)
102+
async 2.220000 0.190000 2.410000 ( 2.340467)
103+
104+
===========================================================
105+
jruby 1.7.19 (1.9.3p551) 2015-01-29 20786bd on Java HotSpot(TM) 64-Bit Server VM 1.8.0_45-b14 +jit [darwin-x86_64]
106+
===========================================================
107+
108+
Calculating -------------------------------------
109+
celluloid 2.000 i/100ms
110+
async 32.000 i/100ms
111+
-------------------------------------------------
112+
celluloid 72.887 (±26.1%) i/s - 334.000
113+
async 1.822k (±31.6%) i/s - 6.368k
114+
115+
Comparison:
116+
async: 1821.9 i/s
117+
celluloid: 72.9 i/s - 25.00x slower
118+
119+
Rehearsal ---------------------------------------------
120+
celluloid 8.890000 1.700000 10.590000 ( 5.930000)
121+
async 2.250000 0.150000 2.400000 ( 1.283000)
122+
----------------------------------- total: 12.990000sec
123+
124+
user system total real
125+
celluloid 6.310000 1.530000 7.840000 ( 5.817000)
126+
async 1.590000 0.060000 1.650000 ( 0.912000)

lib/concurrent/async.rb

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
require 'thread'
2-
require 'concurrent/configuration'
3-
require 'concurrent/errors'
41
require 'concurrent/ivar'
52
require 'concurrent/executor/single_thread_executor'
63

@@ -332,21 +329,16 @@ def method_missing(method, *args, &block)
332329
super unless @delegate.respond_to?(method)
333330
Async::validate_argc(@delegate, method, *args)
334331

335-
self.define_singleton_method(method) do |*method_args|
336-
Async::validate_argc(@delegate, method, *method_args)
337-
ivar = Concurrent::IVar.new
338-
@executor.post(method_args) do |arguments|
339-
begin
340-
ivar.set(@delegate.send(method, *arguments, &block))
341-
rescue => error
342-
ivar.fail(error)
343-
end
332+
ivar = Concurrent::IVar.new
333+
@executor.post(args) do |arguments|
334+
begin
335+
ivar.set(@delegate.send(method, *arguments, &block))
336+
rescue => error
337+
ivar.fail(error)
344338
end
345-
ivar.wait if @blocking
346-
ivar
347339
end
348-
349-
self.send(method, *args)
340+
ivar.wait if @blocking
341+
ivar
350342
end
351343
end
352344
private_constant :AsyncDelegator

spec/concurrent/async_spec.rb

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -202,21 +202,6 @@ def many(*args, &block) nil; end
202202
val = subject.async.with_block{ :foo }
203203
expect(val.value).to eq :foo
204204
end
205-
206-
context '#method_missing' do
207-
208-
it 'defines the method after the first call' do
209-
expect { subject.async.method(:echo) }.to raise_error(NameError)
210-
subject.async.echo(:foo)
211-
expect { subject.async.method(:echo) }.not_to raise_error
212-
end
213-
214-
it 'does not define the method on name/arity exception' do
215-
expect { subject.async.method(:bogus) }.to raise_error(NameError)
216-
expect { subject.async.bogus }.to raise_error(NameError)
217-
expect { subject.async.method(:bogus) }.to raise_error(NameError)
218-
end
219-
end
220205
end
221206

222207
context '#await' do
@@ -275,26 +260,11 @@ def many(*args, &block) nil; end
275260
val = subject.await.with_block{ :foo }
276261
expect(val.value).to eq :foo
277262
end
278-
279-
context '#method_missing' do
280-
281-
it 'defines the method after the first call' do
282-
expect { subject.await.method(:echo) }.to raise_error(NameError)
283-
subject.await.echo(:foo)
284-
expect { subject.await.method(:echo) }.not_to raise_error
285-
end
286-
287-
it 'does not define the method on name/arity exception' do
288-
expect { subject.await.method(:bogus) }.to raise_error(NameError)
289-
expect { subject.await.bogus }.to raise_error(NameError)
290-
expect { subject.await.method(:bogus) }.to raise_error(NameError)
291-
end
292-
end
293263
end
294264

295265
context 'locking' do
296266

297-
it 'uses the same mutex for both #async and #await' do
267+
it 'uses the same lock for both #async and #await' do
298268
object = Class.new {
299269
include Concurrent::Async
300270
attr_reader :bucket

0 commit comments

Comments
 (0)