comp_tree 0.5.2 → 0.7.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.
Files changed (56) hide show
  1. data/CHANGES +24 -0
  2. data/README +19 -52
  3. data/Rakefile +1 -138
  4. data/comp_tree.gemspec +33 -30
  5. data/install.rb +3 -3
  6. data/lib/comp_tree/algorithm.rb +117 -156
  7. data/lib/comp_tree/driver.rb +39 -154
  8. data/lib/comp_tree/error.rb +18 -23
  9. data/lib/comp_tree/node.rb +46 -50
  10. data/lib/comp_tree.rb +56 -0
  11. data/rakelib/jumpstart/ruby.rb +51 -0
  12. data/{contrib/quix/lib/quix → rakelib/jumpstart}/simple_installer.rb +11 -13
  13. data/test/common.rb +29 -0
  14. data/test/test_basic.rb +189 -0
  15. data/test/test_circular.rb +34 -31
  16. data/test/test_drain.rb +38 -0
  17. data/test/test_exception.rb +37 -86
  18. data/test/test_flood.rb +14 -0
  19. data/test/test_grind.rb +77 -0
  20. data/test/test_sequential.rb +21 -0
  21. metadata +45 -58
  22. data/contrib/quix/Rakefile +0 -16
  23. data/contrib/quix/install.rb +0 -3
  24. data/contrib/quix/lib/quix/builtin/dir/casefold_brackets.rb +0 -7
  25. data/contrib/quix/lib/quix/builtin/kernel/tap.rb +0 -9
  26. data/contrib/quix/lib/quix/builtin/module/include.rb +0 -21
  27. data/contrib/quix/lib/quix/builtin/module/private.rb +0 -41
  28. data/contrib/quix/lib/quix/config.rb +0 -37
  29. data/contrib/quix/lib/quix/cygwin.rb +0 -60
  30. data/contrib/quix/lib/quix/diagnostic.rb +0 -44
  31. data/contrib/quix/lib/quix/enumerable.rb +0 -33
  32. data/contrib/quix/lib/quix/fileutils.rb +0 -37
  33. data/contrib/quix/lib/quix/hash_struct.rb +0 -27
  34. data/contrib/quix/lib/quix/kernel.rb +0 -61
  35. data/contrib/quix/lib/quix/lazy_struct.rb +0 -55
  36. data/contrib/quix/lib/quix/string.rb +0 -38
  37. data/contrib/quix/lib/quix/subpackager.rb +0 -52
  38. data/contrib/quix/lib/quix/thread_local.rb +0 -32
  39. data/contrib/quix/lib/quix/vars.rb +0 -138
  40. data/contrib/quix/lib/quix.rb +0 -32
  41. data/contrib/quix/test/all.rb +0 -12
  42. data/contrib/quix/test/test_deps.rb +0 -25
  43. data/contrib/quix/test/test_include.rb +0 -47
  44. data/contrib/quix/test/test_private.rb +0 -86
  45. data/contrib/quix/test/test_root.rb +0 -19
  46. data/contrib/quix/test/test_struct.rb +0 -48
  47. data/contrib/quix/test/test_vars.rb +0 -187
  48. data/lib/comp_tree/bucket_ipc.rb +0 -151
  49. data/lib/comp_tree/diagnostic.rb +0 -44
  50. data/lib/comp_tree/misc.rb +0 -61
  51. data/lib/comp_tree/retriable_fork.rb +0 -42
  52. data/lib/comp_tree/tap.rb +0 -9
  53. data/lib/comp_tree/task_node.rb +0 -22
  54. data/test/all.rb +0 -12
  55. data/test/test_bucketipc.rb +0 -72
  56. data/test/test_comp_tree.rb +0 -364
