parallel 0.3.7 → 0.4.0
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/VERSION +1 -1
- data/lib/parallel.rb +68 -32
- data/parallel.gemspec +4 -2
- data/spec/cases/no_dump_with_each.rb +16 -0
- data/spec/parallel_spec.rb +4 -0
- metadata +4 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/parallel.rb
CHANGED
@@ -3,7 +3,9 @@ require 'thread' # to get Thread.exclusive
|
|
3
3
|
class Parallel
|
4
4
|
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
5
5
|
|
6
|
-
def self.in_threads(count
|
6
|
+
def self.in_threads(options={:count => 2})
|
7
|
+
count, options = extract_count_from_options(options)
|
8
|
+
|
7
9
|
out = []
|
8
10
|
threads = []
|
9
11
|
|
@@ -17,40 +19,18 @@ class Parallel
|
|
17
19
|
out
|
18
20
|
end
|
19
21
|
|
20
|
-
def self.in_processes(
|
22
|
+
def self.in_processes(options = {}, &block)
|
23
|
+
count, options = extract_count_from_options(options)
|
21
24
|
count ||= processor_count
|
25
|
+
preserve_results = (options[:preserve_results] != false)
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
pids = []
|
27
|
-
count.times do |i|
|
28
|
-
reads[i], writes[i] = IO.pipe
|
29
|
-
# activate copy on write friendly GC of REE
|
30
|
-
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
|
31
|
-
pids << Process.fork do
|
32
|
-
Marshal.dump(yield(i), writes[i]) # Serialize result
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
kill_on_ctrl_c(pids)
|
37
|
-
|
38
|
-
# Collect results from pipes simultanously
|
39
|
-
# otherwise pipes get stuck when to much is written (buffer full)
|
40
|
-
out = []
|
41
|
-
in_threads(count) do |i|
|
42
|
-
writes[i].close
|
43
|
-
while text = reads[i].gets
|
44
|
-
out[i] = out[i].to_s + text
|
45
|
-
end
|
46
|
-
reads[i].close
|
47
|
-
end
|
48
|
-
|
49
|
-
out.map{|x| Marshal.load(x) } # Deserialize results
|
27
|
+
pipes = fork_and_start_writing(count, :preserve_results => preserve_results, &block)
|
28
|
+
out = read_from_pipes(pipes)
|
29
|
+
out.map{|x| deserialize(x) } if preserve_results
|
50
30
|
end
|
51
31
|
|
52
32
|
def self.each(array, options={}, &block)
|
53
|
-
map(array, options, &block)
|
33
|
+
map(array, options.merge(:preserve_results => false), &block)
|
54
34
|
array
|
55
35
|
end
|
56
36
|
|
@@ -74,7 +54,7 @@ class Parallel
|
|
74
54
|
loop do
|
75
55
|
index = Thread.exclusive{ current+=1 }
|
76
56
|
break if index >= array.size
|
77
|
-
results[index] = *send(method, 1){ yield array[index] }
|
57
|
+
results[index] = *send(method, options.merge(:count => 1)){ yield array[index] }
|
78
58
|
end
|
79
59
|
end
|
80
60
|
|
@@ -92,7 +72,63 @@ class Parallel
|
|
92
72
|
|
93
73
|
private
|
94
74
|
|
75
|
+
# Collect results from pipes simultanously
|
76
|
+
# otherwise pipes get stuck when to much is written (buffer full)
|
77
|
+
def self.read_from_pipes(reads)
|
78
|
+
out = []
|
79
|
+
in_threads(reads.size) do |i|
|
80
|
+
out[i] = ''
|
81
|
+
while text = reads[i].gets
|
82
|
+
out[i] += text
|
83
|
+
end
|
84
|
+
reads[i].close
|
85
|
+
end
|
86
|
+
out
|
87
|
+
end
|
88
|
+
|
89
|
+
# fork and start writing results into n pipes
|
90
|
+
def self.fork_and_start_writing(count, options, &block)
|
91
|
+
reads = []
|
92
|
+
pids = []
|
93
|
+
count.times do |i|
|
94
|
+
reads[i], write = IO.pipe
|
95
|
+
pids << do_in_new_process(i, options.merge(:write_to => (options[:preserve_results] ? write : nil)), &block)
|
96
|
+
write.close
|
97
|
+
end
|
98
|
+
kill_on_ctrl_c(pids)
|
99
|
+
reads
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.do_in_new_process(work_item, options)
|
103
|
+
# activate copy on write friendly GC of REE
|
104
|
+
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
|
105
|
+
Process.fork do
|
106
|
+
result = yield(work_item)
|
107
|
+
serialize(result, options) if options[:write_to]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.serialize(something, options)
|
112
|
+
Marshal.dump(something, options[:write_to])
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.deserialize(something)
|
116
|
+
Marshal.load(something)
|
117
|
+
end
|
118
|
+
|
119
|
+
# options is either a Interger or a Hash with :count
|
120
|
+
def self.extract_count_from_options(options)
|
121
|
+
if options.is_a?(Hash)
|
122
|
+
count = options[:count]
|
123
|
+
else
|
124
|
+
count = options
|
125
|
+
options = {}
|
126
|
+
end
|
127
|
+
[count, options]
|
128
|
+
end
|
129
|
+
|
95
130
|
# split an array into groups of size items
|
131
|
+
# (copied from ActiveSupport, to not require it)
|
96
132
|
def self.in_groups_of(array, size)
|
97
133
|
results = []
|
98
134
|
loop do
|
@@ -106,7 +142,7 @@ class Parallel
|
|
106
142
|
results
|
107
143
|
end
|
108
144
|
|
109
|
-
#
|
145
|
+
# kill all these processes (children) if user presses Ctrl+c
|
110
146
|
def self.kill_on_ctrl_c(pids)
|
111
147
|
Signal.trap :SIGINT do
|
112
148
|
$stderr.puts 'Parallel execution interrupted, exiting ...'
|
data/parallel.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{parallel}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Michael Grosser"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-01-17}
|
13
13
|
s.email = %q{grosser.michael@gmail.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"README.markdown"
|
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
"VERSION",
|
21
21
|
"lib/parallel.rb",
|
22
22
|
"parallel.gemspec",
|
23
|
+
"spec/cases/no_dump_with_each.rb",
|
23
24
|
"spec/cases/parallel_each.rb",
|
24
25
|
"spec/cases/parallel_high_fork_rate.rb",
|
25
26
|
"spec/cases/parallel_influence_outside_data.rb",
|
@@ -47,6 +48,7 @@ Gem::Specification.new do |s|
|
|
47
48
|
"spec/cases/parallel_map_uneven.rb",
|
48
49
|
"spec/cases/parallel_map_range.rb",
|
49
50
|
"spec/cases/parallel_with_set_processes.rb",
|
51
|
+
"spec/cases/no_dump_with_each.rb",
|
50
52
|
"spec/cases/parallel_start_and_kill.rb",
|
51
53
|
"spec/cases/parallel_raise.rb",
|
52
54
|
"spec/cases/parallel_sleeping_2.rb",
|
data/spec/parallel_spec.rb
CHANGED
@@ -106,6 +106,10 @@ describe Parallel do
|
|
106
106
|
it "returns original array, works like map" do
|
107
107
|
`ruby spec/cases/parallel_each.rb`.should == '-b--c--d--a-a b c d'
|
108
108
|
end
|
109
|
+
|
110
|
+
it "does not use marshal_dump" do
|
111
|
+
`ruby spec/cases/no_dump_with_each.rb 2>&1`.should == 'not dumpable'
|
112
|
+
end
|
109
113
|
end
|
110
114
|
|
111
115
|
describe :in_groups_of do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parallel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-17 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- VERSION
|
28
28
|
- lib/parallel.rb
|
29
29
|
- parallel.gemspec
|
30
|
+
- spec/cases/no_dump_with_each.rb
|
30
31
|
- spec/cases/parallel_each.rb
|
31
32
|
- spec/cases/parallel_high_fork_rate.rb
|
32
33
|
- spec/cases/parallel_influence_outside_data.rb
|
@@ -76,6 +77,7 @@ test_files:
|
|
76
77
|
- spec/cases/parallel_map_uneven.rb
|
77
78
|
- spec/cases/parallel_map_range.rb
|
78
79
|
- spec/cases/parallel_with_set_processes.rb
|
80
|
+
- spec/cases/no_dump_with_each.rb
|
79
81
|
- spec/cases/parallel_start_and_kill.rb
|
80
82
|
- spec/cases/parallel_raise.rb
|
81
83
|
- spec/cases/parallel_sleeping_2.rb
|