kamal-railsbench 0.9.9.pre
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.
- data/BUGS +2 -0
- data/CHANGELOG +2124 -0
- data/GCPATCH +73 -0
- data/INSTALL +75 -0
- data/LICENSE +222 -0
- data/Manifest.txt +53 -0
- data/PROBLEMS +56 -0
- data/README +337 -0
- data/Rakefile +51 -0
- data/bin/railsbench +80 -0
- data/config/benchmarking.rb +21 -0
- data/config/benchmarks.rb +21 -0
- data/config/benchmarks.yml +2 -0
- data/images/empty.png +0 -0
- data/images/minus.png +0 -0
- data/images/plus.png +0 -0
- data/install.rb +70 -0
- data/latest_changes.txt +18 -0
- data/lib/benchmark.rb +576 -0
- data/lib/railsbench/benchmark.rb +576 -0
- data/lib/railsbench/benchmark_specs.rb +63 -0
- data/lib/railsbench/gc_info.rb +158 -0
- data/lib/railsbench/perf_info.rb +146 -0
- data/lib/railsbench/perf_utils.rb +202 -0
- data/lib/railsbench/railsbenchmark.rb +640 -0
- data/lib/railsbench/version.rb +9 -0
- data/lib/railsbench/write_headers_only.rb +15 -0
- data/postinstall.rb +12 -0
- data/ruby184gc.patch +516 -0
- data/ruby185gc.patch +562 -0
- data/ruby186gc.patch +564 -0
- data/ruby19gc.patch +2425 -0
- data/script/convert_raw_data_files +49 -0
- data/script/generate_benchmarks +171 -0
- data/script/perf_bench +74 -0
- data/script/perf_comp +151 -0
- data/script/perf_comp_gc +113 -0
- data/script/perf_diff +48 -0
- data/script/perf_diff_gc +53 -0
- data/script/perf_html +103 -0
- data/script/perf_plot +225 -0
- data/script/perf_plot_gc +254 -0
- data/script/perf_prof +87 -0
- data/script/perf_run +39 -0
- data/script/perf_run_gc +40 -0
- data/script/perf_table +104 -0
- data/script/perf_tex +58 -0
- data/script/perf_times +66 -0
- data/script/perf_times_gc +94 -0
- data/script/run_urls +57 -0
- data/setup.rb +1585 -0
- data/test/railsbench_test.rb +11 -0
- data/test/test_helper.rb +2 -0
- metadata +133 -0
@@ -0,0 +1,640 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/benchmark_specs')
|
2
|
+
|
3
|
+
class RailsBenchmark
|
4
|
+
|
5
|
+
attr_accessor :gc_frequency, :iterations
|
6
|
+
attr_accessor :http_host, :remote_addr, :server_port
|
7
|
+
attr_accessor :relative_url_root
|
8
|
+
attr_accessor :perform_caching, :cache_template_loading
|
9
|
+
attr_accessor :session_data, :session_key, :cookie_data
|
10
|
+
|
11
|
+
def error_exit(msg)
|
12
|
+
STDERR.puts msg
|
13
|
+
raise msg
|
14
|
+
end
|
15
|
+
|
16
|
+
def patched_gc?
|
17
|
+
@patched_gc
|
18
|
+
end
|
19
|
+
|
20
|
+
def relative_url_root=(value)
|
21
|
+
if ActionController::Base.respond_to?(:relative_url_root=)
|
22
|
+
if @rails_version < "3"
|
23
|
+
# rails 2.3
|
24
|
+
ActionController::Base.relative_url_root = value
|
25
|
+
else
|
26
|
+
::Rails.application.config.relative_url_root = value
|
27
|
+
end
|
28
|
+
else
|
29
|
+
# earlier railses
|
30
|
+
ActionController::AbstractRequest.relative_url_root = value
|
31
|
+
end
|
32
|
+
@relative_url_root = value
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(options={})
|
36
|
+
unless @gc_frequency = options[:gc_frequency]
|
37
|
+
@gc_frequency = 0
|
38
|
+
ARGV.each{|arg| @gc_frequency = $1.to_i if arg =~ /-gc(\d+)/ }
|
39
|
+
end
|
40
|
+
|
41
|
+
@iterations = (options[:iterations] || 100).to_i
|
42
|
+
|
43
|
+
@remote_addr = options[:remote_addr] || '127.0.0.1'
|
44
|
+
@http_host = options[:http_host] || '127.0.0.1'
|
45
|
+
@server_port = options[:server_port] || '80'
|
46
|
+
|
47
|
+
@session_data = options[:session_data] || {}
|
48
|
+
@session_key = options[:session_key] || '_session_id'
|
49
|
+
|
50
|
+
ENV['RAILS_ENV'] = 'benchmarking'
|
51
|
+
|
52
|
+
begin
|
53
|
+
require ENV['RAILS_ROOT'] + "/config/environment"
|
54
|
+
@rails_version = Rails::VERSION::STRING
|
55
|
+
require 'dispatcher' if @rails_version < "3" # make edge rails happy
|
56
|
+
|
57
|
+
if @rails_version >= "2.3"
|
58
|
+
@rack_middleware = true
|
59
|
+
if @rails_version < "3"
|
60
|
+
require 'cgi/session'
|
61
|
+
CGI.class_eval <<-"end_eval"
|
62
|
+
def env_table
|
63
|
+
@env_table ||= ENV.to_hash
|
64
|
+
end
|
65
|
+
end_eval
|
66
|
+
end
|
67
|
+
else
|
68
|
+
@rack_middleware = false
|
69
|
+
end
|
70
|
+
|
71
|
+
rescue => e
|
72
|
+
$stderr.puts "failed to load application environment"
|
73
|
+
e.backtrace.each{|line| $stderr.puts line}
|
74
|
+
$stderr.puts "benchmarking aborted"
|
75
|
+
exit(-1)
|
76
|
+
end
|
77
|
+
|
78
|
+
# we don't want local error template output, which crashes anyway, when run under railsbench
|
79
|
+
ActionController::Rescue.class_eval "def local_request?; false; end" if @rails_version < "3"
|
80
|
+
|
81
|
+
# print backtrace and exit if action execution raises an exception
|
82
|
+
ActionController::Rescue.class_eval <<-"end_eval" if @rails_version < "3"
|
83
|
+
def rescue_action_in_public(exception)
|
84
|
+
$stderr.puts "benchmarking aborted due to application error: " + exception.message
|
85
|
+
exception.backtrace.each{|line| $stderr.puts line}
|
86
|
+
$stderr.print "clearing database connections ..."
|
87
|
+
if defined?(ActiveRecord)
|
88
|
+
ActiveRecord::Base.send :clear_all_cached_connections! if ActiveRecord::Base.respond_to?(:clear_all_cached_connections)
|
89
|
+
ActiveRecord::Base.clear_all_connections! if ActiveRecord::Base.respond_to?(:clear_all_connections)
|
90
|
+
end
|
91
|
+
$stderr.puts
|
92
|
+
exit!(-1)
|
93
|
+
end
|
94
|
+
end_eval
|
95
|
+
|
96
|
+
# override rails ActiveRecord::Base#inspect to make profiles more readable
|
97
|
+
if defined?(ActiveRecord)
|
98
|
+
ActiveRecord::Base.class_eval <<-"end_eval"
|
99
|
+
def self.inspect
|
100
|
+
super
|
101
|
+
end
|
102
|
+
end_eval
|
103
|
+
end
|
104
|
+
|
105
|
+
# make sure Rails doesn't try to read post data from stdin
|
106
|
+
CGI::QueryExtension.module_eval <<-end_eval if @rails_version < "3"
|
107
|
+
def read_body(content_length)
|
108
|
+
ENV['RAW_POST_DATA']
|
109
|
+
end
|
110
|
+
end_eval
|
111
|
+
|
112
|
+
if ARGV.include?('-path')
|
113
|
+
$:.each{|f| STDERR.puts f}
|
114
|
+
exit
|
115
|
+
end
|
116
|
+
|
117
|
+
rails_logger = @rails_version > "3" ? Rails.logger : RAILS_DEFAULT_LOGGER
|
118
|
+
|
119
|
+
logger_module = Logger
|
120
|
+
if defined?(Log4r) && rails_logger.is_a?(Log4r::Logger)
|
121
|
+
logger_module = Logger
|
122
|
+
end
|
123
|
+
default_log_level = logger_module.const_get("ERROR")
|
124
|
+
log_level = options[:log] || default_log_level
|
125
|
+
ARGV.each do |arg|
|
126
|
+
case arg
|
127
|
+
when '-log'
|
128
|
+
log_level = default_log_level
|
129
|
+
when '-log=(nil|none)'
|
130
|
+
log_level = nil
|
131
|
+
when /-log=([a-zA-Z]*)/
|
132
|
+
log_level = logger_module.const_get($1.upcase) rescue default_log_level
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
if log_level
|
137
|
+
rails_logger.level = log_level
|
138
|
+
ActiveRecord::Base.logger.level = log_level if defined?(ActiveRecord)
|
139
|
+
ActionController::Base.logger.level = log_level
|
140
|
+
ActionMailer::Base.logger.level = log_level if defined?(ActionMailer)
|
141
|
+
else
|
142
|
+
rails_logger.level = logger_module.const_get "FATAL"
|
143
|
+
ActiveRecord::Base.logger = nil if defined?(ActiveRecord)
|
144
|
+
ActionController::Base.logger = nil
|
145
|
+
ActionMailer::Base.logger = nil if defined?(ActionMailer)
|
146
|
+
end
|
147
|
+
|
148
|
+
if options.has_key?(:perform_caching)
|
149
|
+
ActionController::Base.perform_caching = options[:perform_caching]
|
150
|
+
else
|
151
|
+
ActionController::Base.perform_caching = false if ARGV.include?('-nocache')
|
152
|
+
ActionController::Base.perform_caching = true if ARGV.include?('-cache')
|
153
|
+
end
|
154
|
+
|
155
|
+
if ActionView::Base.respond_to?(:cache_template_loading)
|
156
|
+
if options.has_key?(:cache_template_loading)
|
157
|
+
ActionView::Base.cache_template_loading = options[:cache_template_loading]
|
158
|
+
else
|
159
|
+
ActionView::Base.cache_template_loading = true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
self.relative_url_root = options[:relative_url_root] || ''
|
164
|
+
|
165
|
+
@patched_gc = GC.collections.is_a?(Numeric) rescue false
|
166
|
+
|
167
|
+
if ARGV.include? '-headers_only'
|
168
|
+
require File.dirname(__FILE__) + '/write_headers_only'
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
def establish_test_session
|
174
|
+
if @rack_middleware
|
175
|
+
session_options = ActionController::Base.session_options
|
176
|
+
@session_id = ActiveSupport::SecureRandom.hex(16)
|
177
|
+
do_not_do_much = lambda do |env|
|
178
|
+
env["rack.session"] = @session_data
|
179
|
+
env["rack.session.options"] = {:id => @session_id}
|
180
|
+
[200, {}, ""]
|
181
|
+
end
|
182
|
+
@session_store = ActionController::Base.session_store.new(do_not_do_much, session_options)
|
183
|
+
@session_store.call({})
|
184
|
+
else
|
185
|
+
session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.stringify_keys
|
186
|
+
session_options = session_options.merge('new_session' => true)
|
187
|
+
@session = CGI::Session.new(Hash.new, session_options)
|
188
|
+
@session_data.each{ |k,v| @session[k] = v }
|
189
|
+
@session.update
|
190
|
+
@session_id = @session.session_id
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def update_test_session_data(session_data)
|
195
|
+
if @rack_middleware
|
196
|
+
session_options = ActionController::Base.session_options
|
197
|
+
merge_url_specific_session_data = lambda do |env|
|
198
|
+
old_session_data = env["rack.session"]
|
199
|
+
# $stderr.puts "data in old session: #{old_session_data.inspect}"
|
200
|
+
new_session_data = old_session_data.merge(session_data || {})
|
201
|
+
# $stderr.puts "data in new session: #{new_session_data.inspect}"
|
202
|
+
env["rack.session"] = new_session_data
|
203
|
+
[200, {}, ""]
|
204
|
+
end
|
205
|
+
@session_store.instance_eval { @app = merge_url_specific_session_data }
|
206
|
+
env = {}
|
207
|
+
env["HTTP_COOKIE"] = cookie
|
208
|
+
# debugger
|
209
|
+
@session_store.call(env)
|
210
|
+
else
|
211
|
+
dbman = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager]
|
212
|
+
old_session_data = dbman.new(@session).restore
|
213
|
+
# $stderr.puts old_session_data.inspect
|
214
|
+
new_session_data = old_session_data.merge(session_data || {})
|
215
|
+
new_session_data.each{ |k,v| @session[k] = v }
|
216
|
+
@session.update
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def delete_test_session
|
221
|
+
# no way to delete a session by going through the session adpater in rails 2.3
|
222
|
+
if @session
|
223
|
+
@session.delete
|
224
|
+
@session = nil
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# can be redefined in subclasses to clean out test sessions
|
229
|
+
def delete_new_test_sessions
|
230
|
+
end
|
231
|
+
|
232
|
+
def setup_test_urls(name)
|
233
|
+
@benchmark = name
|
234
|
+
@urls = BenchmarkSpec.load(name)
|
235
|
+
end
|
236
|
+
|
237
|
+
def setup_initial_env
|
238
|
+
ENV['REMOTE_ADDR'] = remote_addr
|
239
|
+
ENV['HTTP_HOST'] = http_host
|
240
|
+
ENV['SERVER_PORT'] = server_port.to_s
|
241
|
+
end
|
242
|
+
|
243
|
+
def setup_request_env(entry)
|
244
|
+
# $stderr.puts entry.inspect
|
245
|
+
ENV['REQUEST_URI'] = @relative_url_root + entry.uri
|
246
|
+
ENV.delete 'RAW_POST_DATA'
|
247
|
+
ENV.delete 'QUERY_STRING'
|
248
|
+
case ENV['REQUEST_METHOD'] = (entry.method || 'get').upcase
|
249
|
+
when 'GET'
|
250
|
+
query_data = entry.query_string || ''
|
251
|
+
query_data = escape_data(query_data) unless entry.raw_data
|
252
|
+
ENV['QUERY_STRING'] = query_data
|
253
|
+
when 'POST'
|
254
|
+
query_data = entry.post_data || ''
|
255
|
+
query_data = escape_data(query_data) unless entry.raw_data
|
256
|
+
ENV['RAW_POST_DATA'] = query_data
|
257
|
+
end
|
258
|
+
ENV['CONTENT_LENGTH'] = query_data.length.to_s
|
259
|
+
ENV['HTTP_COOKIE'] = entry.new_session ? '' : cookie
|
260
|
+
ENV['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' if entry.xhr
|
261
|
+
# $stderr.puts entry.session_data.inspect
|
262
|
+
update_test_session_data(entry.session_data) unless entry.new_session
|
263
|
+
end
|
264
|
+
|
265
|
+
def before_dispatch_hook(entry)
|
266
|
+
end
|
267
|
+
|
268
|
+
def cookie
|
269
|
+
"#{@session_key}=#{@session_id}#{cookie_data}"
|
270
|
+
end
|
271
|
+
|
272
|
+
def escape_data(str)
|
273
|
+
str.split('&').map{|e| e.split('=').map{|e| CGI::escape e}.join('=')}.join('&')
|
274
|
+
end
|
275
|
+
|
276
|
+
def warmup
|
277
|
+
error_exit "No urls given for performance test" unless @urls && @urls.size>0
|
278
|
+
setup_initial_env
|
279
|
+
@urls.each do |entry|
|
280
|
+
error_exit "No uri given for benchmark entry: #{entry.inspect}" unless entry.uri
|
281
|
+
setup_request_env(entry)
|
282
|
+
dispatch(entry)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def run_urls_without_benchmark(gc_stats)
|
287
|
+
# support for running Ruby Performance Validator
|
288
|
+
# or Ruby Memory Validator
|
289
|
+
svl = nil
|
290
|
+
begin
|
291
|
+
if ARGV.include?('-svlPV')
|
292
|
+
require 'svlRubyPV'
|
293
|
+
svl = SvlRubyPV.new
|
294
|
+
elsif ARGV.include?('-svlMV')
|
295
|
+
require 'svlRubyMV'
|
296
|
+
svl = SvlRubyMV
|
297
|
+
end
|
298
|
+
rescue LoadError
|
299
|
+
# SVL dll not available, do nothing
|
300
|
+
end
|
301
|
+
|
302
|
+
# support ruby-prof
|
303
|
+
ruby_prof = nil
|
304
|
+
ARGV.each{|arg| ruby_prof=$1 if arg =~ /-ruby_prof=([^ ]*)/ }
|
305
|
+
begin
|
306
|
+
if ruby_prof
|
307
|
+
# redirect stderr (TODO: I can't remember why we don't do this later)
|
308
|
+
if benchmark_file = ENV['RAILS_BENCHMARK_FILE']
|
309
|
+
$stderr = File.open(benchmark_file, "w")
|
310
|
+
end
|
311
|
+
require 'ruby-prof'
|
312
|
+
measure_mode = "WALL_TIME"
|
313
|
+
ARGV.each{|arg| measure_mode=$1.upcase if arg =~ /-measure_mode=([^ ]*)/ }
|
314
|
+
if %w(PROCESS_TIME WALL_TIME CPU_TIME ALLOCATIONS MEMORY).include?(measure_mode)
|
315
|
+
RubyProf.measure_mode = RubyProf.const_get measure_mode
|
316
|
+
else
|
317
|
+
$stderr = STDERR
|
318
|
+
$stderr.puts "unsupported ruby_prof measure mode: #{measure_mode}"
|
319
|
+
exit(-1)
|
320
|
+
end
|
321
|
+
RubyProf.start
|
322
|
+
end
|
323
|
+
rescue LoadError
|
324
|
+
# ruby-prof not available, do nothing
|
325
|
+
$stderr = STDERR
|
326
|
+
$stderr.puts "ruby-prof not available: giving up"
|
327
|
+
exit(-1)
|
328
|
+
end
|
329
|
+
|
330
|
+
# start profiler and trigger data collection if required
|
331
|
+
if svl
|
332
|
+
svl.startProfiler
|
333
|
+
svl.startDataCollection
|
334
|
+
end
|
335
|
+
|
336
|
+
setup_initial_env
|
337
|
+
GC.enable_stats if gc_stats
|
338
|
+
if gc_frequency==0
|
339
|
+
run_urls_without_benchmark_and_without_gc_control(@urls, iterations)
|
340
|
+
else
|
341
|
+
run_urls_without_benchmark_but_with_gc_control(@urls, iterations, gc_frequency)
|
342
|
+
end
|
343
|
+
if gc_stats
|
344
|
+
GC.enable if gc_frequency
|
345
|
+
GC.start
|
346
|
+
GC.dump
|
347
|
+
GC.disable_stats
|
348
|
+
GC.log "number of requests processed: #{@urls.size * iterations}"
|
349
|
+
end
|
350
|
+
|
351
|
+
# try to detect Ruby interpreter memory leaks (OS X)
|
352
|
+
if ARGV.include?('-leaks')
|
353
|
+
leaks_log = "#{ENV['RAILS_PERF_DATA']}/leaks.log"
|
354
|
+
leaks_command = "leaks -nocontext #{$$} >#{leaks_log}"
|
355
|
+
ENV.delete 'MallocStackLogging'
|
356
|
+
# $stderr.puts "executing '#{leaks_command}'"
|
357
|
+
raise "could not execute leaks command" unless system(leaks_command)
|
358
|
+
mallocs, leaks = *`head -n 2 #{leaks_log}`.split("\n").map{|l| l.gsub(/Process #{$$}: /, '')}
|
359
|
+
if mem_leaks = (leaks =~ /(\d+) leaks for (\d+) total leaked bytes/)
|
360
|
+
$stderr.puts "\n!!!!! memory leaks detected !!!!! (#{leaks_log})"
|
361
|
+
$stderr.puts "=" * leaks.length
|
362
|
+
end
|
363
|
+
if gc_stats
|
364
|
+
GC.log mallocs
|
365
|
+
GC.log leaks
|
366
|
+
end
|
367
|
+
$stderr.puts mallocs, leaks
|
368
|
+
$stderr.puts "=" * leaks.length if mem_leaks
|
369
|
+
end
|
370
|
+
|
371
|
+
# stop data collection if necessary
|
372
|
+
svl.stopDataCollection if svl
|
373
|
+
|
374
|
+
if defined? RubyProf
|
375
|
+
GC.disable #ruby-pof 0.7.x crash workaround
|
376
|
+
result = RubyProf.stop
|
377
|
+
GC.enable #ruby-pof 0.7.x crash workaround
|
378
|
+
min_percent = ruby_prof.split('/')[0].to_f rescue 0.1
|
379
|
+
threshold = ruby_prof.split('/')[1].to_f rescue 1.0
|
380
|
+
profile_type = nil
|
381
|
+
ARGV.each{|arg| profile_type=$1 if arg =~ /-profile_type=([^ ]*)/ }
|
382
|
+
profile_type ||= 'stack'
|
383
|
+
printer =
|
384
|
+
case profile_type
|
385
|
+
when 'stack' then RubyProf::CallStackPrinter
|
386
|
+
when 'grind' then RubyProf::CallTreePrinter
|
387
|
+
when 'flat' then RubyProf::FlatPrinter
|
388
|
+
when 'graph' then RubyProf::GraphHtmlPrinter
|
389
|
+
when 'multi' then RubyProf::MultiPrinter
|
390
|
+
else raise "unknown profile type: #{profile_type}"
|
391
|
+
end.new(result)
|
392
|
+
if profile_type == 'multi'
|
393
|
+
raise "you must specify a benchmark file when using multi printer" unless $stderr.is_a?(File)
|
394
|
+
$stderr.close
|
395
|
+
$stderr = STDERR
|
396
|
+
file_name = ENV['RAILS_BENCHMARK_FILE']
|
397
|
+
profile_name = File.basename(file_name).sub('.html','').sub(".#{profile_type}",'')
|
398
|
+
printer.print(:path => File.dirname(file_name),
|
399
|
+
:profile => profile_name,
|
400
|
+
:min_percent => min_percent, :threshold => threshold,
|
401
|
+
:title => "call tree/graph for benchmark #{@benchmark}")
|
402
|
+
else
|
403
|
+
printer.print($stderr, :min_percent => min_percent, :threshold => threshold,
|
404
|
+
:title => "call tree for benchmark #{@benchmark}")
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
delete_test_session
|
409
|
+
delete_new_test_sessions
|
410
|
+
end
|
411
|
+
|
412
|
+
def run_urls(test)
|
413
|
+
setup_initial_env
|
414
|
+
if gc_frequency>0
|
415
|
+
run_urls_with_gc_control(test, @urls, iterations, gc_frequency)
|
416
|
+
else
|
417
|
+
run_urls_without_gc_control(test, @urls, iterations)
|
418
|
+
end
|
419
|
+
delete_test_session
|
420
|
+
delete_new_test_sessions
|
421
|
+
end
|
422
|
+
|
423
|
+
def run_url_mix(test)
|
424
|
+
if gc_frequency>0
|
425
|
+
run_url_mix_with_gc_control(test, @urls, iterations, gc_frequency)
|
426
|
+
else
|
427
|
+
run_url_mix_without_gc_control(test, @urls, iterations)
|
428
|
+
end
|
429
|
+
delete_test_session
|
430
|
+
delete_new_test_sessions
|
431
|
+
end
|
432
|
+
|
433
|
+
private
|
434
|
+
|
435
|
+
def dispatch(entry)
|
436
|
+
before_dispatch_hook(entry)
|
437
|
+
if @rails_version < "3"
|
438
|
+
Dispatcher.dispatch(CGI.new)
|
439
|
+
else
|
440
|
+
status, headers, response = Rails.application.call(rack_request_env(entry))
|
441
|
+
body = response.body
|
442
|
+
begin
|
443
|
+
send_headers status, headers, $stdout
|
444
|
+
send_body body, $stdout
|
445
|
+
ensure
|
446
|
+
body.close if body.respond_to? :close
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def rack_request_env(entry)
|
452
|
+
env = Rack::MockRequest.env_for(ENV['REQUEST_URI'], :method => ENV['REQUEST_METHOD'])
|
453
|
+
if qs = ENV['QUERY_STRING']
|
454
|
+
env['QUERY_STRING'] = qs
|
455
|
+
env['CONTENT_LENGTH'] = ENV['CONTENT_LENGTH']
|
456
|
+
end
|
457
|
+
if rp = ENV['RAW_POST_DATA']
|
458
|
+
env['rack.input'] = StringIO.new(rp)
|
459
|
+
end
|
460
|
+
if entry.xhr
|
461
|
+
env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
462
|
+
end
|
463
|
+
if cs = ENV['HTTP_COOKIE']
|
464
|
+
env['HTTP_COOKIE'] = cs
|
465
|
+
end
|
466
|
+
env
|
467
|
+
end
|
468
|
+
|
469
|
+
def send_headers(status, headers, io)
|
470
|
+
io.print "Status: #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}\r\n"
|
471
|
+
headers.each do |k, vs|
|
472
|
+
vs.split("\n").each { |v| io.print "#{k}: #{v}\r\n" }
|
473
|
+
end
|
474
|
+
io.print "\r\n"
|
475
|
+
io.flush
|
476
|
+
end
|
477
|
+
|
478
|
+
def send_body(body, io)
|
479
|
+
if body.is_a?(String)
|
480
|
+
io.print body
|
481
|
+
io.flush
|
482
|
+
else
|
483
|
+
body.each do |part|
|
484
|
+
io.print part
|
485
|
+
io.flush
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def run_urls_without_benchmark_but_with_gc_control(urls, n, gc_frequency)
|
491
|
+
urls.each do |entry|
|
492
|
+
setup_request_env(entry)
|
493
|
+
GC.enable; GC.start; GC.disable
|
494
|
+
request_count = 0
|
495
|
+
n.times do
|
496
|
+
dispatch(entry)
|
497
|
+
if (request_count += 1) == gc_frequency
|
498
|
+
GC.enable; GC.start; GC.disable
|
499
|
+
request_count = 0
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
def run_urls_without_benchmark_and_without_gc_control(urls, n)
|
506
|
+
urls.each do |entry|
|
507
|
+
setup_request_env(entry)
|
508
|
+
n.times do
|
509
|
+
dispatch(entry)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def run_urls_with_gc_control(test, urls, n, gc_freq)
|
515
|
+
gc_stats = patched_gc?
|
516
|
+
GC.clear_stats if gc_stats
|
517
|
+
urls.each do |entry|
|
518
|
+
request_count = 0
|
519
|
+
setup_request_env(entry)
|
520
|
+
test.report(entry.name) do
|
521
|
+
GC.disable_stats if gc_stats
|
522
|
+
GC.enable; GC.start; GC.disable
|
523
|
+
GC.enable_stats if gc_stats
|
524
|
+
n.times do
|
525
|
+
dispatch(entry)
|
526
|
+
if (request_count += 1) == gc_freq
|
527
|
+
GC.enable; GC.start; GC.disable
|
528
|
+
request_count = 0
|
529
|
+
end
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
if gc_stats
|
534
|
+
GC.disable_stats
|
535
|
+
Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
|
536
|
+
GC.clear_stats
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
def run_urls_without_gc_control(test, urls, n)
|
541
|
+
gc_stats = patched_gc?
|
542
|
+
GC.clear_stats if gc_stats
|
543
|
+
urls.each do |entry|
|
544
|
+
setup_request_env(entry)
|
545
|
+
GC.disable_stats if gc_stats
|
546
|
+
GC.start
|
547
|
+
GC.enable_stats if gc_stats
|
548
|
+
test.report(entry.name) do
|
549
|
+
n.times do
|
550
|
+
dispatch(entry)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
if gc_stats
|
555
|
+
GC.disable_stats
|
556
|
+
Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
|
557
|
+
GC.clear_stats
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def run_url_mix_without_gc_control(test, urls, n)
|
562
|
+
gc_stats = patched_gc?
|
563
|
+
GC.start
|
564
|
+
if gc_stats
|
565
|
+
GC.clear_stats; GC.enable_stats
|
566
|
+
end
|
567
|
+
test.report("url_mix (#{urls.length} urls)") do
|
568
|
+
n.times do
|
569
|
+
urls.each do |entry|
|
570
|
+
setup_request_env(entry)
|
571
|
+
dispatch(entry)
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
if gc_stats
|
576
|
+
GC.disable_stats
|
577
|
+
Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
|
578
|
+
GC.clear_stats
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
def run_url_mix_with_gc_control(test, urls, n, gc_frequency)
|
583
|
+
gc_stats = patched_gc?
|
584
|
+
GC.enable; GC.start; GC.disable
|
585
|
+
if gc_stats
|
586
|
+
GC.clear_stats; GC.enable_stats
|
587
|
+
end
|
588
|
+
test.report("url_mix (#{urls.length} urls)") do
|
589
|
+
request_count = 0
|
590
|
+
n.times do
|
591
|
+
urls.each do |entry|
|
592
|
+
setup_request_env(entry)
|
593
|
+
dispatch(entry)
|
594
|
+
if (request_count += 1) == gc_frequency
|
595
|
+
GC.enable; GC.start; GC.disable
|
596
|
+
request_count = 0
|
597
|
+
end
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
601
|
+
if gc_stats
|
602
|
+
GC.disable_stats
|
603
|
+
Benchmark::OUTPUT.puts "GC.collections=#{GC.collections}, GC.time=#{GC.time/1E6}"
|
604
|
+
GC.clear_stats
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
|
610
|
+
class RailsBenchmarkWithActiveRecordStore < RailsBenchmark
|
611
|
+
|
612
|
+
def initialize(options={})
|
613
|
+
super(options)
|
614
|
+
@session_class = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager].session_class rescue CGI::Session::ActiveRecordStore rescue ActiveRecord::SessionStore
|
615
|
+
end
|
616
|
+
|
617
|
+
def delete_new_test_sessions
|
618
|
+
@session_class.delete_all if @session_class.respond_to?(:delete_all)
|
619
|
+
end
|
620
|
+
|
621
|
+
end
|
622
|
+
|
623
|
+
|
624
|
+
__END__
|
625
|
+
|
626
|
+
# Copyright (C) 2005-2008 Stefan Kaes
|
627
|
+
#
|
628
|
+
# This program is free software; you can redistribute it and/or modify
|
629
|
+
# it under the terms of the GNU General Public License as published by
|
630
|
+
# the Free Software Foundation; either version 2 of the License, or
|
631
|
+
# (at your option) any later version.
|
632
|
+
#
|
633
|
+
# This program is distributed in the hope that it will be useful,
|
634
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
635
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
636
|
+
# GNU General Public License for more details.
|
637
|
+
#
|
638
|
+
# You should have received a copy of the GNU General Public License
|
639
|
+
# along with this program; if not, write to the Free Software
|
640
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|