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 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: 7.41900205612183
70
+ without threadify: 3.75206303596497
72
71
  ---
73
- with threadify: 3.69886112213135
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
@@ -0,0 +1,10 @@
1
+ require 'threadify'
2
+
3
+ a = Array.new(1024){|i| i}
4
+
5
+ value =
6
+ a.threadify do |i|
7
+ threadify! i if i == 512
8
+ end
9
+
10
+ p value
@@ -1,5 +1,5 @@
1
1
  module Threadify
2
- VERSION = '0.0.2'
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
- jobs = Queue.new
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
- each_with_index{|elem, i| jobs.push [elem, i]}
28
- threads.times{ jobs.push done} # mark the end
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
- # fire off consumers
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
- loop{
39
- job = jobs.pop
40
- break if job == done
41
- args = job.first
42
- jobs << (job << block.call(*args))
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
@@ -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.2
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-01 00:00:00 -06:00
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
- - threadify-0.0.2.gem
35
+ - sample/b.rb
34
36
  has_rdoc: false
35
37
  homepage: http://codeforpeople.com/lib/ruby/threadify/
36
38
  post_install_message:
File without changes