threadify 0.0.2 → 0.0.3
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 +62 -3
- data/a.rb +10 -0
- data/lib/threadify.rb +89 -12
- data/lib/threadify.rb.bak +154 -0
- data/sample/b.rb +33 -0
- metadata +5 -3
- data/threadify-0.0.2.gem +0 -0
data/README
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
NAME
|
2
2
|
threadify.rb
|
3
3
|
|
4
|
-
|
5
4
|
SYNOPSIS
|
6
5
|
enumerable = %w( a b c d )
|
7
6
|
enumerable.threadify(2){ 'process this block using two worker threads' }
|
@@ -68,7 +67,67 @@ SAMPLES
|
|
68
67
|
~ > ruby sample/a.rb
|
69
68
|
|
70
69
|
---
|
71
|
-
without threadify:
|
70
|
+
without threadify: 3.75206303596497
|
72
71
|
---
|
73
|
-
with threadify:
|
72
|
+
with threadify: 1.37899804115295
|
73
|
+
|
74
|
+
|
75
|
+
<========< sample/b.rb >========>
|
76
|
+
|
77
|
+
~ > cat sample/b.rb
|
78
|
+
|
79
|
+
require 'yaml'
|
80
|
+
|
81
|
+
require 'rubygems'
|
82
|
+
require 'threadify'
|
83
|
+
|
84
|
+
size = Integer(ARGV.shift || (2 ** 15))
|
85
|
+
|
86
|
+
haystack = Array.new(size){|i| i}
|
87
|
+
needle = 2 * (size / 3)
|
88
|
+
|
89
|
+
a, b = 4, 2
|
90
|
+
|
91
|
+
time 'without threadify' do
|
92
|
+
a = haystack.each{|value| break value if value == needle}
|
93
|
+
end
|
94
|
+
|
95
|
+
time 'with threadify' do
|
96
|
+
b = haystack.threadify(16){|value| threadify! value if value == needle}
|
97
|
+
end
|
98
|
+
|
99
|
+
raise if a != b
|
100
|
+
|
101
|
+
y :a => a, :b => b, :needle => needle
|
102
|
+
|
103
|
+
BEGIN {
|
104
|
+
def time label
|
105
|
+
a = Time.now.to_f
|
106
|
+
yield
|
107
|
+
ensure
|
108
|
+
b = Time.now.to_f
|
109
|
+
y label => (b - a)
|
110
|
+
end
|
111
|
+
}
|
112
|
+
|
113
|
+
~ > ruby sample/b.rb
|
114
|
+
|
115
|
+
---
|
116
|
+
without threadify: 0.00630998611450195
|
117
|
+
---
|
118
|
+
with threadify: 0.270262956619263
|
119
|
+
---
|
120
|
+
:needle: 21844
|
121
|
+
:a: 21844
|
122
|
+
:b: 21844
|
123
|
+
|
124
|
+
|
125
|
+
HISTORY
|
126
|
+
0.0.3
|
127
|
+
- added ability to short-circuit the parallel processing, a.k.a to 'break'
|
128
|
+
from threadify
|
74
129
|
|
130
|
+
0.0.2
|
131
|
+
- don't use thread.exit, just let the thread die naturally
|
132
|
+
- add version to Threadify module
|
133
|
+
- comments ;-)
|
data/a.rb
ADDED
data/lib/threadify.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Threadify
|
2
|
-
VERSION = '0.0.
|
2
|
+
VERSION = '0.0.3'
|
3
3
|
def Threadify.version() Threadify::VERSION end
|
4
4
|
|
5
5
|
require 'thread'
|
@@ -11,6 +11,8 @@ module Threadify
|
|
11
11
|
attr_accessor :threads
|
12
12
|
attr_accessor :abort_on_exception
|
13
13
|
end
|
14
|
+
|
15
|
+
class Error < ::StandardError; end
|
14
16
|
end
|
15
17
|
|
16
18
|
module Enumerable
|
@@ -20,27 +22,69 @@ module Enumerable
|
|
20
22
|
opts = {:threads => opts} if Numeric === opts
|
21
23
|
threads = Integer(opts[:threads] || opts['threads'] || Threadify.threads)
|
22
24
|
done = Object.new.freeze
|
23
|
-
|
25
|
+
nothing = done
|
26
|
+
#jobs = Array.new(threads).map{ Queue.new }
|
27
|
+
jobs = Array.new(threads).map{ [] }
|
28
|
+
top = Thread.current
|
24
29
|
|
25
30
|
# produce jobs
|
26
31
|
#
|
27
|
-
|
28
|
-
|
32
|
+
#producer = Thread.new do
|
33
|
+
#this = Thread.current
|
34
|
+
#this.abort_on_exception = Threadify.abort_on_exception
|
35
|
+
|
36
|
+
each_with_index{|args, i| jobs[i % threads].push([args, i])}
|
37
|
+
threads.times{|i| jobs[i].push(done)}
|
38
|
+
#end
|
29
39
|
|
30
|
-
#
|
40
|
+
# setup consumer list
|
31
41
|
#
|
32
42
|
consumers = Array.new threads
|
33
43
|
|
44
|
+
# setup support for short-circuit bailout via 'throw :threadify'
|
45
|
+
#
|
46
|
+
thrownv = Hash.new
|
47
|
+
thrownq = Queue.new
|
48
|
+
|
49
|
+
caught = false
|
50
|
+
|
51
|
+
catcher = Thread.new do
|
52
|
+
loop do
|
53
|
+
thrown = thrownq.pop
|
54
|
+
break if thrown == done
|
55
|
+
i, thrown = thrown
|
56
|
+
thrownv[i] = thrown
|
57
|
+
caught = true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# fire off the consumers
|
62
|
+
#
|
34
63
|
threads.times do |i|
|
35
|
-
consumers[i] = Thread.new do
|
64
|
+
consumers[i] = Thread.new(jobs[i]) do |jobsi|
|
36
65
|
this = Thread.current
|
37
66
|
this.abort_on_exception = Threadify.abort_on_exception
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
67
|
+
|
68
|
+
job = nil
|
69
|
+
|
70
|
+
thrown =
|
71
|
+
catch(:threadify) do
|
72
|
+
loop{
|
73
|
+
break if caught
|
74
|
+
#job = jobsi.pop
|
75
|
+
job = jobsi.shift
|
76
|
+
break if job == done
|
77
|
+
args = job.first
|
78
|
+
jobsi << (job << block.call(*args))
|
79
|
+
}
|
80
|
+
nothing
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
unless nothing == thrown
|
85
|
+
args, i = job
|
86
|
+
thrownq.push [i, thrown]
|
87
|
+
end
|
44
88
|
end
|
45
89
|
end
|
46
90
|
|
@@ -48,8 +92,22 @@ module Enumerable
|
|
48
92
|
#
|
49
93
|
consumers.map{|t| t.join}
|
50
94
|
|
95
|
+
# nuke the catcher
|
96
|
+
#
|
97
|
+
thrownq.push done
|
98
|
+
catcher.join
|
99
|
+
|
100
|
+
# iff something(s) was thrown return the one which would have been thrown
|
101
|
+
# earliest in non-parallel execution
|
102
|
+
#
|
103
|
+
unless thrownv.empty?
|
104
|
+
key = thrownv.keys.sort.first
|
105
|
+
return thrownv[key]
|
106
|
+
end
|
107
|
+
|
51
108
|
# collect the results and return them
|
52
109
|
#
|
110
|
+
=begin
|
53
111
|
jobs.push done
|
54
112
|
ret = []
|
55
113
|
while((job = jobs.pop) != done)
|
@@ -58,6 +116,19 @@ module Enumerable
|
|
58
116
|
end
|
59
117
|
ret
|
60
118
|
end
|
119
|
+
=end
|
120
|
+
|
121
|
+
ret = []
|
122
|
+
jobs.each do |results|
|
123
|
+
results.each do |result|
|
124
|
+
break if result == done
|
125
|
+
elem, i, value = result
|
126
|
+
ret[i] = value
|
127
|
+
end
|
128
|
+
end
|
129
|
+
ret
|
130
|
+
end
|
131
|
+
|
61
132
|
end
|
62
133
|
|
63
134
|
class Thread
|
@@ -66,6 +137,12 @@ class Thread
|
|
66
137
|
end
|
67
138
|
end
|
68
139
|
|
140
|
+
class Object
|
141
|
+
def threadify! *values
|
142
|
+
throw :threadify, *values
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
69
146
|
|
70
147
|
if __FILE__ == $0
|
71
148
|
require 'open-uri'
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Threadify
|
2
|
+
VERSION = '0.0.3'
|
3
|
+
def Threadify.version() Threadify::VERSION end
|
4
|
+
|
5
|
+
require 'thread'
|
6
|
+
|
7
|
+
@threads = 8
|
8
|
+
@abort_on_exception = true
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :threads
|
12
|
+
attr_accessor :abort_on_exception
|
13
|
+
end
|
14
|
+
|
15
|
+
class Error < ::StandardError; end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Enumerable
|
19
|
+
def threadify opts = {}, &block
|
20
|
+
# setup
|
21
|
+
#
|
22
|
+
opts = {:threads => opts} if Numeric === opts
|
23
|
+
threads = Integer(opts[:threads] || opts['threads'] || Threadify.threads)
|
24
|
+
done = Object.new.freeze
|
25
|
+
nothing = done
|
26
|
+
jobs = Queue.new
|
27
|
+
top = Thread.current
|
28
|
+
|
29
|
+
# produce jobs
|
30
|
+
#
|
31
|
+
producer = Thread.new do
|
32
|
+
each_with_index{|args, i| jobs.push [args, i]}
|
33
|
+
threads.times{ jobs.push done}
|
34
|
+
end
|
35
|
+
|
36
|
+
# setup consumer list
|
37
|
+
#
|
38
|
+
consumers = Array.new threads
|
39
|
+
|
40
|
+
# setup support for short-circuit bailout via 'throw :threadify'
|
41
|
+
#
|
42
|
+
thrownv = Hash.new
|
43
|
+
thrownq = Queue.new
|
44
|
+
|
45
|
+
caught = false
|
46
|
+
|
47
|
+
catcher = Thread.new do
|
48
|
+
loop do
|
49
|
+
thrown = thrownq.pop
|
50
|
+
break if thrown == done
|
51
|
+
i, thrown = thrown
|
52
|
+
thrownv[i] = thrown
|
53
|
+
caught = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# fire off the consumers
|
58
|
+
#
|
59
|
+
threads.times do |i|
|
60
|
+
consumers[i] =
|
61
|
+
Thread.new do
|
62
|
+
this = Thread.current
|
63
|
+
this.abort_on_exception = Threadify.abort_on_exception
|
64
|
+
|
65
|
+
job = nil
|
66
|
+
|
67
|
+
thrown =
|
68
|
+
catch(:threadify) do
|
69
|
+
loop{
|
70
|
+
break if caught
|
71
|
+
job = jobs.pop
|
72
|
+
break if job == done
|
73
|
+
args = job.first
|
74
|
+
jobs << (job << block.call(*args))
|
75
|
+
}
|
76
|
+
nothing
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
unless nothing == thrown
|
81
|
+
args, i = job
|
82
|
+
thrownq.push [i, thrown]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# wait for consumers to finish
|
88
|
+
#
|
89
|
+
consumers.map{|t| t.join}
|
90
|
+
|
91
|
+
# nuke the catcher
|
92
|
+
#
|
93
|
+
thrownq.push done
|
94
|
+
catcher.join
|
95
|
+
|
96
|
+
# iff something(s) was thrown return the one which would have been thrown
|
97
|
+
# earliest in non-parallel execution
|
98
|
+
#
|
99
|
+
unless thrownv.empty?
|
100
|
+
key = thrownv.keys.sort.first
|
101
|
+
return thrownv[key]
|
102
|
+
end
|
103
|
+
|
104
|
+
# collect the results and return them
|
105
|
+
#
|
106
|
+
jobs.push done
|
107
|
+
ret = []
|
108
|
+
while((job = jobs.pop) != done)
|
109
|
+
elem, i, value = job
|
110
|
+
ret[i] = value
|
111
|
+
end
|
112
|
+
ret
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
class Thread
|
118
|
+
def Thread.ify enumerable, *args, &block
|
119
|
+
enumerable.send :threadify, *args, &block
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class Object
|
124
|
+
def threadify! *values
|
125
|
+
throw :threadify, *values
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
if __FILE__ == $0
|
131
|
+
require 'open-uri'
|
132
|
+
require 'yaml'
|
133
|
+
|
134
|
+
uris = %w( http://google.com http://yahoo.com http://rubyforge.org/ http://ruby-lang.org)
|
135
|
+
|
136
|
+
Thread.ify uris, :threads => 3 do |uri|
|
137
|
+
body = open(uri){|pipe| pipe.read}
|
138
|
+
y uri => body.size
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
__END__
|
144
|
+
|
145
|
+
sample output
|
146
|
+
|
147
|
+
---
|
148
|
+
http://yahoo.com: 9562
|
149
|
+
---
|
150
|
+
http://google.com: 6290
|
151
|
+
---
|
152
|
+
http://rubyforge.org/: 22352
|
153
|
+
---
|
154
|
+
http://ruby-lang.org: 9984
|
data/sample/b.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'threadify'
|
5
|
+
|
6
|
+
size = Integer(ARGV.shift || (2 ** 15))
|
7
|
+
|
8
|
+
haystack = Array.new(size){|i| i}
|
9
|
+
needle = 2 * (size / 3)
|
10
|
+
|
11
|
+
a, b = 4, 2
|
12
|
+
|
13
|
+
time 'without threadify' do
|
14
|
+
a = haystack.each{|value| break value if value == needle}
|
15
|
+
end
|
16
|
+
|
17
|
+
time 'with threadify' do
|
18
|
+
b = haystack.threadify(16){|value| threadify! value if value == needle}
|
19
|
+
end
|
20
|
+
|
21
|
+
raise if a != b
|
22
|
+
|
23
|
+
y :a => a, :b => b, :needle => needle
|
24
|
+
|
25
|
+
BEGIN {
|
26
|
+
def time label
|
27
|
+
a = Time.now.to_f
|
28
|
+
yield
|
29
|
+
ensure
|
30
|
+
b = Time.now.to_f
|
31
|
+
y label => (b - a)
|
32
|
+
end
|
33
|
+
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: threadify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ara T. Howard
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-07-
|
12
|
+
date: 2008-07-11 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -22,15 +22,17 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
+
- a.rb
|
25
26
|
- gemspec.rb
|
26
27
|
- gen_readme.rb
|
27
28
|
- install.rb
|
28
29
|
- lib
|
29
30
|
- lib/threadify.rb
|
31
|
+
- lib/threadify.rb.bak
|
30
32
|
- README
|
31
33
|
- sample
|
32
34
|
- sample/a.rb
|
33
|
-
-
|
35
|
+
- sample/b.rb
|
34
36
|
has_rdoc: false
|
35
37
|
homepage: http://codeforpeople.com/lib/ruby/threadify/
|
36
38
|
post_install_message:
|
data/threadify-0.0.2.gem
DELETED
File without changes
|