agent 0.1.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +0 -0
  3. data/Gemfile +0 -1
  4. data/Gemfile.lock +12 -12
  5. data/README.md +51 -36
  6. data/Rakefile +5 -0
  7. data/agent.gemspec +1 -0
  8. data/autotest/discover.rb +9 -1
  9. data/benchmark/multi_ruby_bench.sh +87 -0
  10. data/benchmark/sieve.rb +238 -0
  11. data/examples/agent-workers.rb +51 -0
  12. data/examples/producer-consumer.rb +15 -0
  13. data/lib/agent.rb +1 -15
  14. data/lib/agent/all.rb +22 -0
  15. data/lib/agent/blocking_once.rb +26 -0
  16. data/lib/agent/channel.rb +88 -41
  17. data/lib/agent/error.rb +15 -0
  18. data/lib/agent/errors.rb +14 -0
  19. data/lib/agent/go.rb +9 -0
  20. data/lib/agent/kernel/channel.rb +7 -0
  21. data/lib/agent/kernel/go.rb +7 -0
  22. data/lib/agent/kernel/select.rb +7 -0
  23. data/lib/agent/notifier.rb +34 -0
  24. data/lib/agent/once.rb +32 -0
  25. data/lib/agent/pop.rb +70 -0
  26. data/lib/agent/push.rb +70 -0
  27. data/lib/agent/queue.rb +133 -0
  28. data/lib/agent/queue/buffered.rb +68 -0
  29. data/lib/agent/queue/unbuffered.rb +88 -0
  30. data/lib/agent/queues.rb +50 -0
  31. data/lib/agent/selector.rb +119 -0
  32. data/lib/agent/uuid.rb +36 -0
  33. data/lib/agent/version.rb +1 -1
  34. data/lib/agent/wait_group.rb +43 -0
  35. data/spec/blocking_once_spec.rb +122 -0
  36. data/spec/channel_spec.rb +153 -82
  37. data/spec/error_spec.rb +15 -0
  38. data/spec/examples/channel_of_channels_spec.rb +17 -14
  39. data/spec/examples/producer_consumer_spec.rb +26 -16
  40. data/spec/examples/sieve_spec.rb +43 -37
  41. data/spec/go_spec.rb +17 -0
  42. data/spec/notifier_spec.rb +42 -0
  43. data/spec/once_spec.rb +91 -0
  44. data/spec/pop_spec.rb +97 -0
  45. data/spec/push_spec.rb +95 -0
  46. data/spec/queue_spec.rb +335 -37
  47. data/spec/queues_spec.rb +28 -0
  48. data/spec/selector_spec.rb +398 -0
  49. data/spec/spec_helper.rb +19 -0
  50. data/spec/uuid_spec.rb +13 -0
  51. data/spec/wait_group_spec.rb +45 -0
  52. metadata +94 -54
  53. data/lib/agent/transport/queue.rb +0 -82
  54. data/spec/helper.rb +0 -5
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ tmp/*
5
+ .rbx
data/.rspec ADDED
File without changes
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in go.gemspec
4
3
  gemspec
@@ -1,22 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- agent (0.0.1)
4
+ agent (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
- diff-lcs (1.1.2)
10
- rspec (2.0.1)
11
- rspec-core (~> 2.0.1)
12
- rspec-expectations (~> 2.0.1)
13
- rspec-mocks (~> 2.0.1)
14
- rspec-core (2.0.1)
15
- rspec-expectations (2.0.1)
16
- diff-lcs (>= 1.1.2)
17
- rspec-mocks (2.0.1)
18
- rspec-core (~> 2.0.1)
19
- rspec-expectations (~> 2.0.1)
9
+ diff-lcs (1.1.3)
10
+ rake (0.9.2.2)
11
+ rspec (2.9.0)
12
+ rspec-core (~> 2.9.0)
13
+ rspec-expectations (~> 2.9.0)
14
+ rspec-mocks (~> 2.9.0)
15
+ rspec-core (2.9.0)
16
+ rspec-expectations (2.9.0)
17
+ diff-lcs (~> 1.1.3)
18
+ rspec-mocks (2.9.0)
20
19
 
21
20
  PLATFORMS
22
21
  java
@@ -24,4 +23,5 @@ PLATFORMS
24
23
 
25
24
  DEPENDENCIES
26
25
  agent!
26
+ rake
27
27
  rspec
data/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # Agent
2
2
 
3
- Agent is a diverse family of related approaches for modelling concurrent systems, in Ruby. In other words, it is a collection of different [process calculi](http://en.wikipedia.org/wiki/Process_calculus) primitives and patterns, with no specific, idiomatic affiliation to any specific implementation. A few available patterns so far:
3
+ Agent is an attempt at [Go-like (CSP / pi-calculus) concurrency in Ruby](http://www.igvita.com/2010/12/02/concurrency-with-actors-goroutines-ruby/), but with an additional twist. It is a collection of different [process calculi](http://en.wikipedia.org/wiki/Process_calculus) primitives and patterns, with no specific, idiomatic affiliation to any specific implementation. A few available patterns so far:
4
4
 
5
5
  - Goroutines on top of green Ruby threads
6
- - Named, in-memory channels
6
+ - Named, Typed, Bufferd and Unbufferred in-memory "channels"
7
+ - Selectable "channels"
7
8
 
8
- This gem is a work in progress & an experiment, so treat it as such. At the moment, it is heavily influenced by Google's Go and π-calculus primitives.
9
+ This gem is a work in progress, so treat it as such.
9
10
 
10
11
  # Working Code Examples
11
12
 
@@ -14,29 +15,63 @@ This gem is a work in progress & an experiment, so treat it as such. At the mome
14
15
  * [Sieve of Eratosthenes](https://github.com/igrigorik/agent/blob/master/spec/examples/sieve_spec.rb)
15
16
 
16
17
  # Example: Goroutine Generator
18
+ A simple multi-threaded consumer-producer, except without a thread or a mutex in sight! Note that by default Agent channels are unbuffered, meaning that the size is implicitly set to 0. Hence, in example below, the producer will generate a single value, and block until we call receive - rinse, repeat.
17
19
 
18
- producer = Proc.new do |c|
19
- puts "Starting generator: #{c.name}"
20
+ ```ruby
21
+ c = channel!(Integer)
20
22
 
21
- i = 0
22
- loop { c.pipe << i+= 1 }
23
- end
23
+ go! do
24
+ i = 0
25
+ loop { c << (i += 1) }
26
+ end
24
27
 
25
- c = Agent::Channel.new(name: :incr, type: Integer)
28
+ p c.receive.first # => 1
29
+ p c.receive.first # => 2
30
+ ```
26
31
 
27
- Generator = Struct.new(:name, :pipe)
28
- g = Generator.new(:incr, c)
32
+ # Example: Multi-channel selector
33
+ A "select" statement chooses which of a set of possible communications will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations. Select will block until one of the channels becomes available:
29
34
 
30
- go(g, &producer)
35
+ ```ruby
36
+ cw = channel!(Integer, 1)
37
+ cr = channel!(Integer, 1)
31
38
 
32
- c.receive
33
- c.receive
39
+ select do |s|
40
+ s.case(cr, :receive) { |value| do_something(value) }
41
+ s.case(cw, :send, 3)
42
+ end
43
+ ```
34
44
 
45
+ In example above, cr is currently unavailable to read from (since its empty), but cw is ready for writing since the channel is buffered and empty. Hence, select will immediately choose the cw case and execute that code block.
46
+
47
+ ```ruby
48
+ cr = channel!(Integer, 1)
49
+
50
+ select do |s|
51
+ s.case(cr, :receive) { |value| do_something(value) }
52
+ s.default { puts :default }
53
+ end
54
+ ```
55
+
56
+ In this example, cr is unavailable for read (since its empty), but we also provide "default" case which is executed immediately if no other cases are matched. In other words, no blocking.
57
+
58
+ ```ruby
59
+ cr = channel!(Integer, 1)
60
+
61
+ select do |s|
62
+ s.case(cr, :receive) { |value| do_something(value) }
63
+ s.timeout(1.0) { puts :timeout }
64
+ end
65
+ ```
66
+
67
+ Once again, cr is empty, hence cannot be read from and there is no default block. Select will block until cr is readable or until the timeout condition is met - which in the case above is set to 1 second.
35
68
 
36
69
  # Go & π-calculus: Background & Motivation
37
70
 
38
71
  *Do not communicate by sharing memory; instead, share memory by communicating.*
39
72
 
73
+ See [Concurrency with Actors, Goroutines & Ruby](http://www.igvita.com/2010/12/02/concurrency-with-actors-goroutines-ruby/) for motivation and comparison to other concurrency models.
74
+
40
75
  Concurrent programming in many environments is made difficult by the subtleties required to implement correct access to shared variables. Google's Go encourages a different approach in which shared values are passed around on channels and, in fact, never actively shared by separate threads of execution. Only one goroutine has access to the value at any given time. Data races cannot occur, by design.
41
76
 
42
77
  One way to think about this model is to consider a typical single-threaded program running on one CPU. It has no need for synchronization primitives. Now run another such instance; it too needs no synchronization. Now let those two communicate; if the communication is the synchronizer, there's still no need for other synchronization. Unix pipelines, for example, fit this model perfectly. Although Go's approach to concurrency originates in Hoare's Communicating Sequential Processes (CSP), it can also be seen as a type-safe generalization of Unix pipes.
@@ -46,28 +81,8 @@ To learn more about Go see following resources:
46
81
  * [golang.org](http://golang.org/)
47
82
  * [Go's concurrency](http://golang.org/doc/effective_go.html#concurrency)
48
83
  * [Go's channels](http://golang.org/doc/effective_go.html#channels)
84
+ * [Go's select statement](http://golang.org/doc/go_spec.html#Select_statements)
49
85
 
50
86
  # License
51
87
 
52
- (The MIT License)
53
-
54
- Copyright (c) 2010 Ilya Grigorik
55
-
56
- Permission is hereby granted, free of charge, to any person obtaining
57
- a copy of this software and associated documentation files (the
58
- 'Software'), to deal in the Software without restriction, including
59
- without limitation the rights to use, copy, modify, merge, publish,
60
- distribute, sublicense, and/or sell copies of the Software, and to
61
- permit persons to whom the Software is furnished to do so, subject to
62
- the following conditions:
63
-
64
- The above copyright notice and this permission notice shall be
65
- included in all copies or substantial portions of the Software.
66
-
67
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
68
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
69
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
70
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
71
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
72
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
73
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
88
+ The MIT License - Copyright (c) 2011 Ilya Grigorik
data/Rakefile CHANGED
@@ -1,2 +1,7 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
@@ -13,6 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.description = s.summary
14
14
 
15
15
  s.rubyforge_project = "agent"
16
+ s.add_development_dependency "rake"
16
17
  s.add_development_dependency "rspec"
17
18
 
18
19
  s.files = `git ls-files`.split("\n")
@@ -1 +1,9 @@
1
- Autotest.add_discovery { 'rspec2' }
1
+ Autotest.add_discovery { 'rspec2' }
2
+
3
+ if RUBY_PLATFORM =~ /java/
4
+ Autotest.add_hook :initialize do |at|
5
+ def at.ruby
6
+ "#{super} --1.9"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,87 @@
1
+ #!/bin/bash
2
+
3
+ if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
4
+
5
+ # First try to load from a user install
6
+ source "$HOME/.rvm/scripts/rvm"
7
+
8
+ elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
9
+
10
+ # Then try to load from a root install
11
+ source "/usr/local/rvm/scripts/rvm"
12
+
13
+ else
14
+
15
+ printf "ERROR: An RVM installation was not found.\n"
16
+
17
+ fi
18
+
19
+ echo
20
+ echo `ruby -v`
21
+ echo
22
+ ruby benchmark/sieve.rb 0
23
+ ruby benchmark/sieve.rb 1
24
+ ruby benchmark/sieve.rb 2
25
+ ruby benchmark/sieve.rb 3
26
+ echo
27
+ rvm use 1.8.7
28
+ echo
29
+ echo `ruby -v`
30
+ echo
31
+ ruby benchmark/sieve.rb 0
32
+ ruby benchmark/sieve.rb 1
33
+ ruby benchmark/sieve.rb 2
34
+ ruby benchmark/sieve.rb 3
35
+ echo
36
+ rvm use 1.9.2
37
+ echo
38
+ echo `ruby -v`
39
+ echo
40
+ ruby benchmark/sieve.rb 0
41
+ ruby benchmark/sieve.rb 1
42
+ ruby benchmark/sieve.rb 2
43
+ ruby benchmark/sieve.rb 3
44
+ echo
45
+ rvm use 1.9.3
46
+ echo
47
+ echo `ruby -v`
48
+ echo
49
+ ruby benchmark/sieve.rb 0
50
+ ruby benchmark/sieve.rb 1
51
+ ruby benchmark/sieve.rb 2
52
+ ruby benchmark/sieve.rb 3
53
+ echo
54
+ rvm use jruby
55
+ echo
56
+ echo `ruby -v`
57
+ echo
58
+ ruby benchmark/sieve.rb 0
59
+ ruby benchmark/sieve.rb 1
60
+ ruby benchmark/sieve.rb 2
61
+ ruby benchmark/sieve.rb 3
62
+ echo
63
+ JRUBY_OPTS="--1.9"
64
+ echo `ruby -v`
65
+ echo
66
+ ruby benchmark/sieve.rb 0
67
+ ruby benchmark/sieve.rb 1
68
+ ruby benchmark/sieve.rb 2
69
+ ruby benchmark/sieve.rb 3
70
+ echo
71
+ rvm use rbx-1.2.4
72
+ echo
73
+ echo `ruby -v`
74
+ echo
75
+ ruby benchmark/sieve.rb 0
76
+ ruby benchmark/sieve.rb 1
77
+ ruby benchmark/sieve.rb 2
78
+ ruby benchmark/sieve.rb 3
79
+ echo
80
+ rvm use rbx-2.0.testing
81
+ echo
82
+ echo `ruby -v`
83
+ echo
84
+ ruby benchmark/sieve.rb 0
85
+ ruby benchmark/sieve.rb 1
86
+ ruby benchmark/sieve.rb 2
87
+ ruby benchmark/sieve.rb 3
@@ -0,0 +1,238 @@
1
+ require 'benchmark'
2
+ project_lib_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
3
+ $LOAD_PATH.unshift(project_lib_path)
4
+ require 'agent'
5
+
6
+ $size = (ARGV.pop || 0).to_i
7
+
8
+ def generate(channels)
9
+ ch = channel!(Integer, $size)
10
+ channels << ch
11
+ go!{ i = 1; loop { ch << i+= 1} }
12
+
13
+ return ch
14
+ end
15
+
16
+ def filter(in_channel, prime, channels)
17
+ out = channel!(Integer, $size)
18
+ channels << out
19
+
20
+ go! do
21
+ loop do
22
+ i, _ = in_channel.receive
23
+ out << i if (i % prime) != 0
24
+ end
25
+ end
26
+
27
+ return out
28
+ end
29
+
30
+ def sieve(channels)
31
+ out = channel!(Integer, $size)
32
+ channels << out
33
+
34
+ go! do
35
+ ch = generate(channels)
36
+ loop do
37
+ prime, _ = ch.receive
38
+ out << prime
39
+ ch = filter(ch, prime, channels)
40
+ end
41
+ end
42
+
43
+ return out
44
+ end
45
+
46
+ ################
47
+ ################
48
+
49
+ nth_prime = 150
50
+ concurrency = 5
51
+ channels = []
52
+
53
+ puts "#{nth_prime}'s prime, #{concurrency} goroutines, channel buffer size is #{$size}"
54
+
55
+ Benchmark.bm(7) do |x|
56
+ x.report("receive") do
57
+ runners = []
58
+
59
+ concurrency.times do |n|
60
+ runners << go! do
61
+ primes = sieve(channels)
62
+ nth_prime.times { primes.receive }
63
+ end
64
+ end
65
+
66
+ runners.map {|t| t.join}
67
+ end
68
+ end
69
+
70
+ puts
71
+
72
+ channels.each(&:close)
73
+
74
+ __END__
75
+
76
+ The setup:
77
+ 13" Macbook Air
78
+ OSX Lion 10.7.3
79
+ 1.8 GHz Intel Core i7
80
+ 4 GB 1333 MHz DDR3
81
+ SSD
82
+ Terminal w/ OSX Lion's system ruby by default
83
+
84
+ The command:
85
+ benchmark/multi_ruby_bench.sh
86
+
87
+ The results:
88
+
89
+ ruby 1.8.7 (2010-01-10 patchlevel 249) [universal-darwin11.0]
90
+
91
+ 150's prime, 5 goroutines, channel buffer size is 0
92
+ user system total real
93
+ receive 27.100000 0.290000 27.390000 ( 27.324909)
94
+
95
+ 150's prime, 5 goroutines, channel buffer size is 1
96
+ user system total real
97
+ receive 24.690000 0.280000 24.970000 ( 24.910035)
98
+
99
+ 150's prime, 5 goroutines, channel buffer size is 2
100
+ user system total real
101
+ receive 24.730000 0.280000 25.010000 ( 24.946830)
102
+
103
+ 150's prime, 5 goroutines, channel buffer size is 3
104
+ user system total real
105
+ receive 25.010000 0.290000 25.300000 ( 25.238002)
106
+
107
+
108
+ ruby 1.8.7 (2012-02-08 patchlevel 358) [i686-darwin11.3.0]
109
+
110
+ 150's prime, 5 goroutines, channel buffer size is 0
111
+ user system total real
112
+ receive 17.890000 0.090000 17.980000 ( 17.984755)
113
+
114
+ 150's prime, 5 goroutines, channel buffer size is 1
115
+ user system total real
116
+ receive 17.030000 0.090000 17.120000 ( 17.131916)
117
+
118
+ 150's prime, 5 goroutines, channel buffer size is 2
119
+ user system total real
120
+ receive 17.060000 0.090000 17.150000 ( 17.168336)
121
+
122
+ 150's prime, 5 goroutines, channel buffer size is 3
123
+ user system total real
124
+ receive 17.090000 0.100000 17.190000 ( 17.187443)
125
+
126
+
127
+ ruby 1.9.2p318 (2012-02-14 revision 34678) [x86_64-darwin11.3.0]
128
+
129
+ 150's prime, 5 goroutines, channel buffer size is 0
130
+ user system total real
131
+ receive 5.410000 2.360000 7.770000 ( 7.241433)
132
+
133
+ 150's prime, 5 goroutines, channel buffer size is 1
134
+ user system total real
135
+ receive 5.270000 1.980000 7.250000 ( 6.817536)
136
+
137
+ 150's prime, 5 goroutines, channel buffer size is 2
138
+ user system total real
139
+ receive 5.330000 2.030000 7.360000 ( 6.918912)
140
+
141
+ 150's prime, 5 goroutines, channel buffer size is 3
142
+ user system total real
143
+ receive 5.340000 1.950000 7.290000 ( 6.864300)
144
+
145
+
146
+ ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin11.3.0]
147
+
148
+ 150's prime, 5 goroutines, channel buffer size is 0
149
+ user system total real
150
+ receive 6.470000 5.220000 11.690000 ( 8.803085)
151
+
152
+ 150's prime, 5 goroutines, channel buffer size is 1
153
+ user system total real
154
+ receive 6.390000 4.190000 10.580000 ( 8.248593)
155
+
156
+ 150's prime, 5 goroutines, channel buffer size is 2
157
+ user system total real
158
+ receive 6.240000 3.760000 10.000000 ( 7.936199)
159
+
160
+ 150's prime, 5 goroutines, channel buffer size is 3
161
+ user system total real
162
+ receive 6.000000 3.200000 9.200000 ( 7.461371)
163
+
164
+
165
+ jruby 1.6.7 (ruby-1.8.7-p357) (2012-02-22 3e82bc8) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_29) [darwin-x86_64-java]
166
+
167
+ 150's prime, 5 goroutines, channel buffer size is 0
168
+ user system total real
169
+ receive 9.672000 0.000000 9.672000 ( 9.637000)
170
+
171
+ 150's prime, 5 goroutines, channel buffer size is 1
172
+ user system total real
173
+ receive 12.528000 0.000000 12.528000 ( 12.494000)
174
+
175
+ 150's prime, 5 goroutines, channel buffer size is 2
176
+ user system total real
177
+ receive 13.191000 0.000000 13.191000 ( 13.150000)
178
+
179
+ 150's prime, 5 goroutines, channel buffer size is 3
180
+ user system total real
181
+ receive 14.702000 0.000000 14.702000 ( 14.668000)
182
+
183
+
184
+ jruby 1.6.7 (ruby-1.8.7-p357) (2012-02-22 3e82bc8) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_29) [darwin-x86_64-java]
185
+
186
+ 150's prime, 5 goroutines, channel buffer size is 0
187
+ user system total real
188
+ receive 9.869000 0.000000 9.869000 ( 9.836000)
189
+
190
+ 150's prime, 5 goroutines, channel buffer size is 1
191
+ user system total real
192
+ receive 12.399000 0.000000 12.399000 ( 12.362000)
193
+
194
+ 150's prime, 5 goroutines, channel buffer size is 2
195
+ user system total real
196
+ receive 13.146000 0.000000 13.146000 ( 13.154000)
197
+
198
+ 150's prime, 5 goroutines, channel buffer size is 3
199
+ user system total real
200
+ receive 13.888000 0.000000 13.888000 ( 13.847000)
201
+
202
+
203
+ rubinius 1.2.4 (1.8.7 release 2011-07-05 JI) [x86_64-apple-darwin11.3.0]
204
+
205
+ 150's prime, 5 goroutines, channel buffer size is 0
206
+ user system total real
207
+ receive 20.890472 3.099183 23.989655 ( 19.765032)
208
+
209
+ 150's prime, 5 goroutines, channel buffer size is 1
210
+ user system total real
211
+ receive 21.727269 3.028753 24.756022 ( 20.554911)
212
+
213
+ 150's prime, 5 goroutines, channel buffer size is 2
214
+ user system total real
215
+ receive 45.912357 5.814811 51.727168 ( 44.636344)
216
+
217
+ 150's prime, 5 goroutines, channel buffer size is 3
218
+ user system total real
219
+ receive 46.211119 7.487593 53.698712 ( 44.716384)
220
+
221
+
222
+ rubinius 2.0.0dev (1.8.7 65c6146e yyyy-mm-dd JI) [x86_64-apple-darwin11.3.0]
223
+
224
+ 150's prime, 5 goroutines, channel buffer size is 0
225
+ user system total real
226
+ receive 62.980970 4.578037 67.559007 ( 18.560938)
227
+
228
+ 150's prime, 5 goroutines, channel buffer size is 1
229
+ user system total real
230
+ receive 52.822284 4.667321 57.489605 ( 15.986046)
231
+
232
+ 150's prime, 5 goroutines, channel buffer size is 2
233
+ user system total real
234
+ receive 54.217875 4.579821 58.797696 ( 16.267339)
235
+
236
+ 150's prime, 5 goroutines, channel buffer size is 3
237
+ user system total real
238
+ receive 59.092219 4.881411 63.973630 ( 17.664269)