data/test/common.rb ADDED
@@ -0,0 +1,29 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
2
+
3
+ require 'test/unit'
4
+ require 'comp_tree'
5
+ require 'benchmark'
6
+
7
+ module TestCommon
8
+ if ARGV.include?("--bench")
9
+ def separator
10
+ puts
11
+ puts "-"*60
12
+ end
13
+
14
+ def bench_output(desc = nil, stream = STDOUT, &block)
15
+ if desc
16
+ stream.puts(desc)
17
+ end
18
+ if block
19
+ expression = block.call
20
+ result = eval(expression, block.binding)
21
+ stream.printf("%-16s => %s\n", expression, result.inspect)
22
+ result
23
+ end
24
+ end
25
+ else
26
+ def separator() end
27
+ def bench_output(desc = nil, stream = STDOUT, &block) end
28
+ end
29
+ end
@@ -0,0 +1,189 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class TestBasic < Test::Unit::TestCase
4
+ def test_define
5
+ (1..20).each { |threads|
6
+ CompTree.build { |driver|
7
+ driver.define(:area, :width, :height, :offset) { |width, height, offset|
8
+ width*height - offset
9
+ }
10
+
11
+ driver.define(:width, :border) { |border|
12
+ 2 + border
13
+ }
14
+
15
+ driver.define(:height, :border) { |border|
16
+ 3 + border
17
+ }
18
+
19
+ driver.define(:border) {
20
+ 5
21
+ }
22
+
23
+ driver.define(:offset) {
24
+ 7
25
+ }
26
+
27
+ assert_equal((2 + 5)*(3 + 5) - 7, driver.compute(:area, threads))
28
+ }
29
+ }
30
+ end
31
+
32
+ def test_already_computed
33
+ [nil, false, true, 33].each { |result|
34
+ CompTree.build { |driver|
35
+ driver.define(:a) { result }
36
+ (1..3).each { |n|
37
+ assert_equal(result, driver.compute(:a, n))
38
+ }
39
+ }
40
+ }
41
+ end
42
+
43
+ def test_threads_opt
44
+ (1..20).each { |threads|
45
+ CompTree.build do |driver|
46
+ driver.define(:a) { 33 }
47
+ assert_equal(33, driver.compute(:a, :threads => threads))
48
+ end
49
+ }
50
+ end
51
+
52
+ def test_malformed
53
+ CompTree.build { |driver|
54
+ assert_raise(CompTree::ArgumentError) {
55
+ driver.define {
56
+ }
57
+ }
58
+ assert_raise(CompTree::RedefinitionError) {
59
+ driver.define(:a) {
60
+ }
61
+ driver.define(:a) {
62
+ }
63
+ }
64
+ assert_raise(CompTree::ArgumentError) {
65
+ driver.define(:b) {
66
+ }
67
+ driver.compute(:b, 0)
68
+ }
69
+ assert_raise(CompTree::ArgumentError) {
70
+ driver.define(:c) {
71
+ }
72
+ driver.compute(:c, -1)
73
+ }
74
+ }
75
+ end
76
+
77
+ def test_exception_in_compute
78
+ test_error = Class.new(RuntimeError)
79
+ CompTree.build { |driver|
80
+ driver.define(:area, :width, :height, :offset) { |width, height, offset|
81
+ width*height - offset
82
+ }
83
+
84
+ driver.define(:width, :border) { |border|
85
+ 2 + border
86
+ }
87
+
88
+ driver.define(:height, :border) { |border|
89
+ 3 + border
90
+ }
91
+
92
+ driver.define(:border) {
93
+ raise test_error
94
+ }
95
+
96
+ driver.define(:offset) {
97
+ 7
98
+ }
99
+
100
+ assert_raise(test_error) {
101
+ driver.compute(:area, 6)
102
+ }
103
+ }
104
+ end
105
+
106
+ def test_node_subclass
107
+ data = Object.new
108
+ subclass = Class.new(CompTree::Node) {
109
+ define_method :stuff do
110
+ data
111
+ end
112
+ }
113
+ CompTree.build(:node_class => subclass) { |driver|
114
+ driver.define(:a) { }
115
+ assert_equal(data, driver.nodes[:a].stuff)
116
+ }
117
+ end
118
+
119
+ def test_non_symbols
120
+ width_id = Object.new
121
+ height_id = 272727
122
+ (1..3).each { |threads|
123
+ CompTree.build { |driver|
124
+ driver.define("area", width_id, height_id, :offset) {
125
+ |width, height, offset|
126
+ width*height - offset
127
+ }
128
+
129
+ driver.define(width_id, :border) { |border|
130
+ 2 + border
131
+ }
132
+
133
+ driver.define(height_id, :border) { |border|
134
+ 3 + border
135
+ }
136
+
137
+ driver.define(:border) {
138
+ 5
139
+ }
140
+
141
+ driver.define(:offset) {
142
+ 7
143
+ }
144
+
145
+ assert_equal((2 + 5)*(3 + 5) - 7, driver.compute("area", threads))
146
+ }
147
+ }
148
+ end
149
+
150
+ def test_node_name_equality_comparison
151
+ CompTree.build { |driver|
152
+ driver.define("hello") { }
153
+ assert_raise(CompTree::RedefinitionError) {
154
+ driver.define("hello") { }
155
+ }
156
+ }
157
+ end
158
+
159
+ def test_result_variety
160
+ [true, false, nil, Object.new, 33].each { |result|
161
+ (1..20).each { |threads|
162
+ CompTree.build { |driver|
163
+ driver.define(:area, :width, :height, :offset) {
164
+ |width, height, offset|
165
+ result
166
+ }
167
+
168
+ driver.define(:width, :border) { |border|
169
+ result
170
+ }
171
+
172
+ driver.define(:height, :border) { |border|
173
+ result
174
+ }
175
+
176
+ driver.define(:border) {
177
+ result
178
+ }
179
+
180
+ driver.define(:offset) {
181
+ result
182
+ }
183
+
184
+ assert_equal(result, driver.compute(:area, threads))
185
+ }
186
+ }
187
+ }
188
+ end
189
+ end
@@ -1,36 +1,39 @@
1
- $LOAD_PATH.unshift(File.expand_path("#{File.dirname(__FILE__)}/../lib"))
1
+ require File.dirname(__FILE__) + "/common"
2
2
 
