threadify 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|