gravitext-util 1.4.0-java

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc ADDED
@@ -0,0 +1,36 @@
1
+ === 1.4.0 (2010-5-8)
2
+ * New generic UniMap (Java) with ruby dynamic accessor extension.
3
+ * New Charsets constants.
4
+ * New Streams utilities; ByteBufferInputStream and ByteArrayInputStream.
5
+ * New ResizableByteBufferOutputStream.
6
+ * New Closeables utilities.
7
+ * ArrayHTMap and HashHTMap are no longer final (supports UniMap).
8
+ * Added immutable public KeySpace.keys().
9
+ * Added convenience methods KeySpace.createGeneric() and createListKey().
10
+ * Added URL64 encoder/decoder utility.
11
+ * Added explicit dev dependencies for rjack-slf4j and rjack-logback.
12
+ * Remove old/deprecated perftest.* harness and ConcurrentTest*
13
+ * Use rjack-tarpit for build.
14
+ * Set gem platform to java.
15
+
16
+ === 1.3.2 (2009-8-6)
17
+ * Add ConstrainedInputStream.readLength() and mark/reset support.
18
+
19
+ === 1.3.1 (2009-8-2)
20
+ * ResizableByteBuffer.putFromStream break on zero length read.
21
+ * Add ConstrainedInputStream.
22
+ * Use rdoc 2.4.3 and hoe 1.12.2 for build.
23
+
24
+ === 1.3 (2009-2-23)
25
+ * New gem packaging.
26
+ * Replaced java perftest harness with ruby test harness/wiring.
27
+ * Split heterogeneous access methods from HTMap to HTAccess interface
28
+ to enable generic filters and other consumers.
29
+ * Non-checked exception version of Closeable interface.
30
+
31
+ === 1.2 (2008-4-28)
32
+ * Added ResizableByteBuffer, ResizableCharBuffer,
33
+ ResizableCharBufferWriter classes.
34
+
35
+ === 1.1 (2007-7-15)
36
+ * First public release.
data/Manifest.txt ADDED
@@ -0,0 +1,16 @@
1
+ History.rdoc
2
+ Manifest.txt
3
+ NOTICE.txt
4
+ README.rdoc
5
+ Rakefile
6
+ pom.xml
7
+ bin/gravitext-perftest
8
+ lib/gravitext-util/version.rb
9
+ lib/gravitext-util.rb
10
+ lib/gravitext-util/concurrent.rb
11
+ lib/gravitext-util/perftest.rb
12
+ lib/gravitext-util/unimap.rb
13
+ test/test_concurrent.rb
14
+ test/test_perftest.rb
15
+ test/test_unimap.rb
16
+ lib/gravitext-util/gravitext-util-1.4.0.jar
data/NOTICE.txt ADDED
@@ -0,0 +1,2 @@
1
+ gravitext-util
2
+ Copyright (c) 2007-2010 David Kellum
data/README.rdoc ADDED
@@ -0,0 +1,45 @@
1
+ = gravitext-util
2
+
3
+ * http://gravitext.rubyforge.org
4
+ * http://gravitext.com/oss/gravitext-util
5
+ * http://github.com/dekellum/gravitext
6
+
7
+ == Description
8
+
9
+ A collection of core java utilities with ruby adapters for JRuby.
10
+
11
+ * A concurrent (thread safety) testing facility for java JUnit,
12
+ ruby Test::Unit, or other test harnesses.
13
+
14
+ * A concurrent performance testing facility for java/JRuby with simple
15
+ test wiring in ruby.
16
+
17
+ * A Heterogeneous Type-safe Map implementation in java with
18
+ dynamically generated ruby accessors.
19
+
20
+ * A set of core java utility classes (resizable buffers, a stopwatch,
21
+ SI unit formatting, fast random number generator), in support of the
22
+ above and of general utility.
23
+
24
+ == Dependencies
25
+
26
+ * Java 1.5+
27
+ * JRuby 1.1.6+
28
+ * rjack-slf4j[http://rjack.rubyforge.org/slf4j] and
29
+ rjack-logback[http://rjack.rubyforge.org/logback] gems for testing.
30
+
31
+ == License
32
+
33
+ Copyright (c) 2007-2010 David Kellum
34
+
35
+ Licensed under the Apache License, Version 2.0 (the "License"); you
36
+ may not use this file except in compliance with the License. You
37
+ may obtain a copy of the License at:
38
+
39
+ http://www.apache.org/licenses/LICENSE-2.0
40
+
41
+ Unless required by applicable law or agreed to in writing, software
42
+ distributed under the License is distributed on an "AS IS" BASIS,
43
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
44
+ implied. See the License for the specific language governing
45
+ permissions and limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # -*- ruby -*-
2
+
3
+ $LOAD_PATH << './lib'
4
+ require 'gravitext-util/version'
5
+
6
+ require 'rubygems'
7
+ gem 'rjack-tarpit', '~> 1.2.0'
8
+ require 'rjack-tarpit'
9
+
10
+ t = RJack::TarPit.new( 'gravitext-util',
11
+ Gravitext::Util::VERSION,
12
+ :no_assembly, :java_platform )
13
+
14
+ t.specify do |h|
15
+ h.developer( "David Kellum", "dek-oss@gravitext.com" )
16
+ h.extra_dev_deps += [ [ 'rjack-slf4j', '~> 1.5.8' ],
17
+ [ 'rjack-logback', '>= 0.9.17' ] ]
18
+ h.rubyforge_name = "gravitext"
19
+ end
20
+
21
+ file 'Manifest.txt' => [ 'lib/gravitext-util/version.rb' ]
22
+
23
+ task :check_pom_version do
24
+ t.test_line_match( 'pom.xml', /<version>/, /#{t.version}/ )
25
+ end
26
+ task :check_history_version do
27
+ t.test_line_match( 'History.rdoc', /^==/, / #{t.version} / )
28
+ end
29
+ task :check_history_date do
30
+ t.test_line_match( 'History.rdoc', /^==/, /\([0-9\-]+\)$/ )
31
+ end
32
+
33
+ task :gem => [ :check_pom_version, :check_history_version ]
34
+ task :tag => [ :check_pom_version, :check_history_version, :check_history_date ]
35
+ task :push => [ :check_history_date ]
36
+
37
+ t.define_tasks
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env jruby
2
+ # -*- ruby -*-
3
+ #--
4
+ # Copyright (c) 2007-2010 David Kellum
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
7
+ # may not use this file except in compliance with the License. You
8
+ # may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15
+ # implied. See the License for the specific language governing
16
+ # permissions and limitations under the License.
17
+ #++
18
+
19
+ $LOAD_PATH.unshift File.join( File.dirname(__FILE__), "..", "lib" )
20
+
21
+ require 'gravitext-util'
22
+ require 'gravitext-util/perftest'
23
+ require 'java'
24
+
25
+ module TestCollection
26
+ include Gravitext
27
+ include Gravitext::Concurrent
28
+
29
+ import 'com.gravitext.htmap.perftests.HTMapPerfTest'
30
+ import 'com.gravitext.perftest.tests.EmptyPerfTest'
31
+ import 'com.gravitext.perftest.tests.SortPerfTest'
32
+ import 'com.gravitext.util.perftests.ByteBufferInputStreamPerfTest'
33
+ import 'com.gravitext.util.perftests.FastRandomPerfTest'
34
+ import 'com.gravitext.util.perftests.ResizableCharBufferWriterPerfTest'
35
+
36
+ def self.run
37
+ tests = TestCollection::lookup_factories( ARGV[0] || 'random' )
38
+
39
+ harness = PerfTest::Harness.new( tests )
40
+ # harness.do_per_run_timing = false #FIXME
41
+ # harness.thread_count = 2
42
+ # harness.final_runs = 10000
43
+
44
+ harness.execute
45
+ end
46
+
47
+ def self.lookup_factories( name )
48
+ case name
49
+ when 'random'
50
+ tests = FastRandomPerfTest::Mode.values.map do |mode|
51
+ FastRandomPerfTest.new( mode )
52
+ end
53
+
54
+ btest = BlockTestFactory.new do |run,fr|
55
+ 10_000.times do
56
+ rand( 1000 )
57
+ end
58
+ rand( 3 )
59
+ end
60
+ btest.name = 'Ruby-Kernel.rand'
61
+ tests << btest
62
+
63
+ btest = BlockTestFactory.new do |run,random|
64
+ 10_000.times do
65
+ random.next_int( 1000 )
66
+ end
67
+ random.next_int( 3 )
68
+ end
69
+ btest.name = 'Ruby-FAST'
70
+ tests << btest
71
+
72
+ tests
73
+ when 'htmap'
74
+ HTMapPerfTest::TEST_CLASSES.map do |cls|
75
+ HTMapPerfTest.new( cls )
76
+ end
77
+ when 'buffer-writer'
78
+ ResizableCharBufferWriterPerfTest::TEST_CLASSES.map do |cls|
79
+ ResizableCharBufferWriterPerfTest.new( cls )
80
+ end
81
+ when 'buffer-instream'
82
+ tests = ByteBufferInputStreamPerfTest::TEST_CLASSES.map do |cls|
83
+ ByteBufferInputStreamPerfTest.new( cls )
84
+ end
85
+ tests += ByteBufferInputStreamPerfTest::TEST_CLASSES.map do |cls|
86
+ t = ByteBufferInputStreamPerfTest.new( cls )
87
+ t.read_one = true
88
+ t
89
+ end
90
+ when 'empty'
91
+ btest = BlockTestFactory.new { 1 }
92
+ btest.name = 'RubyEmptyBlock'
93
+ [ EmptyPerfTest.new, REmptyTestFactory.new, btest ]
94
+ when 'sort'
95
+ btest = BlockTestFactory.new do
96
+ numbers = []
97
+ ( rand( 10000 ) + 10 ).times do
98
+ numbers << rand( 10000 )
99
+ end
100
+ numbers.sort!
101
+ numbers.first
102
+ end
103
+ btest.name = 'RubySort'
104
+ [ SortPerfTest.new, btest ]
105
+ end
106
+ end
107
+
108
+ class REmptyTestFactory
109
+ include Gravitext::Concurrent
110
+ include TestFactory
111
+ include TestRunnable
112
+
113
+ def name
114
+ 'RubyEmpty'
115
+ end
116
+
117
+ def create_test_runnable( seed )
118
+ self.class.new
119
+ end
120
+
121
+ def run_iteration( run )
122
+ 1
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ TestCollection.run
@@ -0,0 +1,29 @@
1
+ #--
2
+ # Copyright (c) 2007-2010 David Kellum
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
5
+ # may not use this file except in compliance with the License. You
6
+ # may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13
+ # implied. See the License for the specific language governing
14
+ # permissions and limitations under the License.
15
+ #++
16
+
17
+ require 'gravitext-util/version'
18
+ require 'java'
19
+
20
+ module Gravitext
21
+ module Util
22
+ require File.join( LIB_DIR, "gravitext-util-#{ VERSION }.jar" )
23
+
24
+ import 'com.gravitext.util.FastRandom'
25
+ end
26
+
27
+ require 'gravitext-util/concurrent'
28
+ require 'gravitext-util/unimap'
29
+ end
@@ -0,0 +1,110 @@
1
+ #--
2
+ # Copyright (c) 2007-2010 David Kellum
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
5
+ # may not use this file except in compliance with the License. You
6
+ # may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13
+ # implied. See the License for the specific language governing
14
+ # permissions and limitations under the License.
15
+ #++
16
+
17
+ require 'java'
18
+ require 'gravitext-util'
19
+
20
+ module Gravitext
21
+
22
+ # Provides a concurrent testing facility
23
+ module Concurrent
24
+ import 'com.gravitext.concurrent.TestExecutor'
25
+ import 'com.gravitext.concurrent.TestFactory'
26
+ import 'com.gravitext.concurrent.TestRunnable'
27
+
28
+ # Returns the number of available processor cores on the host.
29
+ def self.available_cores
30
+ Java::java.lang.Runtime::runtime.available_processors
31
+ end
32
+
33
+ # Run TestRunnable instances created from (TestFactory)
34
+ # test_factory concurrently with threads (one TestRunnable
35
+ # per thread.). The first of any Exceptions raised in a test
36
+ # thread will be re-raised in the calling thread. Returns sum of
37
+ # runIteration return counts.
38
+ def self.execute_test_factory( test_factory, runs,
39
+ threads = available_cores )
40
+ TestExecutor::run( test_factory, runs, threads )
41
+ end
42
+
43
+ # Run test_runnable_class instances concurrently in threads. The
44
+ # test_runnable_class should take a instance of FastRandom in its
45
+ # initialize(). The first of any Exceptions raised in a test
46
+ # thread will be re-raised in the calling thread. Returns sum of
47
+ # runIteration return counts.
48
+ def self.execute_runnable( test_runnable_class, runs,
49
+ threads = available_cores )
50
+ TestExecutor::run( BasicTestFactory.new( test_runnable_class ),
51
+ runs, threads )
52
+ end
53
+
54
+ # Run block concurrently in the specified number of threads. The
55
+ # first of any Exceptions raised in block will be re-raised in the
56
+ # calling thread. Returns sum of runIteration return counts.
57
+ #
58
+ # :call-seq:
59
+ # execute_test(runs,threads = available_cores) { |run,random| ... } -> Integer
60
+ def self.execute_test( runs, threads = available_cores, &block )
61
+ TestExecutor::run( BlockTestFactory.new( block ), runs, threads )
62
+ end
63
+
64
+ class BlockTestFactory
65
+ include TestFactory
66
+
67
+ attr_accessor :name
68
+
69
+ def initialize( proc = nil, &block )
70
+ @name = 'BlockTestFactory'
71
+ @block = proc || block
72
+ end
73
+
74
+ def create_test_runnable( seed )
75
+ BlockTestRunnable.new( seed, @block )
76
+ end
77
+ end
78
+
79
+ class BlockTestRunnable
80
+ include TestRunnable
81
+
82
+ def initialize( seed, block )
83
+ @block = block
84
+ @random = Gravitext::Util::FastRandom.new( seed )
85
+ end
86
+
87
+ def run_iteration( run )
88
+ @block.call( run, @random )
89
+ end
90
+ end
91
+
92
+ class BasicTestFactory
93
+ include TestFactory
94
+
95
+ def initialize( test_class )
96
+ @test_class = test_class
97
+ end
98
+
99
+ def name
100
+ @test_class.name
101
+ end
102
+
103
+ def create_test_runnable( seed )
104
+ @test_class.new( Gravitext::Util::FastRandom.new( seed ) )
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ end
@@ -0,0 +1,408 @@
1
+ #--
2
+ # Copyright (c) 2007-2010 David Kellum
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
5
+ # may not use this file except in compliance with the License. You
6
+ # may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13
+ # implied. See the License for the specific language governing
14
+ # permissions and limitations under the License.
15
+ #++
16
+
17
+ require 'gravitext-util'
18
+
19
+ require 'ostruct'
20
+ require 'java'
21
+
22
+ module Gravitext
23
+
24
+ # Concurrent performance testing facility
25
+ module PerfTest
26
+
27
+ module CalcUtil #:nodoc: all
28
+
29
+ NaN = Java::java.lang.Double::NaN
30
+
31
+ def throughput_change( exec, prior = nil )
32
+ if prior
33
+ p = prior.mean_throughput
34
+ ( exec.mean_throughput - p ) / p
35
+ else
36
+ NaN
37
+ end
38
+ end
39
+
40
+ def latency_change( exec, prior = nil )
41
+ if prior
42
+ p = prior.mean_latency.seconds
43
+ ( exec.mean_latency.seconds - p ) / p
44
+ else
45
+ NaN
46
+ end
47
+ end
48
+ end
49
+
50
+ # Concurrent performance testing harness with support for adaptive
51
+ # warmup and comparison on multiple TestFactory instances.
52
+ class Harness
53
+ include CalcUtil
54
+ import 'com.gravitext.concurrent.TestExecutor'
55
+ import 'com.gravitext.util.Duration'
56
+
57
+ # Number of threads to test with (default: available cores)
58
+ attr_accessor :thread_count
59
+
60
+ # Target duration of warmup comparison iterations in seconds (default:8s)
61
+ attr_accessor :warmup_exec_target
62
+
63
+ # Minimum warmup time in seconds for each test factory (default: 25s)
64
+ attr_accessor :warmup_total_target
65
+
66
+ # Secondary warmup requirement: consectutive iteration
67
+ # throughputs within tollerance (default: 0.05)
68
+ attr_accessor :warmup_tolerance
69
+
70
+ # Number of final comparison iterations (default: 3)
71
+ attr_accessor :final_iterations
72
+
73
+ # Number of runs in final comparisons (default: computed from
74
+ # warmup and final_exec_target)
75
+ attr_accessor :final_runs
76
+
77
+ # Target duration of final comparison iterations in seconds (default: 10s)
78
+ attr_accessor :final_exec_target
79
+
80
+ # Test executation progress is sent to listener, see PrintListener.
81
+ attr_accessor :listener
82
+
83
+ # Do per run timing for mean latency. (default: true)
84
+ attr_accessor :do_per_run_timing
85
+
86
+ # Maximum difference in counts for final run counts to be
87
+ # alligned (default: 3.0)
88
+ attr_accessor :max_align_ratio
89
+
90
+ # Initialize given array of com.gravitext.concurrent.TestFactory
91
+ # instances.
92
+ def initialize( factories )
93
+ @factories = factories
94
+ @thread_count = Concurrent::available_cores
95
+
96
+ @warmup_exec_target = 8.0
97
+ @warmup_total_target = 25
98
+ @warmup_tolerance = 0.05
99
+
100
+ @final_iterations = 3
101
+ @final_runs = nil
102
+ @final_exec_target = 10.0
103
+
104
+ @max_align_ratio = 3.0
105
+ @do_per_run_timing = true
106
+
107
+ @listener = PrintListener.new
108
+ end
109
+
110
+ def execute
111
+ @listener.begin( self, @factories )
112
+
113
+ finals = warmup
114
+
115
+ run_counts = if @final_runs
116
+ Array.new( @factories.size, @final_runs )
117
+ else
118
+ counts = @factories.map do |factory|
119
+ exec = finals.detect { |e| e.factory == factory }
120
+ ( exec.mean_throughput * @final_exec_target ).to_i
121
+ end
122
+ align_counts( counts )
123
+ end
124
+
125
+ results = execute_comparisons( run_counts, @final_iterations )
126
+ sums = sum_results( results ) if @final_iterations > 1
127
+
128
+ @listener.comparisons_end( self, sums )
129
+ sums
130
+ end
131
+
132
+ def warmup
133
+
134
+ @listener.warmups_begin( self )
135
+
136
+ states = @factories.map do |factory|
137
+ s = OpenStruct.new
138
+ s.factory = factory
139
+ s.prior = nil
140
+ s.warm_time = 0.0
141
+ s
142
+ end
143
+
144
+ first = true
145
+ finals = []
146
+ until states.empty? do
147
+
148
+ @listener.warmup_next_series( self ) unless first
149
+
150
+ states,done = states.partition do |s|
151
+
152
+ # Cleanup before each run
153
+ Java::java.lang.System::gc
154
+ Java::java.lang.Thread::yield
155
+
156
+ runs = if s.prior
157
+ ( @warmup_exec_target * s.prior.runs_executed ) /
158
+ s.prior.duration.seconds
159
+ else
160
+ 1
161
+ end
162
+
163
+ executor = create_executor( s.factory, runs.to_i )
164
+ @listener.warmup_start_run( executor )
165
+ executor.run_test
166
+ @listener.warmup_complete_run( executor, s.prior )
167
+
168
+ # Test throughput change, and increment warm_time
169
+ s.warm_time += executor.duration.seconds
170
+ tchange = throughput_change( executor, s.prior )
171
+ s.prior = executor
172
+
173
+ ( ( s.warm_time < @warmup_total_target ) ||
174
+ ( tchange.abs > @warmup_tolerance ) )
175
+ end
176
+ finals += done.map { |s| s.prior }
177
+ first = false
178
+ end
179
+
180
+ @listener.warmups_end( finals )
181
+ finals
182
+ end
183
+
184
+ def align_counts( counts )
185
+ mean = ( counts.inject { |sum,c| sum + c } ) / counts.size
186
+
187
+ # Round to 2-significant digits
188
+ f = 1
189
+ ( f *= 10 ) while ( mean / f ) > 100
190
+ mean = ( mean.to_f / f ).round * f
191
+
192
+ if ( mean.to_f / counts.min ) > @max_align_ratio
193
+ counts
194
+ else
195
+ Array.new( counts.size, mean )
196
+ end
197
+ end
198
+
199
+ def execute_comparisons( run_counts, iterations )
200
+
201
+ results = Array.new( @factories.size ) { [] }
202
+
203
+ @listener.comparisons_begin( self, run_counts )
204
+
205
+ iterations.times do |iteration|
206
+ @listener.comparison_next_series( self ) unless iteration.zero?
207
+
208
+ Java::java.lang.System::gc
209
+ Java::java.lang.Thread::yield
210
+
211
+ @factories.each_index do |f|
212
+ executor = create_executor( @factories[f], run_counts[f] )
213
+ @listener.comparison_start_run( executor )
214
+ executor.run_test
215
+ @listener.comparison_complete_run( executor, results[0].last )
216
+ results[f] << executor
217
+ end
218
+ end
219
+
220
+ results
221
+ end
222
+
223
+ def sum_results( results )
224
+ sums = []
225
+
226
+ results.each do |runs|
227
+ sum = OpenStruct.new
228
+ sum.factory = runs.first.factory
229
+ sum.runs_target = 0
230
+ sum.duration = 0.0
231
+ sum.result_sum = 0
232
+ sum.runs_executed = 0
233
+ sum.mean_throughput = 0.0
234
+ sum.mean_latency = 0.0
235
+
236
+ runs.each do |exec|
237
+ sum.runs_target += exec.runs_target
238
+ sum.duration += exec.duration.seconds
239
+ sum.result_sum += exec.result_sum
240
+ sum.runs_executed += exec.runs_executed
241
+ sum.mean_throughput += exec.mean_throughput
242
+ sum.mean_latency += exec.mean_latency.seconds
243
+ end
244
+
245
+ sum.duration = Duration.new( sum.duration )
246
+ sum.mean_throughput /= runs.size
247
+ sum.mean_latency = Duration.new( sum.mean_latency / runs.size )
248
+
249
+ sums << sum
250
+ end
251
+
252
+ sums
253
+ end
254
+
255
+ def create_executor( factory, runs )
256
+ executor = TestExecutor.new( factory, runs, @thread_count )
257
+ executor.do_per_run_timing = @do_per_run_timing
258
+ executor
259
+ end
260
+
261
+ end
262
+
263
+ # Listen for various events from the Harness and print results to console
264
+ class PrintListener
265
+ include CalcUtil
266
+ import 'com.gravitext.util.Metric'
267
+
268
+ # Status is written via out << (default $stdout)
269
+ def initialize( out = $stdout )
270
+ @out = out
271
+ end
272
+
273
+ def begin( harness, factories )
274
+ @out << "Concurrent testing: #{harness.thread_count} threads."
275
+ new_line
276
+ @nwidth = ( factories.map { |f| f.name.length } << 4 ).max
277
+ end
278
+
279
+ def warmups_begin( harness )
280
+ @out << ( "Warmup min %gs (change tolerance: %g) per test:" %
281
+ [ harness.warmup_total_target, harness.warmup_tolerance ] )
282
+ new_line
283
+ print_header
284
+ end
285
+
286
+ def warmup_start_run( executor )
287
+ print_result_start( executor )
288
+ end
289
+
290
+ def warmup_complete_run( executor, prior )
291
+ print_result( executor, prior )
292
+ end
293
+
294
+ def warmup_next_series( harness )
295
+ print_separator
296
+ end
297
+
298
+ def warmups_end( final_executors )
299
+ new_line
300
+ end
301
+
302
+ def comparisons_begin( harness, run_counts )
303
+ @out << ( "Comparison runs (%d iterations):" %
304
+ [ harness.final_iterations ] )
305
+ new_line
306
+ print_header
307
+ end
308
+
309
+ def comparison_next_series( harness )
310
+ print_separator
311
+ end
312
+
313
+ def comparison_start_run( executor )
314
+ print_result_start( executor )
315
+ end
316
+
317
+ def comparison_complete_run( executor, prior )
318
+ print_result( executor, prior )
319
+ end
320
+
321
+ def comparisons_end( harness, executor_sums )
322
+ print_separator( '=' )
323
+ executor_sums.each_index do |s|
324
+ print_result_start( executor_sums[s] )
325
+ print_result( executor_sums[s], ( executor_sums.first unless s.zero? ) )
326
+ end
327
+ end
328
+
329
+ def new_line
330
+ @out << "\n"
331
+ end
332
+
333
+ def print_header
334
+ @out << ( "%-#{@nwidth}s %-6s %-7s %-6s %8s %10s(%6s) %-9s (%6s)" %
335
+ [ "Test",
336
+ "Count",
337
+ "Time",
338
+ "R Sum",
339
+ "~R Value",
340
+ "Throughput",
341
+ "Change",
342
+ "~Latency",
343
+ "Change" ] )
344
+ new_line
345
+ print_separator( '=' )
346
+ end
347
+
348
+ def print_separator( char = '-' )
349
+ @out << ( char * ( @nwidth + 69 ) )
350
+ new_line
351
+ end
352
+
353
+ def print_result_start( exec, out = @out )
354
+ out << ( "%-#{@nwidth}s %6s " %
355
+ [ exec.factory.name,
356
+ Metric::format( exec.runs_target ) ] )
357
+ end
358
+
359
+ def print_result( exec, prior = nil, out = @out )
360
+ out << ( "%7s %6s %6s/r %6sr/s (%6s) %7s/r (%6s)" %
361
+ [ exec.duration,
362
+ Metric::format( exec.result_sum.to_f ),
363
+ Metric::format( exec.result_sum.to_f /
364
+ exec.runs_executed ),
365
+ Metric::format( exec.mean_throughput ),
366
+ Metric::format_difference( throughput_change(exec,prior) ),
367
+ exec.mean_latency,
368
+ Metric::format_difference( latency_change(exec,prior) ) ] )
369
+ new_line
370
+ end
371
+
372
+ end
373
+
374
+ # Derivation of PrintListener for consise debug log output
375
+ class LogListener < PrintListener
376
+
377
+ # Send <<() to log.debug
378
+ class LogWriter
379
+ def initialize( log )
380
+ @log = log
381
+ end
382
+ def <<( msg )
383
+ @log.debug( msg )
384
+ end
385
+ end
386
+
387
+ def initialize( logger )
388
+ super( LogWriter.new( logger ) )
389
+ end
390
+
391
+ alias :orig_result_start :print_result_start
392
+ def print_result_start( exec ); end
393
+
394
+ # Print run start and result output on single log line
395
+ def print_result( exec, prior = nil )
396
+ line = ""
397
+ orig_result_start( exec, line )
398
+ super( exec, prior, line )
399
+ @out << line
400
+ end
401
+
402
+ def new_line; end
403
+ def print_separator( char = '-' ); end
404
+
405
+ end
406
+
407
+ end
408
+ end