3
- require 'comp_tree'
4
- require 'test/unit'
3
+ class TestCircular < Test::Unit::TestCase
4
+ def test_circular
5
+ CompTree.build { |driver|
6
+ driver.define(:area, :width, :height, :offset) { |width, height, offset|
7
+ width*height - offset
8
+ }
9
+
10
+ driver.define(:width, :border) { |border|
11
+ 2 + border
12
+ }
13
+
14
+ driver.define(:height, :border) { |border|
15
+ 3 + border
16
+ }
17
+
18
+ driver.define(:border) {
19
+ 5
20
+ }
21
+
22
+ driver.define(:offset, :area) {
23
+ 7
24
+ }
5
25
 
6
- module CompTree
7
- class TestCircular < Test::Unit::TestCase
8
- def test_1
9
- CompTree::Driver.new { |driver|
10
- driver.define(:area, :width, :height, :offset) { |width, height, offset|
11
- width*height - offset
12
- }
13
-
14
- driver.define(:width, :border) { |border|
15
- 2 + border
16
- }
17
-
18
- driver.define(:height, :border) { |border|
19
- 3 + border
20
- }
21
-
22
- driver.define(:border) {
23
- 5
24
- }
25
-
26
- driver.define(:offset, :area) {
27
- 7
28
- }
26
+ assert_equal([:area, :offset, :area], driver.check_circular(:area))
27
+ assert_equal([:offset, :area, :offset], driver.check_circular(:offset))
28
+ }
29
+ end
29
30
 
30
- assert_raises(Error::CircularError) {
31
- driver.check_circular(:area)
32
- }
33
- }
34
- end
31
+ def test_not_circular
32
+ CompTree.build { |driver|
33
+ driver.define(:a, :b) { true }
34
+ driver.define(:b) { true }
35
+ assert_nil(driver.check_circular(:a))
36
+ assert_nil(driver.check_circular(:b))
37
+ }
35
38
  end
36
39
  end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class TestDrain < Test::Unit::TestCase
