railsbench 0.8.4

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.
@@ -0,0 +1,123 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/perf_utils.rb")
2
+ require "set"
3
+
4
+ # Entry Format:
5
+ #
6
+ # Garbage collection started
7
+ # objects processed: 0223696
8
+ # live objects : 0192126
9
+ # freelist objects : 0000000
10
+ # freed objects : 0031570
11
+ # kept 0000370 / freed 0000609 objects of type OBJECT
12
+ # kept 0001071 / freed 0000062 objects of type CLASS
13
+ # kept 0000243 / freed 0000061 objects of type ICLASS
14
+ # kept 0000041 / freed 0000061 objects of type FLOAT
15
+ # kept 0013974 / freed 0015432 objects of type STRING
16
+ # kept 0000651 / freed 0000002 objects of type REGEXP
17
+ # kept 0000617 / freed 0009948 objects of type ARRAY
18
+ # kept 0000646 / freed 0001398 objects of type HASH
19
+ # kept 0000004 / freed 0000121 objects of type BIGNUM
20
+ # kept 0000006 / freed 0000005 objects of type FILE
21
+ # kept 0000400 / freed 0000253 objects of type DATA
22
+ # kept 0000001 / freed 0000093 objects of type MATCH
23
+ # kept 0000067 / freed 0000136 objects of type VARMAP
24
+ # kept 0000167 / freed 0000939 objects of type SCOPE
25
+ # kept 0173634 / freed 0002389 objects of type NODE
26
+ # GC time: 47 msec
27
+
28
+ GCAttributes = [:processed, :live, :freelist, :freed, :time]
29
+ GCSummaries = [:min, :max, :mean, :stddev, :stddev_percentage]
30
+
31
+ class GCLogEntry
32
+ attr_accessor *GCAttributes
33
+ attr_accessor :live_objects, :freed_objects
34
+ def initialize
35
+ @live_objects = {}
36
+ @freed_objects = {}
37
+ end
38
+ end
39
+
40
+ class GCInfo
41
+
42
+ attr_reader(*GCAttributes)
43
+ attr_reader :entries, :num_requests, :collections, :garbage_produced, :time_total, :topology
44
+ attr_reader :live_objects, :freed_objects, :object_types
45
+
46
+ GCAttributes.each do |attr|
47
+ GCSummaries.each do |method|
48
+ attr_reader "#{attr}_#{method}"
49
+ end
50
+ end
51
+
52
+ def initialize(file)
53
+ @entries = []
54
+ @num_requests = 0
55
+ @topology = []
56
+ @object_types = Set.new
57
+
58
+ file.each_line do |line|
59
+ case line
60
+ when /^Garbage collection started$/
61
+ @entries << GCLogEntry.new
62
+ when /^objects processed\s*:\s*(\d+)$/
63
+ @entries.last.processed = $1.to_i
64
+ when /^live objects\s*:\s*(\d+)$/
65
+ @entries.last.live = $1.to_i
66
+ when /^freelist objects\s*:\s*(\d+)$/
67
+ @entries.last.freelist = $1.to_i
68
+ when /^freed objects\s*:\s*(\d+)$/
69
+ @entries.last.freed = $1.to_i
70
+ when /^GC time\s*:\s*(\d+)\s*msec$/
71
+ @entries.last.time = $1.to_i
72
+ when /^number of requests processed: (\d+)$/
73
+ @num_requests = $1.to_i
74
+ when /^HEAP\[\s*(\d+)\]: size=\s*(\d+)$/
75
+ @topology << $2.to_i
76
+ when /^kept (\d+) \/ freed (\d+) objects of type ([a-zA-Z]+)/
77
+ @object_types.add($3)
78
+ @entries.last.live_objects[$3] = $1.to_i
79
+ @entries.last.freed_objects[$3] = $2.to_i
80
+ end
81
+ end
82
+
83
+ @time_total = @entries.map{|e| e.time}.sum
84
+ @collections = @entries.length
85
+ @garbage_produced = @entries.map{|e| e.freed}.sum
86
+ @live_objects = @entries.map{|e| e.live_objects}
87
+ @freed_objects = @entries.map{|e| e.freed_objects}
88
+
89
+ GCAttributes.each do |attr|
90
+ a = @entries.map{|e| e.send attr}
91
+ a.pop
92
+
93
+ [:min, :max, :mean].each do |method|
94
+ instance_variable_set "@#{attr}_#{method}", (a.send method)
95
+ end
96
+ mean = instance_variable_get "@#{attr}_mean"
97
+ stddev = instance_variable_set "@#{attr}_stddev", (a.send :stddev, mean)
98
+ instance_variable_set "@#{attr}_stddev_percentage", stddev_percentage(stddev, mean)
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+
105
+ ### Local Variables: ***
106
+ ### mode:ruby ***
107
+ ### End: ***
108
+
109
+ # Copyright (C) 2006 Stefan Kaes
110
+ #
111
+ # This program is free software; you can redistribute it and/or modify
112
+ # it under the terms of the GNU General Public License as published by
113
+ # the Free Software Foundation; either version 2 of the License, or
114
+ # (at your option) any later version.
115
+ #
116
+ # This program is distributed in the hope that it will be useful,
117
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
118
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
119
+ # GNU General Public License for more details.
120
+ #
121
+ # You should have received a copy of the GNU General Public License
122
+ # along with this program; if not, write to the Free Software
123
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,145 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/perf_utils.rb")
2
+
3
+ # example of raw performance data
4
+
5
+ # /home/skaes/railsbench/script/perf_bench 100 -bm=all -mysql_session -patched_gc -links -OT
6
+ # user system total real
7
+ # loading environment 0.954000 1.938000 2.892000 ( 2.890000)
8
+ # /empty/index 0.093000 0.000000 0.093000 ( 0.172000)
9
+ # /welcome/index 0.156000 0.000000 0.156000 ( 0.172000)
10
+ # /rezept/index 0.125000 0.015000 0.140000 ( 0.203000)
11
+ # /rezept/myknzlpzl 0.125000 0.000000 0.125000 ( 0.203000)
12
+ # /rezept/show/413 0.406000 0.094000 0.500000 ( 0.594000)
13
+ # /rezept/cat/Hauptspeise 0.547000 0.094000 0.641000 ( 0.688000)
14
+ # /rezept/cat/Hauptspeise?page=5 0.531000 0.047000 0.578000 ( 0.688000)
15
+ # /rezept/letter/G 0.422000 0.078000 0.500000 ( 0.609000)
16
+ # GC.collections=0, GC.time=0.0
17
+ # user system total real
18
+ # loading environment 0.813000 2.078000 2.891000 ( 2.890000)
19
+ # /empty/index 0.125000 0.016000 0.141000 ( 0.157000)
20
+ # /welcome/index 0.109000 0.000000 0.109000 ( 0.187000)
21
+ # /rezept/index 0.110000 0.031000 0.141000 ( 0.219000)
22
+ # /rezept/myknzlpzl 0.109000 0.016000 0.125000 ( 0.219000)
23
+ # /rezept/show/413 0.422000 0.078000 0.500000 ( 0.625000)
24
+ # /rezept/cat/Hauptspeise 0.437000 0.125000 0.562000 ( 0.656000)
25
+ # /rezept/cat/Hauptspeise?page=5 0.453000 0.125000 0.578000 ( 0.688000)
26
+ # /rezept/letter/G 0.438000 0.000000 0.438000 ( 0.594000)
27
+ # GC.collections=0, GC.time=0.0
28
+ # user system total real
29
+ # loading environment 0.938000 1.968000 2.906000 ( 2.906000)
30
+ # /empty/index 0.109000 0.000000 0.109000 ( 0.172000)
31
+ # /welcome/index 0.094000 0.031000 0.125000 ( 0.171000)
32
+ # /rezept/index 0.110000 0.047000 0.157000 ( 0.219000)
33
+ # /rezept/myknzlpzl 0.140000 0.016000 0.156000 ( 0.203000)
34
+ # /rezept/show/413 0.422000 0.047000 0.469000 ( 0.593000)
35
+ # /rezept/cat/Hauptspeise 0.515000 0.015000 0.530000 ( 0.672000)
36
+ # /rezept/cat/Hauptspeise?page=5 0.484000 0.063000 0.547000 ( 0.672000)
37
+ # /rezept/letter/G 0.453000 0.015000 0.468000 ( 0.610000)
38
+ # GC.collections=0, GC.time=0.0
39
+
40
+
41
+ PerfAttributes = [:gc_calls, :gc_time, :load_time, :total_time]
42
+ PerfSummaries = [:min, :max, :mean, :stddev, :stddev_percentage]
43
+
44
+ class PerfEntry
45
+ attr_accessor *PerfAttributes
46
+ attr_accessor :keys, :timings
47
+ def initialize
48
+ @keys = []
49
+ @timings = {}
50
+ end
51
+ end
52
+
53
+ class PerfInfo
54
+
55
+ attr_reader :options, :iterations, :keys
56
+ attr_reader :entries, :runs, :request_count, :requests_per_key
57
+
58
+ def gc_stats?
59
+ @gc_stats
60
+ end
61
+
62
+ PerfSummaries.each do |method|
63
+ PerfAttributes.each do |attr|
64
+ attr_reader "#{attr}_#{method}"
65
+ end
66
+ class_eval "def timings_#{method}(key); @timings[:#{method}][key]; end"
67
+ end
68
+
69
+ def initialize(file)
70
+ @entries = []
71
+ file.each_line do |line|
72
+ case line
73
+ when /^.*perf_([a-zA-Z.]+)\s+(\d+)\s+(.*)$/
74
+ @iterations = $2.to_i
75
+ @options = $3
76
+ when /\s+user\s+system\s+total\s+real/
77
+ @entries << PerfEntry.new
78
+ when /^(.*)\s+([\d\.]+)\s+([\d\.]+)\s+([\d\.]+)\s+\(\s*([\d\.]+)\s*\)$/
79
+ key, time = $1.strip, $5.to_f
80
+ if key == "loading environment"
81
+ @entries.last.load_time = time
82
+ else
83
+ @entries.last.keys << key
84
+ @entries.last.timings[key] = time
85
+ end
86
+ when /^GC.collections=(\d+), GC.time=([\d\.]+)$/
87
+ @entries.last.gc_calls, @entries.last.gc_time = [$1.to_i,$2.to_f]
88
+ @gc_stats = true
89
+ end
90
+ end
91
+
92
+ @entries.each{ |e| e.total_time = e.timings.values.sum }
93
+ @keys = @entries.first.keys
94
+ @runs = @entries.length
95
+ if @keys.length == 1 && @keys[0] =~ /\((\d+) urls\)$/
96
+ @requests_per_key = $1.to_i
97
+ else
98
+ @requests_per_key = 1
99
+ end
100
+ @request_count = @iterations * @keys.length * @requests_per_key
101
+ @timings = PerfSummaries.inject({}){ |hash, method| hash[method] = Hash.new; hash }
102
+
103
+ @keys.each do |k|
104
+ a = @entries.map{|e| e.timings[k]}
105
+ [:min, :max, :mean].each do |method|
106
+ @timings[method][k] = a.send(method)
107
+ end
108
+ mean = @timings[:mean][k]
109
+ stddev = @timings[:stddev][k] = a.send(:stddev, mean)
110
+ @timings[:stddev_percentage][k] = stddev_percentage(stddev, mean)
111
+ end
112
+
113
+ PerfAttributes.each do |attr|
114
+ a = @entries.map{|e| e.send attr}
115
+ [:min, :max, :mean].each do |method|
116
+ instance_variable_set "@#{attr}_#{method}", (a.send method)
117
+ end
118
+ mean = instance_variable_get "@#{attr}_mean"
119
+ stddev = instance_variable_set "@#{attr}_stddev", (a.send :stddev, mean)
120
+ instance_variable_set "@#{attr}_stddev_percentage", stddev_percentage(stddev, mean)
121
+ end
122
+
123
+ end
124
+ end
125
+
126
+
127
+ ### Local Variables: ***
128
+ ### mode:ruby ***
129
+ ### End: ***
130
+
131
+ # Copyright (C) 2006 Stefan Kaes
132
+ #
133
+ # This program is free software; you can redistribute it and/or modify
134
+ # it under the terms of the GNU General Public License as published by
135
+ # the Free Software Foundation; either version 2 of the License, or
136
+ # (at your option) any later version.
137
+ #
138
+ # This program is distributed in the hope that it will be useful,
139
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
140
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
141
+ # GNU General Public License for more details.
142
+ #
143
+ # You should have received a copy of the GNU General Public License
144
+ # along with this program; if not, write to the Free Software
145
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,65 @@
1
+ # some utility methods
2
+
3
+ class Array
4
+ def index_map
5
+ res = {}
6
+ each_with_index{|element, index| res[index] = element}
7
+ res
8
+ end
9
+
10
+ def restrict_to(index_set)
11
+ res = []
12
+ each_with_index{|e,i| res << e if index_set.include?(i)}
13
+ res
14
+ end
15
+
16
+ def sum
17
+ inject(0.0){|r,v| r += v }
18
+ end
19
+
20
+ def mean
21
+ sum/length
22
+ end
23
+
24
+ def stddev(mean=nil)
25
+ mean ||= self.mean
26
+ r = inject(0.0){|r,v| r += (v-mean)*(v-mean) }
27
+ Math.sqrt(r/(length-1))
28
+ end
29
+ end
30
+
31
+ def stddev_percentage(stddev, mean)
32
+ stddev.zero? ? 0.0 : (stddev/mean)*100
33
+ end
34
+
35
+ def die(msg, error_code=1)
36
+ $stderr.puts msg
37
+ exit error_code
38
+ end
39
+
40
+ class File
41
+ def self.open_or_die(filename, &block)
42
+ filename = filename.sub(/^\/([cdefgh])(\/)/, '\1:\2') if RUBY_PLATFORM =~ /win32/
43
+ begin
44
+ if stat(filename).readable?
45
+ open(filename, &block)
46
+ else
47
+ die "file #{filename} is unreadable"
48
+ end
49
+ rescue
50
+ die "file #{filename} does not exist"
51
+ end
52
+ end
53
+ end
54
+
55
+ def truncate(text, length = 32, truncate_string = "...")
56
+ if text.nil? then return "" end
57
+ l = truncate_string.length + 1
58
+
59
+ if $KCODE == "NONE"
60
+ text.length > length ? text[0..(length - l)] + truncate_string : text
61
+ else
62
+ chars = text.split(//)
63
+ chars.length > length ? chars[0..(length - l)].join + truncate_string : text
64
+ end
65
+ end
@@ -0,0 +1,397 @@
1
+ class RailsBenchmark
2
+
3
+ attr_accessor :gc_frequency, :iterations, :url_spec
4
+ attr_accessor :http_host, :remote_addr, :server_port
5
+ attr_accessor :relative_url_root
6
+ attr_accessor :perform_caching, :cache_template_loading
7
+ attr_accessor :session_data
8
+
9
+ def error_exit(msg)
10
+ STDERR.puts msg
11
+ raise msg
12
+ end
13
+
14
+ def patched_gc?
15
+ @patched_gc
16
+ end
17
+
18
+ def relative_url_root=(value)
19
+ ActionController::AbstractRequest.relative_url_root = value
20
+ @relative_url_root = value
21
+ end
22
+
23
+ def initialize(options={})
24
+ unless @gc_frequency = options[:gc_frequency]
25
+ @gc_frequency = 0
26
+ ARGV.each{|arg| @gc_frequency = $1.to_i if arg =~ /-gc(\d+)/ }
27
+ end
28
+
29
+ @iterations = (options[:iterations] || 100).to_i
30
+
31
+ @remote_addr = options[:remote_addr] || '127.0.0.1'
32
+ @http_host = options[:http_host] || '127.0.0.1'
33
+ @server_port = options[:server_port] || '80'
34
+
35
+ @session_data = options[:session_data] || {}
36
+
37
+ @url_spec = options[:url_spec]
38
+
39
+ ENV['RAILS_ENV'] = 'benchmarking'
40
+
41
+ require ENV['RAILS_ROOT'] + "/config/environment"
42
+ require 'dispatcher' # make edge rails happy
43
+
44
+ # we don't want local error template output, which crashes anyway
45
+ ActionController::Rescue.class_eval "def local_request?; false; end"
46
+
47
+ # make sure an error code gets returned for 1.1.6
48
+ ActionController::Rescue.class_eval <<-"end_eval"
49
+ def rescue_action_in_public(exception)
50
+ case exception
51
+ when ActionController::RoutingError, ActionController::UnknownAction
52
+ render_text(IO.read(File.join(RAILS_ROOT, 'public', '404.html')), "404 Not Found")
53
+ else
54
+ render_text(IO.read(File.join(RAILS_ROOT, 'public', '500.html')), "500 Internal Error")
55
+ end
56
+ end
57
+ end_eval
58
+
59
+ if ARGV.include?('-path')
60
+ $:.each{|f| STDERR.puts f}
61
+ exit
62
+ end
63
+
64
+ log_level = options[:log]
65
+ log_level = Logger::DEBUG if ARGV.include?('-log')
66
+ ARGV.each{|arg| arg =~ /-log=([a-zA-Z]*)/ && (log_level = eval("Logger::#{$1.upcase}")) }
67
+
68
+ if log_level
69
+ RAILS_DEFAULT_LOGGER.level = log_level
70
+ #ActiveRecord::Base.logger.level = log_level
71
+ #ActionController::Base.logger.level = log_level
72
+ #ActionMailer::Base.logger = level = log_level if defined?(ActionMailer)
73
+ else
74
+ ActiveRecord::Base.logger = nil
75
+ ActionController::Base.logger = nil
76
+ ActionMailer::Base.logger = nil if defined?(ActionMailer)
77
+ end
78
+
79
+ if options.has_key?(:perform_caching)
80
+ ActionController::Base.perform_caching = options[:perform_caching]
81
+ else
82
+ ActionController::Base.perform_caching = false if ARGV.include?('-nocache')
83
+ ActionController::Base.perform_caching = true if ARGV.include?('-cache')
84
+ end
85
+
86
+ if options.has_key?(:cache_template_loading)
87
+ ActionView::Base.cache_template_loading = options[:cache_template_loading]
88
+ else
89
+ ActionView::Base.cache_template_loading = true
90
+ end
91
+
92
+ self.relative_url_root = options[:relative_url_root] || ''
93
+
94
+ @patched_gc = GC.collections.is_a?(Numeric) rescue false
95
+
96
+ if ARGV.include? '-headers_only'
97
+ require File.dirname(__FILE__) + '/write_headers_only'
98
+ end
99
+
100
+ end
101
+
102
+ def establish_test_session
103
+ session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.stringify_keys
104
+ session_options = session_options.merge('new_session' => true)
105
+ @session = CGI::Session.new(Hash.new, session_options)
106
+ @session_data.each{ |k,v| @session[k] = v }
107
+ @session.update
108
+ @session_id = @session.session_id
109
+ end
110
+
111
+ def delete_test_session
112
+ @session.delete
113
+ @session = nil
114
+ end
115
+
116
+ # can be redefined in subclasses to clean out test sessions
117
+ def delete_new_test_sessions
118
+ end
119
+
120
+ def setup_test_urls(name)
121
+ raise "There is no benchmark named '#{name}'" unless @url_spec[name]
122
+ @urls = self.class.parse_url_spec(@url_spec, name)
123
+ end
124
+
125
+ def setup_initial_env
126
+ ENV['REMOTE_ADDR'] = remote_addr
127
+ ENV['HTTP_HOST'] = http_host
128
+ ENV['SERVER_PORT'] = server_port.to_s
129
+ ENV['REQUEST_METHOD'] = 'GET'
130
+ end
131
+
132
+ def setup_request_env(uri, query_string, new_session)
133
+ ENV['REQUEST_URI'] = @relative_url_root + uri
134
+ ENV['QUERY_STRING'] = query_string || ''
135
+ ENV['CONTENT_LENGTH'] = (query_string || '').length.to_s
136
+ ENV['HTTP_COOKIE'] = new_session ? '' : "_session_id=#{@session_id}"
137
+ end
138
+
139
+ def warmup
140
+ error_exit "No urls given for performance test" unless @urls && @urls.size>0
141
+ setup_initial_env
142
+ @urls.each do |entry|
143
+ error_exit "No uri given for benchmark entry: #{entry.inspect}" unless entry['uri']
144
+ setup_request_env(entry['uri'], entry['query_string'], entry['new_session'])
145
+ Dispatcher.dispatch
146
+ end
147
+ end
148
+
149
+ def run_urls_without_benchmark(gc_stats)
150
+ # support for running Ruby Performance Validator
151
+ # or Ruby Memory Validator
152
+ svl = nil
153
+ begin
154
+ if ARGV.include?('-svlPV')
155
+ require 'svlRubyPV'
156
+ svl = SvlRubyPV.new
157
+ elsif ARGV.include?('-svlMV')
158
+ require 'svlRubyMV'
159
+ svl = SvlRubyMV.new
160
+ end
161
+ rescue LoadError
162
+ # SVL dll not available, do nothing
163
+ end
164
+
165
+ # support ruby-prof
166
+ ruby_prof = nil
167
+ ARGV.each{|arg| ruby_prof=$1 if arg =~ /-ruby_prof=(\d*\.?\d*)/ }
168
+ begin
169
+ if ruby_prof
170
+ require 'ruby-prof'
171
+ RubyProf.clock_mode = RubyProf::WALL_TIME
172
+ RubyProf.start
173
+ end
174
+ rescue LoadError
175
+ # ruby-prof not available, do nothing
176
+ end
177
+
178
+ # start profiler and trigger data collection if required
179
+ if svl
180
+ svl.startProfiler
181
+ svl.startDataCollection
182
+ end
183
+
184
+ setup_initial_env
185
+ GC.enable_stats if gc_stats
186
+ if gc_frequency==0
187
+ run_urls_without_benchmark_and_without_gc_control(@urls, iterations)
188
+ else
189
+ run_urls_without_benchmark_but_with_gc_control(@urls, iterations, gc_frequency)
190
+ end
191
+ if gc_stats
192
+ GC.enable if gc_frequency
193
+ GC.start
194
+ GC.dump
195
+ GC.disable_stats
196
+ GC.log "number of requests processed: #{@urls.size * iterations}"
197
+ end
198
+
199
+ # stop data collection if necessary
200
+ svl.stopDataCollection if svl
201
+
202
+ if defined? RubyProf
203
+ result = RubyProf.stop
204
+ # Print a flat profile to text
205
+ printer = RubyProf::GraphHtmlPrinter.new(result)
206
+ printer.print(STDERR, ruby_prof.to_f)
207
+ end
208
+
209
+ delete_test_session
210
+ delete_new_test_sessions
211
+ end
212
+
213
+ def run_urls(test)
214
+ setup_initial_env
215
+ if gc_frequency>0
216
+ run_urls_with_gc_control(test, @urls, iterations, gc_frequency)
217
+ else
218
+ run_urls_without_gc_control(test, @urls, iterations)
219
+ end
220
+ delete_test_session
221
+ delete_new_test_sessions
222
+ end
223
+
224
+ def run_url_mix(test)
225
+ if gc_frequency>0
226
+ run_url_mix_with_gc_control(test, @urls, iterations, gc_frequency)
227
+ else
228
+ run_url_mix_without_gc_control(test, @urls, iterations)
229
+ end
230
+ delete_test_session
231
+ delete_new_test_sessions
232
+ end
233
+
234
+ private
235
+
236
+ def run_urls_without_benchmark_but_with_gc_control(urls, n, gc_frequency)
237
+ urls.each do |entry|
238
+ setup_request_env(entry['uri'], entry['query_string'], entry['new_session'])
239
+ GC.enable; GC.start; GC.disable
240
+ request_count = 0
241
+ n.times do
242
+ Dispatcher.dispatch
243
+ if (request_count += 1) == gc_frequency
244
+ GC.enable; GC.start; GC.disable
245
+ request_count = 0
246
+ end
247
+ end
248
+ end
249
+ end
250
+
251
+ def run_urls_without_benchmark_and_without_gc_control(urls, n)
252
+ urls.each do |entry|
253
+ setup_request_env(entry['uri'], entry['query_string'], entry['new_session'])
254
+ n.times do
255
+ Dispatcher.dispatch
256
+ end
257
+ end
258
+ end
259
+
260
+ def run_urls_with_gc_control(test, urls, n, gc_freq)
261
+ gc_stats = patched_gc?
262
+ GC.clear_stats if gc_stats
263
+ urls.each do |entry|
264
+ request_count = 0
265
+ setup_request_env(entry['uri'], entry['query_string'], entry['new_session'])
266
+ test.report(entry['uri']) do
267
+ GC.disable_stats if gc_stats
268
+ GC.enable; GC.start; GC.disable
269
+ GC.enable_stats if gc_stats
270
+ n.times do
271
+ Dispatcher.dispatch
272
+ if (request_count += 1) == gc_freq
273
+ GC.enable; GC.start; GC.disable
274
+ request_count = 0
275
+ end
276
+ end
277
+ end
278
+ end
279
+ if gc_stats
280
+ GC.disable_stats
281
+ Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
282
+ GC.clear_stats
283
+ end
284
+ end
285
+
286
+ def run_urls_without_gc_control(test, urls, n)
287
+ gc_stats = patched_gc?
288
+ GC.clear_stats if gc_stats
289
+ urls.each do |entry|
290
+ setup_request_env(entry['uri'], entry['query_string'], entry['new_session'])
291
+ GC.disable_stats if gc_stats
292
+ GC.start
293
+ GC.enable_stats if gc_stats
294
+ test.report(entry['uri']) do
295
+ n.times do
296
+ Dispatcher.dispatch
297
+ end
298
+ end
299
+ end
300
+ if gc_stats
301
+ GC.disable_stats
302
+ Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
303
+ GC.clear_stats
304
+ end
305
+ end
306
+
307
+ def run_url_mix_without_gc_control(test, urls, n)
308
+ gc_stats = patched_gc?
309
+ GC.start
310
+ if gc_stats
311
+ GC.clear_stats; GC.enable_stats
312
+ end
313
+ test.report("url_mix (#{urls.length} urls)") do
314
+ n.times do
315
+ urls.each do |entry|
316
+ setup_request_env(entry['uri'], entry['query_string'], entry['new_session'])
317
+ Dispatcher.dispatch
318
+ end
319
+ end
320
+ end
321
+ if gc_stats
322
+ GC.disable_stats
323
+ Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
324
+ GC.clear_stats
325
+ end
326
+ end
327
+
328
+ def run_url_mix_with_gc_control(test, urls, n, gc_frequency)
329
+ gc_stats = patched_gc?
330
+ GC.enable; GC.start; GC.disable
331
+ if gc_stats
332
+ GC.clear_stats; GC.enable_stats
333
+ end
334
+ test.report("url_mix (#{urls.length} urls)") do
335
+ request_count = 0
336
+ n.times do
337
+ urls.each do |entry|
338
+ setup_request_env(entry['uri'], entry['query_string'], entry['new_session'])
339
+ Dispatcher.dispatch
340
+ if (request_count += 1) == gc_frequency
341
+ GC.enable; GC.start; GC.disable
342
+ request_count = 0
343
+ end
344
+ end
345
+ end
346
+ end
347
+ if gc_stats
348
+ GC.disable_stats
349
+ Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
350
+ GC.clear_stats
351
+ end
352
+ end
353
+
354
+ def self.parse_url_spec(url_spec, name)
355
+ res = url_spec[name]
356
+ if res.is_a?(String)
357
+ res = res.split(/, */).collect!{ |n| parse_url_spec(url_spec, n) }.flatten
358
+ elsif res.is_a?(Hash)
359
+ res = [ res ]
360
+ end
361
+ res
362
+ end
363
+
364
+ end
365
+
366
+
367
+ class RailsBenchmarkWithActiveRecordStore < RailsBenchmark
368
+
369
+ def initialize(options={})
370
+ super(options)
371
+ @session_class = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager].session_class rescue CGI::Session::ActiveRecordStore
372
+ end
373
+
374
+ def delete_new_test_sessions
375
+ @session_class.delete_all if @session_class.respond_to?(:delete_all)
376
+ end
377
+
378
+ end
379
+
380
+
381
+ __END__
382
+
383
+ # Copyright (C) 2005, 2006 Stefan Kaes
384
+ #
385
+ # This program is free software; you can redistribute it and/or modify
386
+ # it under the terms of the GNU General Public License as published by
387
+ # the Free Software Foundation; either version 2 of the License, or
388
+ # (at your option) any later version.
389
+ #
390
+ # This program is distributed in the hope that it will be useful,
391
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
392
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
393
+ # GNU General Public License for more details.
394
+ #
395
+ # You should have received a copy of the GNU General Public License
396
+ # along with this program; if not, write to the Free Software
397
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA