forkoff 0.0.1 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -20,13 +20,52 @@ DESCRIPTION
20
20
  child process and collecting the results. forkoff can limit the number of
21
21
  child processes which is, by default, 2.
22
22
 
23
+ HISTORY
24
+ 0.0.4
25
+ - code re-org
26
+ - add :strategy option
27
+ - default number of processes is 2, not 8
28
+
29
+ 0.0.1
30
+
31
+ - updated to use producer threds pushing onto a SizedQueue for each consumer
32
+ channel. in this way the producers do not build up a massize parllel data
33
+ structure but provide data to the consumers only as fast as they can fork
34
+ and proccess it. basically for a 4 process run you'll end up with 4
35
+ channels of size 1 between 4 produces and 4 consumers, each consumer is a
36
+ thread popping of jobs, forking, and yielding results.
37
+
38
+ - removed use of Queue for capturing the output. now it's simply an array
39
+ of arrays which removed some sync overhead.
40
+
41
+ - you can configure the number of processes globally with
42
+
43
+ Forkoff.default['proccess'] = 4
44
+
45
+ - you can now pass either an options hash
46
+
47
+ forkoff( :processes => 2 ) ...
48
+
49
+ or plain vanilla number
50
+
51
+ forkoff( 2 ) ...
52
+
53
+ to the forkoff call
54
+
55
+ - default number of processes is 8, not 2
56
+
57
+
58
+ 0.0.0
59
+
60
+ initial version
61
+
62
+
23
63
  SAMPLES
24
64
 
25
65
  <========< samples/a.rb >========>
26
66
 
27
67
  ~ > cat samples/a.rb
28
68
 
29
- #
30
69
  # forkoff makes it trivial to do parallel processing with ruby, the following
31
70
  # prints out each word in a separate process
32
71
  #
@@ -37,15 +76,14 @@ SAMPLES
37
76
 
38
77
  ~ > ruby samples/a.rb
39
78
 
40
- hey from 19511
41
- you from 19512
79
+ hey from 1032
80
+ you from 1033
42
81
 
43
82
 
44
83
  <========< samples/b.rb >========>
45
84
 
46
85
  ~ > cat samples/b.rb
47
86
 
48
- #
49
87
  # for example, this takes only 4 seconds or so to complete (8 iterations
50
88
  # running in two processes = twice as fast)
51
89
  #
@@ -69,7 +107,7 @@ SAMPLES
69
107
 
70
108
  ~ > ruby samples/b.rb
71
109
 
72
- elapsed: 4.19796895980835
110
+ elapsed: 4.25545883178711
73
111
  results: [0, 1, 4, 9, 16, 25, 36, 49]
74
112
 
75
113
 
@@ -77,7 +115,6 @@ SAMPLES
77
115
 
78
116
  ~ > cat samples/c.rb
79
117
 
80
- #
81
118
  # forkoff does *NOT* spawn processes in batches, waiting for each batch to
82
119
  # complete. rather, it keeps a certain number of processes busy until all
83
120
  # results have been gathered. in otherwords the following will ensure that 3
@@ -114,70 +151,92 @@ SAMPLES
114
151
  ~ > ruby samples/c.rb
115
152
 
116
153
 
117
- pid: 19526
118
- elapsed: 3.39831399917603
154
+ pid: 1048
155
+ elapsed: 3.14415812492371
119
156
 
120
157
  ---
121
158
  a: |
122
- -+- 19526 ahoward ruby -Ilib samples/c.rb
123
- |-+- 19527 ahoward ruby -Ilib samples/c.rb
124
- |-+- 19528 ahoward ruby -Ilib samples/c.rb
125
- \-+- 19529 ahoward ruby -Ilib samples/c.rb
159
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
160
+ |-+- 01049 ahoward ruby -Ilib samples/c.rb
161
+ |-+- 01050 ahoward ruby -Ilib samples/c.rb
162
+ \-+- 01051 ahoward ruby -Ilib samples/c.rb
126
163
 
127
164
  ---
128
165
  b: |
129
- -+- 19526 ahoward ruby -Ilib samples/c.rb
130
- |-+- 19527 ahoward ruby -Ilib samples/c.rb
131
- |-+- 19528 ahoward ruby -Ilib samples/c.rb
132
- \-+- 19529 ahoward ruby -Ilib samples/c.rb
166
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
167
+ |-+- 01049 ahoward (ruby)
168
+ |-+- 01050 ahoward ruby -Ilib samples/c.rb
169
+ \-+- 01051 ahoward ruby -Ilib samples/c.rb
133
170
 
134
171
  ---
135
172
  c: |
136
- -+- 19526 ahoward ruby -Ilib samples/c.rb
137
- |-+- 19527 ahoward (ruby)
138
- |-+- 19528 ahoward (ruby)
139
- \-+- 19529 ahoward ruby -Ilib samples/c.rb
173
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
174
+ |-+- 01049 ahoward ruby -Ilib samples/c.rb
175
+ |-+- 01050 ahoward ruby -Ilib samples/c.rb
176
+ \-+- 01051 ahoward ruby -Ilib samples/c.rb
140
177
 
141
178
  ---
142
179
  d: |
143
- -+- 19526 ahoward ruby -Ilib samples/c.rb
144
- |-+- 19536 ahoward ruby -Ilib samples/c.rb
145
- |-+- 19537 ahoward (ruby)
146
- \--- 19538 ahoward ruby -Ilib samples/c.rb
180
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
181
+ |-+- 01061 ahoward ruby -Ilib samples/c.rb
182
+ |-+- 01062 ahoward ruby -Ilib samples/c.rb
183
+ \-+- 01063 ahoward ruby -Ilib samples/c.rb
147
184
 
148
185
  ---
149
186
  e: |
150
- -+- 19526 ahoward ruby -Ilib samples/c.rb
151
- |-+- 19536 ahoward ruby -Ilib samples/c.rb
152
- |-+- 19537 ahoward ruby -Ilib samples/c.rb
153
- \--- 19538 ahoward ruby -Ilib samples/c.rb
187
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
188
+ |-+- 01061 ahoward (ruby)
189
+ |-+- 01062 ahoward ruby -Ilib samples/c.rb
190
+ \-+- 01063 ahoward ruby -Ilib samples/c.rb
154
191
 
155
192
  ---
156
193
  f: |
157
- -+- 19526 ahoward ruby -Ilib samples/c.rb
158
- |--- 19536 ahoward (ruby)
159
- |-+- 19538 ahoward ruby -Ilib samples/c.rb
160
- \--- 19543 ahoward ruby -Ilib samples/c.rb
194
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
195
+ |-+- 01061 ahoward ruby -Ilib samples/c.rb
196
+ |-+- 01062 ahoward ruby -Ilib samples/c.rb
197
+ \-+- 01063 ahoward ruby -Ilib samples/c.rb
161
198
 
162
199
  ---
163
200
  g: |
164
- -+- 19526 ahoward ruby -Ilib samples/c.rb
165
- |--- 19543 ahoward (ruby)
166
- |-+- 19546 ahoward ruby -Ilib samples/c.rb
167
- \--- 19547 ahoward ruby -Ilib samples/c.rb
201
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
202
+ |-+- 01090 ahoward ruby -Ilib samples/c.rb
203
+ |-+- 01091 ahoward ruby -Ilib samples/c.rb
204
+ \-+- 01092 ahoward ruby -Ilib samples/c.rb
168
205
 
169
206
  ---
170
207
  h: |
171
- -+- 19526 ahoward ruby -Ilib samples/c.rb
172
- |-+- 19543 ahoward ruby -Ilib samples/c.rb
173
- |--- 19546 ahoward ruby -Ilib samples/c.rb
174
- \--- 19547 ahoward ruby -Ilib samples/c.rb
208
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
209
+ |-+- 01090 ahoward ruby -Ilib samples/c.rb
210
+ |-+- 01091 ahoward ruby -Ilib samples/c.rb
211
+ \-+- 01092 ahoward ruby -Ilib samples/c.rb
175
212
 
176
213
  ---
177
214
  i: |
178
- -+- 19526 ahoward ruby -Ilib samples/c.rb
179
- |-+- 19543 ahoward ruby -Ilib samples/c.rb
180
- |-+- 19546 ahoward ruby -Ilib samples/c.rb
181
- \-+- 19547 ahoward ruby -Ilib samples/c.rb
215
+ -+- 01048 ahoward ruby -Ilib samples/c.rb
216
+ |-+- 01090 ahoward ruby -Ilib samples/c.rb
217
+ |-+- 01091 ahoward ruby -Ilib samples/c.rb
218
+ \-+- 01092 ahoward ruby -Ilib samples/c.rb
219
+
220
+
221
+
222
+ <========< samples/d.rb >========>
223
+
224
+ ~ > cat samples/d.rb
225
+
226
+ # forkoff supports two strategies of reading the result from the child: via
227
+ # pipe (the default) or via file. you can select which to use using the
228
+ # :strategy option.
229
+ #
230
+
231
+ require 'forkoff'
182
232
 
233
+ %w( hey you guys ).forkoff :strategy => :file do |word|
234
+ puts "#{ word } from #{ Process.pid }"
235
+ end
236
+
237
+ ~ > ruby samples/d.rb
238
+
239
+ hey from 1102
240
+ you from 1103
241
+ guys from 1104
183
242
 
data/gemspec.rb CHANGED
@@ -21,7 +21,6 @@ Gem::Specification::new do |spec|
21
21
  spec.executables = shiteless[Dir::glob("bin/*")].map{|exe| File::basename exe}
22
22
 
23
23
  spec.require_path = "lib"
24
- spec.autorequire = lib
25
24
 
26
25
  spec.has_rdoc = File::exist? "doc"
27
26
  spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
@@ -32,4 +31,5 @@ Gem::Specification::new do |spec|
32
31
  spec.author = "Ara T. Howard"
33
32
  spec.email = "ara.t.howard@gmail.com"
34
33
  spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
34
+ spec.rubyforge_project = 'codeforpeople'
35
35
  end
data/install.rb CHANGED
File without changes
@@ -1,26 +1,142 @@
1
1
  require 'thread'
2
2
 
3
3
  module Forkoff
4
- VERSION = '0.0.1'
5
- def self::version() Forkoff::VERSION end
4
+ def version
5
+ '0.0.4'
6
+ end
7
+
8
+ def done
9
+ @done ||= Object.new
10
+ end
11
+
12
+ def default
13
+ @default ||= { 'processes' => 2 }
14
+ end
15
+
16
+ def pipe
17
+ 'pipe'
18
+ end
19
+
20
+ def file
21
+ 'file'
22
+ end
23
+
24
+ def pid
25
+ @pid ||= Process.pid
26
+ end
27
+
28
+ def ppid
29
+ @ppid ||= Process.ppid
30
+ end
31
+
32
+ def tid
33
+ Thread.current.object_id.abs
34
+ end
35
+
36
+ def hostname
37
+ require 'socket'
38
+ @hostname ||= (Socket.gethostname rescue 'localhost.localdomain')
39
+ end
40
+
41
+ def tmpdir
42
+ require 'tmpdir'
43
+ @tmpdir ||= Dir.tmpdir
44
+ end
45
+
46
+ def tmpdir= tmpdir
47
+ @tmpdir = tmpdir.to_s
48
+ end
49
+
50
+ def tmpfile &block
51
+ basename = [hostname, pid, ppid, tid, rand].join('-')
52
+ tmp = File.join(tmpdir, basename)
53
+
54
+ fd = nil
55
+ flags = File::CREAT|File::EXCL|File::RDWR
56
+
57
+ 42.times do
58
+ begin
59
+ fd = open tmp, flags
60
+ break
61
+ rescue Object
62
+ sleep rand
63
+ end
64
+ end
65
+ raise Error, "could not create tmpfile" unless fd
66
+
67
+ if block
68
+ begin
69
+ return block.call(fd)
70
+ ensure
71
+ fd.close unless fd.closed? rescue nil
72
+ FileUtils.rm_rf tmp rescue nil
73
+ end
74
+ else
75
+ return fd
76
+ end
77
+ end
6
78
 
7
- Done = Object.new
8
- def self::done() Forkoff::Done end
79
+ def pipe_result *args, &block
80
+ r, w = IO.pipe
81
+ pid = fork
82
+
83
+ unless pid
84
+ r.close
85
+ result =
86
+ begin
87
+ block.call(*args)
88
+ rescue Object => e
89
+ e
90
+ end
91
+ w.write( Marshal.dump( result ) )
92
+ w.close
93
+ exit
94
+ end
95
+
96
+ w.close
97
+ data = ''
98
+ while(( buf = r.read(8192) ))
99
+ data << buf
100
+ end
101
+ result = Marshal.load( data )
102
+ r.close
103
+ Process.waitpid pid
104
+ return result
105
+ end
106
+
107
+ def file_result *args, &block
108
+ tmpfile do |fd|
109
+ pid = fork
9
110
 
10
- @default = {
11
- 'processes' => 2
12
- }
111
+ unless pid
112
+ result =
113
+ begin
114
+ block.call(*args)
115
+ rescue Object => e
116
+ e
117
+ end
118
+ fd.write( Marshal.dump( result ) )
119
+ exit
120
+ end
13
121
 
14
- class << self
15
- attr_accessor 'default'
122
+ Process.waitpid pid
123
+ fd.rewind
124
+ data = fd.read
125
+ result = Marshal.load( data )
126
+ return result
127
+ end
16
128
  end
129
+
130
+ class Error < ::StandardError; end
131
+
132
+ extend self
17
133
  end
18
134
 
19
135
  module Enumerable
20
-
21
136
  def forkoff options = {}, &block
22
137
  options = { 'processes' => Integer(options) } unless Hash === options
23
138
  n = Integer( options['processes'] || options[:processes] || Forkoff.default['processes'] )
139
+ strategy = options['strategy'] || options[:strategy] || 'pipe'
24
140
  qs = Array.new(n){ SizedQueue.new 1 }
25
141
  results = Array.new(n){ [] }
26
142
 
@@ -39,25 +155,17 @@ module Enumerable
39
155
  break if value == Forkoff.done
40
156
  args, index = value
41
157
 
42
- r, w = IO.pipe
43
- pid = fork
44
-
45
- unless pid
46
- r.close
47
- result =
48
- begin
49
- block.call(*args)
50
- rescue Object => e
51
- e
52
- end
53
- w.write( Marshal.dump( result ) )
54
- exit
55
- end
56
-
57
- w.close
58
- result = Marshal.load( r.read )
158
+ result =
159
+ case strategy.to_s.strip.downcase
160
+ when 'pipe'
161
+ Forkoff.pipe_result(*args, &block)
162
+ when 'file'
163
+ Forkoff.file_result(*args, &block)
164
+ else
165
+ raise ArgumentError, "strategy=#{ strategy.class }(#{ strategy.inspect })"
166
+ end
167
+
59
168
  results[i].push( [result, index] )
60
- Process.waitpid pid
61
169
  end
62
170
 
63
171
  results[i].push( Forkoff.done )
@@ -1,4 +1,3 @@
1
- #
2
1
  # forkoff makes it trivial to do parallel processing with ruby, the following
3
2
  # prints out each word in a separate process
4
3
  #
@@ -1,4 +1,3 @@
1
- #
2
1
  # for example, this takes only 4 seconds or so to complete (8 iterations
3
2
  # running in two processes = twice as fast)
4
3
  #
@@ -1,4 +1,3 @@
1
- #
2
1
  # forkoff does *NOT* spawn processes in batches, waiting for each batch to
3
2
  # complete. rather, it keeps a certain number of processes busy until all
4
3
  # results have been gathered. in otherwords the following will ensure that 3
@@ -0,0 +1,10 @@
1
+ # forkoff supports two strategies of reading the result from the child: via
2
+ # pipe (the default) or via file. you can select which to use using the
3
+ # :strategy option.
4
+ #
5
+
6
+ require 'forkoff'
7
+
8
+ %w( hey you guys ).forkoff :strategy => :file do |word|
9
+ puts "#{ word } from #{ Process.pid }"
10
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forkoff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ara T. Howard
8
- autorequire: forkoff
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-04-21 00:00:00 -06:00
12
+ date: 2008-09-13 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -24,7 +24,6 @@ extra_rdoc_files: []
24
24
  files:
25
25
  - gemspec.rb
26
26
  - gen_readme.rb
27
- - HISTORY
28
27
  - install.rb
29
28
  - lib
30
29
  - lib/forkoff.rb
@@ -33,6 +32,7 @@ files:
33
32
  - samples/a.rb
34
33
  - samples/b.rb
35
34
  - samples/c.rb
35
+ - samples/d.rb
36
36
  has_rdoc: false
37
37
  homepage: http://codeforpeople.com/lib/ruby/forkoff/
38
38
  post_install_message:
@@ -54,8 +54,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
54
  version:
55
55
  requirements: []
56
56
 
57
- rubyforge_project:
58
- rubygems_version: 1.0.1
57
+ rubyforge_project: codeforpeople
58
+ rubygems_version: 1.2.0
59
59
  signing_key:
60
60
  specification_version: 2
61
61
  summary: forkoff
data/HISTORY DELETED
@@ -1,34 +0,0 @@
1
-
2
- 0.0.1
3
-
4
- - updated to use producer threds pushing onto a SizedQueue for each consumer
5
- channel. in this way the producers do not build up a massize parllel data
6
- structure but provide data to the consumers only as fast as they can fork
7
- and proccess it. basically for a 4 process run you'll end up with 4
8
- channels of size 1 between 4 produces and 4 consumers, each consumer is a
9
- thread popping of jobs, forking, and yielding results.
10
-
11
- - removed use of Queue for capturing the output. now it's simply an array
12
- of arrays which removed some sync overhead.
13
-
14
- - you can configure the number of processes globally with
15
-
16
- Forkoff.default['proccess'] = 4
17
-
18
- - you can now pass either an options hash
19
-
20
- forkoff( :processes => 2 ) ...
21
-
22
- or plain vanilla number
23
-
24
- forkoff( 2 ) ...
25
-
26
- to the forkoff call
27
-
28
- - default number of processes is 8, not 2
29
-
30
-
31
- 0.0.0
32
-
33
- initial version
34
-