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.
Files changed (54) hide show
  1. data/BUGS +2 -0
  2. data/CHANGELOG +2124 -0
  3. data/GCPATCH +73 -0
  4. data/INSTALL +75 -0
  5. data/LICENSE +222 -0
  6. data/Manifest.txt +53 -0
  7. data/PROBLEMS +56 -0
  8. data/README +337 -0
  9. data/Rakefile +51 -0
  10. data/bin/railsbench +80 -0
  11. data/config/benchmarking.rb +21 -0
  12. data/config/benchmarks.rb +21 -0
  13. data/config/benchmarks.yml +2 -0
  14. data/images/empty.png +0 -0
  15. data/images/minus.png +0 -0
  16. data/images/plus.png +0 -0
  17. data/install.rb +70 -0
  18. data/latest_changes.txt +18 -0
  19. data/lib/benchmark.rb +576 -0
  20. data/lib/railsbench/benchmark.rb +576 -0
  21. data/lib/railsbench/benchmark_specs.rb +63 -0
  22. data/lib/railsbench/gc_info.rb +158 -0
  23. data/lib/railsbench/perf_info.rb +146 -0
  24. data/lib/railsbench/perf_utils.rb +202 -0
  25. data/lib/railsbench/railsbenchmark.rb +640 -0
  26. data/lib/railsbench/version.rb +9 -0
  27. data/lib/railsbench/write_headers_only.rb +15 -0
  28. data/postinstall.rb +12 -0
  29. data/ruby184gc.patch +516 -0
  30. data/ruby185gc.patch +562 -0
  31. data/ruby186gc.patch +564 -0
  32. data/ruby19gc.patch +2425 -0
  33. data/script/convert_raw_data_files +49 -0
  34. data/script/generate_benchmarks +171 -0
  35. data/script/perf_bench +74 -0
  36. data/script/perf_comp +151 -0
  37. data/script/perf_comp_gc +113 -0
  38. data/script/perf_diff +48 -0
  39. data/script/perf_diff_gc +53 -0
  40. data/script/perf_html +103 -0
  41. data/script/perf_plot +225 -0
  42. data/script/perf_plot_gc +254 -0
  43. data/script/perf_prof +87 -0
  44. data/script/perf_run +39 -0
  45. data/script/perf_run_gc +40 -0
  46. data/script/perf_table +104 -0
  47. data/script/perf_tex +58 -0
  48. data/script/perf_times +66 -0
  49. data/script/perf_times_gc +94 -0
  50. data/script/run_urls +57 -0
  51. data/setup.rb +1585 -0
  52. data/test/railsbench_test.rb +11 -0
  53. data/test/test_helper.rb +2 -0
  54. metadata +133 -0