4
+ include TestCommon
5
+
6
+ def drain
7
+ 500000.times { }
8
+ end
9
+
10
+ def run_drain(threads)
11
+ CompTree.build { |driver|
12
+ func = lambda { |*args|
13
+ drain
14
+ }
15
+ driver.define(:area, :width, :height, :offset, &func)
16
+ driver.define(:width, :border, &func)
17
+ driver.define(:height, :border, &func)
18
+ driver.define(:border, &func)
19
+ driver.define(:offset, &func)
20
+ bench_output "number of threads: #{threads}"
21
+ bench = Benchmark.measure { driver.compute(:area, threads) }
22
+ bench_output bench
23
+ }
24
+ end
25
+
26
+ def each_drain
27
+ (1..10).each { |threads|
28
+ yield threads
29
+ }
30
+ end
31
+
32
+ def test_drain
33
+ separator
34
+ each_drain { |threads|
35
+ run_drain(threads)
36
+ }
37
+ end
38
+ end
@@ -1,97 +1,48 @@
1
-
2
- #
3
- # Mean workaround using separate processes due to assert_raise causing
4
- # problems with threads.
5
- #
6
-
7
- require 'test/unit'
8
-
9
- module WorkaroundConfig
10
- HERE = File.dirname(__FILE__)
11
- LIB_DIR = File.expand_path("#{HERE}/../lib")
12
- QUIX_LIB_DIR = File.expand_path("#{HERE}/../contrib/quix/lib")
13
- OUTPUT_FILE = "#{HERE}/#{File.basename(__FILE__)}.output"
14
-
15
- $LOAD_PATH.unshift LIB_DIR
16
- $LOAD_PATH.unshift QUIX_LIB_DIR
17
- end
18
-
19
- require 'comp_tree'
20
- require 'quix/config'
21
-
22
- module CompTree
23
- class TestRaises < Test::Unit::TestCase
24
- include WorkaroundConfig
25
-
26
- def test_exception
27
- if RUBY_PLATFORM =~ %r!java!
28
- puts "skipping #{File.basename(__FILE__)}."
29
- else
30
- [true, false].each { |use_fork|
31
- [true, false].each { |define_all|
32
- assert(
33
- !system(
34
- ::Quix::Config.ruby_executable,
35
- "-e",
36
- code(use_fork, define_all)))
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class TestException < Test::Unit::TestCase
4
+ def test_exception
5
+ test_error = Class.new StandardError
6
+ [true, false].each { |define_all|
7
+ error = (
8
+ begin
9
+ CompTree.build { |driver|
10
+ driver.define(:area, :width, :height, :offset) {
11
+ |width, height, offset|
12
+ width*height - offset
13
+ }
37
14
 
38
- output = File.read(OUTPUT_FILE)
15
+ driver.define(:width, :border) { |border|
16
+ 2 + border
17
+ }
39
18
 
19
+ driver.define(:height, :border) { |border|
20
+ 3 + border
21
+ }
22
+
40
23
  if define_all
41
- assert_match(%r!CompTreeTestError!, output)
42
- else
43
- assert_match(%r!NoFunctionError!, output)
24
+ driver.define(:border) {
25
+ raise test_error
26
+ }
44
27
  end
45
28
 
46
- File.unlink(OUTPUT_FILE) # leave when exception raised above
47
- }
48
- }
49
- end
50
- end
51
-
52
- def code(use_fork, define_all)
53
- %Q(
54
- $LOAD_PATH.unshift '#{LIB_DIR}'
55
- require 'comp_tree'
56
- require 'open3'
57
-
58
- class CompTreeTestError < Exception ; end
59
-
60
- CompTree::Driver.new { |driver|
61
- driver.define(:area, :width, :height, :offset) {
62
- |width, height, offset|
63
- width*height - offset
64
- }
65
-
66
- driver.define(:width, :border) { |border|
67
- 2 + border
29
+ driver.define(:offset) {
30
+ 7
31
+ }
32
+
33
+ driver.compute(:area, 99)
68
34
  }
35
+ nil
36
+ rescue => e
37
+ e
38
+ end
39
+ )
69
40
 
70
- driver.define(:height, :border) { |border|
71
- 3 + border
72
- }
73
- ) +
74
41
  if define_all
75
- %Q(
76
- driver.define(:border) {
77
- raise CompTreeTestError
78
- }
79
- )
42
+ assert_block { error.is_a? test_error }
80
43
  else
81
- ""
82
- end +
83
- %Q(
84
- driver.define(:offset) {
85
- 7
86
- }
87
-
88
- File.open('#{OUTPUT_FILE}', "w") { |out|
89
- $stderr = out
90
- driver.compute(
91
- :area, :threads => 99, :fork => #{use_fork.inspect})
92
- }
93
- }
94
- )
95
- end
44
+ assert_block { error.is_a? CompTree::NoFunctionError }
45
+ end
46
+ }
96
47
  end
97
48
  end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class TestFlood < Test::Unit::TestCase
4
+ def test_thread_flood
5
+ (1..200).each { |num_threads|
6
+ CompTree.build { |driver|
7
+ noop = lambda { |*args| true }
8
+ driver.define(:a, :b, &noop)
9
+ driver.define(:b, &noop)
10
+ driver.compute(:a, num_threads)
11
+ }
12
+ }
13
+ end
14
+ end
@@ -0,0 +1,77 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class TestGrind < Test::Unit::TestCase
4
+ include TestCommon
5
+
6
+ GENERATOR_DATA = {
7
+ :level_range => 1..4,
8
+ :children_range => 1..6,
9
+ :thread_range => 1..6,
10
+ :drain_iterations => 0,
11
+ }
12
+
13
+ ROOT = 'a'
14
+
15
+ def test_grind
16
+ run_generated_tree(GENERATOR_DATA)
17
+ end
18
+
19
+ def generate_comp_tree(num_levels, num_children, drain_iterations)
20
+ CompTree.build { |driver|
21
+ name_gen = ROOT.dup
22
+ pick_names = lambda { |*args|
23
+ (0..rand(num_children)).map {
24
+ name_gen.succ!
25
+ name_gen.dup
26
+ }
27
+ }
28
+ drain = lambda { |*args|
29
+ drain_iterations.times {
30
+ }
31
+ }
32
+ build_tree = lambda { |parent, children, level|
33
+ #trace "building #{parent} --> #{children.join(' ')}"
34
+
35
+ driver.define(parent, *children, &drain)
36
+
37
+ if level < num_levels
38
+ children.each { |child|
39
+ build_tree.call(child, pick_names.call, level + 1)
40
+ }
41
+ else
42
+ children.each { |child|
43
+ driver.define(child, &drain)
44
+ }
45
+ end
46
+ }
47
+ build_tree.call(ROOT, pick_names.call, drain_iterations)
48
+ driver
49
+ }
50
+ end
51
+
52
+ def run_generated_tree(args)
53
+ args[:level_range].each { |num_levels|
54
+ args[:children_range].each { |num_children|
55
+ separator
56
+ bench_output {%{num_levels}}
57
+ bench_output {%{num_children}}
58
+ driver = generate_comp_tree(
59
+ num_levels,
60
+ num_children,
61
+ args[:drain_iterations])
62
+ args[:thread_range].each { |threads|
63
+ bench_output {%{threads}}
64
+ 2.times {
65
+ driver.reset(ROOT)
66
+ result = nil
67
+ bench = Benchmark.measure {
68
+ result = driver.compute(ROOT, threads)
69
+ }
70
+ bench_output bench
71
+ assert_equal(result, args[:drain_iterations])
72
+ }
73
+ }
74
+ }
75
+ }
76
+ end
77
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class TestSequential < Test::Unit::TestCase
4
+ def test_sequential
5
+ (1..50).each { |num_threads|
6
+ [1, 2, 3, 20, 50].each { |num_nodes|
7
+ CompTree.build { |driver|
8
+ driver.define(:root) { true }
9
+ (1..num_nodes).each { |n|
10
+ if n == 0
11
+ driver.define("a#{n}".to_s, :root) { true }
12
+ else
13
+ driver.define("a#{n}".to_s, "a#{n-1}".to_s) { true }
14
+ end
15
+ }
16
+ driver.compute(:root, num_threads)
17
+ }
18
+ }
19
+ }
20
+ end
21
+ end