@@ -0,0 +1,63 @@
1
+ require 'delegate'
2
+ require 'yaml'
3
+ require 'erb'
4
+
5
+ class BenchmarkSpec < DelegateClass(Hash)
6
+ attr_accessor :name
7
+
8
+ READERS = %w(uri method post_data query_string new_session action controller session_data xhr raw_data)
9
+ READERS.each do |method|
10
+ define_method(method) { self[method] }
11
+ end
12
+
13
+ def initialize(name, hash)
14
+ super(hash)
15
+ @name = name
16
+ end
17
+
18
+ def inspect
19
+ "BenchmarkSpec(#{name},#{super})"
20
+ end
21
+
22
+ class << self
23
+ def load(name, file_name = nil)
24
+ unless file_name
25
+ file_name = ENV['RAILS_ROOT'] + "/config/benchmarks.yml"
26
+ end
27
+ @@specs = YAML::load(ERB.new(IO.read(file_name)).result)
28
+ raise "There is no benchmark named '#{name}'" unless @@specs[name]
29
+ parse(@@specs, name)
30
+ end
31
+
32
+ def parse(specs, name)
33
+ spec = specs[name]
34
+ if spec.is_a?(String)
35
+ spec.split(/, */).collect!{ |n| parse(specs, n) }.flatten
36
+ elsif spec.is_a?(Hash)
37
+ [ BenchmarkSpec.new(name,spec) ]
38
+ elsif spec.is_a?(Array)
39
+ spec.collect{|n| parse(specs, n)}.flatten
40
+ else
41
+ raise "oops: unknown entry type in benchmark specification"
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ __END__
48
+
49
+ # Copyright (C) 2007, 2008 Stefan Kaes
50
+ #
51
+ # This program is free software; you can redistribute it and/or modify
52
+ # it under the terms of the GNU General Public License as published by
53
+ # the Free Software Foundation; either version 2 of the License, or
54
+ # (at your option) any later version.
55
+ #
56
+ # This program is distributed in the hope that it will be useful,
57
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
58
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59
+ # GNU General Public License for more details.
60
+ #
61
+ # You should have received a copy of the GNU General Public License
62
+ # along with this program; if not, write to the Free Software
63
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,158 @@
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
+ # Sentinel:
29
+ # HEAP[ 0]: size= 300000
30
+ # HEAP[ 1]: size= 600000
31
+ # ...
32
+ # number of requests processed: 1000
33
+ # 128334 nodes malloced for 17345 KB
34
+ # 396 leaks for 10464 total leaked bytes.
35
+
36
+
37
+ GCAttributes = [:processed, :live, :freelist, :freed, :time]
38
+ GCSummaries = [:min, :max, :mean, :stddev, :stddev_percentage]
39
+
40
+ class GCLogEntry
41
+ attr_accessor *GCAttributes
42
+ attr_accessor :live_objects, :freed_objects
43
+ def initialize
44
+ @live_objects = {}
45
+ @freed_objects = {}
46
+ end
47
+ end
48
+
49
+ class GCInfo
50
+
51
+ attr_reader(*GCAttributes)
52
+ attr_reader :entries, :num_requests, :collections, :garbage_produced, :time_total, :topology
53
+ attr_reader :live_objects, :freed_objects, :object_types, :garbage_totals
54
+ attr_reader :mallocs, :malloced, :leaks, :leaked
55
+
56
+ GCAttributes.each do |attr|
57
+ GCSummaries.each do |method|
58
+ attr_reader "#{attr}_#{method}"
59
+ end
60
+ end
61
+
62
+ def initialize(file)
63
+ @entries = []
64
+ @num_requests = 0
65
+ @topology = []
66
+ @object_types = Set.new
67
+ @mallocs = @malloced = @leaks = @leaked = 0
68
+
69
+ file.each_line do |line|
70
+ case line
71
+ when /^Garbage collection started$/
72
+ @entries << GCLogEntry.new
73
+ when /^objects processed\s*:\s*(\d+)$/
74
+ @entries.last.processed = $1.to_i
75
+ when /^live objects\s*:\s*(\d+)$/
76
+ @entries.last.live = $1.to_i
77
+ when /^freelist objects\s*:\s*(\d+)$/
78
+ @entries.last.freelist = $1.to_i
79
+ when /^freed objects\s*:\s*(\d+)$/
80
+ @entries.last.freed = $1.to_i
81
+ when /^GC time\s*:\s*(\d+)\s*msec$/
82
+ @entries.last.time = $1.to_i
83
+ when /^number of requests processed: (\d+)$/
84
+ @num_requests = $1.to_i
85
+ when /^HEAP\[\s*(\d+)\]: size=\s*(\d+)$/
86
+ @topology = [] if $1.to_i == 0
87
+ @topology << $2.to_i
88
+ when /^kept (\d+) \/ freed (\d+) objects of type ([a-zA-Z]+)/
89
+ @object_types.add($3)
90
+ @entries.last.live_objects[$3] = $1.to_i
91
+ @entries.last.freed_objects[$3] = $2.to_i
92
+ when /^(\d+) nodes malloced for (\d+) KB$/
93
+ @mallocs = $1.to_i
94
+ @malloced = $2.to_i * 1024
95
+ when /^(\d+) leaks for (\d+) total leaked bytes.$/
96
+ @leaks = $1.to_i
97
+ @leaked = $2.to_i
98
+ end
99
+ end
100
+
101
+ @time_total = @entries.map{|e| e.time}.sum
102
+ @collections = @entries.length
103
+ @garbage_produced = @entries.map{|e| e.freed}.sum
104
+ @live_objects = @entries.map{|e| e.live_objects}
105
+ @freed_objects = @entries.map{|e| e.freed_objects}
106
+ @freelist = @entries.map{|e| e.freelist}
107
+ @garbage_totals = @freed_objects.inject(Hash.new(0)) do |totals, freed|
108
+ freed.each do |object_type, count|
109
+ totals[object_type] += freed[object_type] || 0
110
+ end
111
+ totals
112
+ end
113
+
114
+ GCAttributes.each do |attr|
115
+ a = @entries.map{|e| e.send attr}
116
+ # we need to pop the last entry, as the freelist is not empty when doing the last forced GC
117
+ a.pop if :freelist == attr.to_sym
118
+
119
+ [:min, :max, :mean].each do |method|
120
+ instance_variable_set "@#{attr}_#{method}", (a.send method)
121
+ end
122
+ mean = instance_variable_get "@#{attr}_mean"
123
+ stddev = instance_variable_set "@#{attr}_stddev", (a.send :stddev, mean)
124
+ instance_variable_set "@#{attr}_stddev_percentage", stddev_percentage(stddev, mean)
125
+ end
126
+ end
127
+
128
+ OBJECT_TYPES = %w(NODE STRING ARRAY HASH SCOPE VARMAP CLASS ICLASS REGEXP FLOAT MATCH FILE DATA MODULE OBJECT)
129
+
130
+ class << self
131
+ def object_types(list_of_gc_infos)
132
+ list_of_gc_infos.inject(OBJECT_TYPES) do |object_types, gci|
133
+ (object_types + gci.object_types.to_a).uniq
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+
140
+ ### Local Variables: ***
141
+ ### mode:ruby ***
142
+ ### End: ***
143
+
144
+ # Copyright (C) 2006, 2007, 2008 Stefan Kaes
145
+ #
146
+ # This program is free software; you can redistribute it and/or modify
147
+ # it under the terms of the GNU General Public License as published by
148
+ # the Free Software Foundation; either version 2 of the License, or
149
+ # (at your option) any later version.
150
+ #
151
+ # This program is distributed in the hope that it will be useful,
152
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
153
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
154
+ # GNU General Public License for more details.
155
+ #
156
+ # You should have received a copy of the GNU General Public License
157
+ # along with this program; if not, write to the Free Software
158
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,146 @@
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
+ next if attr.to_s =~ /^gc_/ and !gc_stats?
115
+ a = @entries.map{|e| e.send attr}
116
+ [:min, :max, :mean].each do |method|
117
+ instance_variable_set "@#{attr}_#{method}", (a.send method)
118
+ end
119
+ mean = instance_variable_get "@#{attr}_mean"
120
+ stddev = instance_variable_set "@#{attr}_stddev", (a.send :stddev, mean)
121
+ instance_variable_set "@#{attr}_stddev_percentage", stddev_percentage(stddev, mean)
122
+ end
123
+
124
+ end
125
+ end
126
+
127
+
128
+ ### Local Variables: ***
129
+ ### mode:ruby ***
130
+ ### End: ***
131
+
132
+ # Copyright (C) 2006, 2007, 2008 Stefan Kaes
133
+ #
134
+ # This program is free software; you can redistribute it and/or modify
135
+ # it under the terms of the GNU General Public License as published by
136
+ # the Free Software Foundation; either version 2 of the License, or
137
+ # (at your option) any later version.
138
+ #
139
+ # This program is distributed in the hope that it will be useful,
140
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
141
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
142
+ # GNU General Public License for more details.
143
+ #
144
+ # You should have received a copy of the GNU General Public License
145
+ # along with this program; if not, write to the Free Software
146
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,202 @@
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 determine_rails_root_or_die!(msg=nil)
36
+ unless ENV['RAILS_ROOT']
37
+ if File.directory?("config") && File.exists?("config/environment.rb")
38
+ ENV['RAILS_ROOT'] = File.expand_path(".")
39
+ else
40
+ die(msg || "#{File.basename $PROGRAM_NAME}: $RAILS_ROOT not set and could not be configured automatically")
41
+ end
42
+ end
43
+ end
44
+
45
+ def die(msg, error_code=1)
46
+ $stderr.puts msg
47
+ exit error_code
48
+ end
49
+
50
+ class File
51
+ def self.open_or_die(filename, &block)
52
+ filename = filename.sub(/^\/([cdefgh])(\/)/, '\1:\2') if RUBY_PLATFORM =~ /win32/
53
+ begin
54
+ if stat(filename).readable?
55
+ open(filename, &block)
56
+ else
57
+ die "file #{filename} is unreadable"
58
+ end
59
+ rescue
60
+ die "file #{filename} does not exist"
61
+ end
62
+ end
63
+ end
64
+
65
+ def truncate(text, length = 32, truncate_string = "...")
66
+ if text.nil? then return "" end
67
+ l = truncate_string.length + 1
68
+
69
+ if RUBY_VERSION !~ /1.9/ && $KCODE == "NONE"
70
+ text.length > length ? text[0..(length - l)] + truncate_string : text
71
+ else
72
+ chars = text.split(//)
73
+ chars.length > length ? chars[0..(length - l)].join + truncate_string : text
74
+ end
75
+ end
76
+
77
+ RAILSBENCH_BINDIR = File.expand_path(File.dirname(__FILE__) + "/../../script")
78
+
79
+ def enable_gc_stats(file)
80
+ ENV['RUBY_GC_STATS'] = "1"
81
+ ENV['RUBY_GC_DATA_FILE'] = file
82
+ end
83
+
84
+ def disable_gc_stats
85
+ ENV.delete 'RUBY_GC_STATS'
86
+ ENV.delete 'RUBY_GC_DATA_FILE'
87
+ end
88
+
89
+ def unset_gc_variables
90
+ %w(RUBY_HEAP_MIN_SLOTS RUBY_GC_MALLOC_LIMIT RUBY_HEAP_FREE_MIN).each{|v| ENV.delete v}
91
+ end
92
+
93
+ def load_gc_variables(gc_spec)
94
+ File.open_or_die("#{ENV['RAILS_ROOT']}/config/#{gc_spec}.gc").each_line do |line|
95
+ ENV[$1] = $2 if line =~ /^(?:export )?(.*)=(.*)$/
96
+ end
97
+ end
98
+
99
+ def set_gc_variables(argv)
100
+ gc_spec = nil
101
+ argv.each{|arg| gc_spec=$1 if arg =~ /-gc=([^ ]*)/}
102
+
103
+ if gc_spec
104
+ load_gc_variables(gc_spec)
105
+ else
106
+ unset_gc_variables
107
+ end
108
+ end
109
+
110
+ def benchmark_file_name(benchmark, config_name, prefix=nil, suffix=nil)
111
+ perf_data_dir = (ENV['RAILS_PERF_DATA'] ||= ENV['HOME'])
112
+ date = Time.now.strftime '%m-%d'
113
+ suffix = ".#{suffix}" if suffix
114
+ ENV['RAILS_BENCHMARK_FILE'] =
115
+ if config_name
116
+ "#{perf_data_dir}/#{date}.#{benchmark}.#{config_name}#{suffix}.txt"
117
+ else
118
+ "#{perf_data_dir}/perf_run#{prefix}.#{benchmark}#{suffix}.txt"
119
+ end
120
+ end
121
+
122
+ def quote_arguments(argv)
123
+ argv.map{|a| a.include?(' ') ? "'#{a}'" : a.to_s}.join(' ')
124
+ end
125
+
126
+ def ruby
127
+ ENV['RUBY'] || "ruby"
128
+ end
129
+
130
+ def perf_run(script, iterations, options, raw_data_file)
131
+ perf_runs = (ENV['RAILS_PERF_RUNS'] ||= "3").to_i
132
+
133
+ disable_gc_stats
134
+ set_gc_variables([iterations, options])
135
+
136
+ perf_options = "#{iterations} #{options}"
137
+ null = (RUBY_PLATFORM =~ /win32/i) ? 'nul' : '/dev/null'
138
+
139
+ perf_cmd = "#{ruby} #{RAILSBENCH_BINDIR}/perf_bench #{perf_options}"
140
+ print_cmd = "#{ruby} #{RAILSBENCH_BINDIR}/perf_times #{raw_data_file}"
141
+
142
+ puts "benchmarking #{perf_runs} runs with options #{perf_options}"
143
+
144
+ File.open(raw_data_file, "w"){ |f| f.puts perf_cmd }
145
+ perf_runs.times do
146
+ system("#{perf_cmd} >#{null}") || die("#{script}: #{perf_cmd} returned #{$?}")
147
+ end
148
+ File.open(raw_data_file, "a" ){|f| f.puts }
149
+
150
+ unset_gc_variables
151
+ system(print_cmd) || die("#{script}: #{print_cmd} returned #{$?}")
152
+ end
153
+
154
+ def perf_run_gc(script, iterations, options, raw_data_file)
155
+ warmup = "-warmup"
156
+ warmup = "" if options =~ /-warmup/
157
+
158
+ enable_gc_stats(raw_data_file)
159
+ set_gc_variables([options])
160
+
161
+ perf_options = "#{iterations} #{warmup} #{options}"
162
+ null = (RUBY_PLATFORM =~ /win32/) ? 'nul' : '/dev/null'
163
+
164
+ perf_cmd = "#{ruby} #{RAILSBENCH_BINDIR}/run_urls #{perf_options} >#{null}"
165
+ print_cmd = "#{ruby} #{RAILSBENCH_BINDIR}/perf_times_gc #{raw_data_file}"
166
+
167
+ if options =~ /-leaks/
168
+ if RUBY_PLATFORM =~ /darwin9/
169
+ puts "enabling MallocStackLogging"
170
+ perf_cmd.insert(0, "MallocStackLogging=1 ")
171
+ else
172
+ die "leak debugging not supported on #{RUBY_PLATFORM}"
173
+ end
174
+ end
175
+
176
+ puts "benchmarking GC performance with options #{perf_options}"
177
+ puts
178
+
179
+ system(perf_cmd) || die("#{script}: #{perf_cmd} returned #{$?}")
180
+
181
+ disable_gc_stats
182
+ unset_gc_variables
183
+ system(print_cmd) || die("#{script}: #{print_cmd} returned #{$?}")
184
+ end
185
+
186
+ __END__
187
+
188
+ # Copyright (C) 2005-2008 Stefan Kaes
189
+ #
190
+ # This program is free software; you can redistribute it and/or modify
191
+ # it under the terms of the GNU General Public License as published by
192
+ # the Free Software Foundation; either version 2 of the License, or
193
+ # (at your option) any later version.
194
+ #
195
+ # This program is distributed in the hope that it will be useful,
196
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
197
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
198
+ # GNU General Public License for more details.
199
+ #
200
+ # You should have received a copy of the GNU General Public License
201
+ # along with this program; if not, write to the Free Software
202
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA