railsbench 0.9.2 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGELOG +1808 -451
  2. data/GCPATCH +73 -0
  3. data/INSTALL +5 -0
  4. data/Manifest.txt +23 -13
  5. data/PROBLEMS +0 -0
  6. data/README +23 -7
  7. data/Rakefile +1 -2
  8. data/bin/railsbench +7 -1
  9. data/config/benchmarking.rb +0 -0
  10. data/config/benchmarks.rb +3 -2
  11. data/config/benchmarks.yml +0 -0
  12. data/images/empty.png +0 -0
  13. data/images/minus.png +0 -0
  14. data/images/plus.png +0 -0
  15. data/install.rb +1 -1
  16. data/latest_changes.txt +18 -0
  17. data/lib/benchmark.rb +0 -0
  18. data/lib/railsbench/benchmark.rb +576 -0
  19. data/lib/railsbench/benchmark_specs.rb +63 -63
  20. data/lib/railsbench/gc_info.rb +38 -3
  21. data/lib/railsbench/perf_info.rb +1 -1
  22. data/lib/railsbench/perf_utils.rb +202 -179
  23. data/lib/railsbench/railsbenchmark.rb +213 -55
  24. data/lib/railsbench/version.rb +9 -9
  25. data/lib/railsbench/write_headers_only.rb +15 -15
  26. data/postinstall.rb +0 -0
  27. data/ruby185gc.patch +56 -29
  28. data/ruby186gc.patch +564 -0
  29. data/ruby19gc.patch +2425 -0
  30. data/script/convert_raw_data_files +49 -49
  31. data/script/generate_benchmarks +14 -4
  32. data/script/perf_bench +12 -8
  33. data/script/perf_comp +1 -1
  34. data/script/perf_comp_gc +9 -1
  35. data/script/perf_diff +2 -2
  36. data/script/perf_diff_gc +2 -2
  37. data/script/perf_html +1 -1
  38. data/script/perf_plot +192 -75
  39. data/script/perf_plot_gc +213 -74
  40. data/script/perf_prof +29 -10
  41. data/script/perf_run +2 -2
  42. data/script/perf_run_gc +2 -2
  43. data/script/perf_table +2 -2
  44. data/script/perf_tex +1 -1
  45. data/script/perf_times +6 -6
  46. data/script/perf_times_gc +14 -2
  47. data/script/run_urls +16 -10
  48. data/setup.rb +0 -0
  49. data/test/railsbench_test.rb +0 -0
  50. data/test/test_helper.rb +2 -0
  51. metadata +77 -55
data/script/perf_plot_gc CHANGED
@@ -1,104 +1,243 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'rubygems'
4
- require 'gruff'
4
+ require 'optparse'
5
+ require 'ostruct'
5
6
 
6
7
  $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
7
8
  require 'railsbench/gc_info'
8
9
 
9
- # extract options
10
+ # parse options
11
+ o = OpenStruct.new
12
+ o.title = "GC Data Plot"
13
+ o.graph_width = '1400x1050'
14
+ o.font_size = 10
15
+ o.ignored_object_types = []
16
+ o.output_file = nil
17
+ o.plot_live = true
18
+ o.plot_freed = true
19
+ o.plot_type = :both
20
+ o.engine = :gruff
21
+
22
+ parser = OptionParser.new do |opts|
23
+ opts.banner = "Usage: perf_plot_gc [options] file1 file2 ..."
24
+
25
+ opts.separator ""
26
+ opts.separator "Options:"
27
+
28
+ opts.on("-t", "--title T",
29
+ "Specify the title for your plot") do |t|
30
+ o.title = t
31
+ end
10
32
 
11
- selection = []
12
- title = "GC Data Graph"
13
- files = []
14
- names = []
15
- labels = %w()
16
- perf_data = []
17
- graph_type = Gruff::StackedBar
18
- graph_width = '1400x1050'
19
- font_size = 14
20
- ignored_object_types = %w(NODE STRING)
33
+ opts.on("-i", "--ignore LIST", Array,
34
+ "Specify the object types to ignore") do |i|
35
+ o.ignored_object_types = i.map{|t| t.upcase}
36
+ end
21
37
 
22
- ARGV.each do |arg|
23
- case arg
24
- # when '-line'
25
- # graph_type = Gruff::Line
26
- # when '-bar'
27
- # graph_type = Gruff::Bar
28
- when /^-width=(\d+)$/
29
- graph_width = $1.to_i
30
- when /^-geometry=(\d+x\d+)$/
31
- graph_width = $1
32
- # when /^-title=(.*)$/
33
- # title = $1 unless $1.strip.empty?
34
- # when /^-names=(.+)$/
35
- # names = $1.split(',')
36
- when /^-font_size=(\d+)$/
37
- font_size = $1.to_i
38
- when /^-ignore=(.*)$/
39
- ignored_object_types = $1.split(',').map(&:upcase)
40
- else
41
- files << File.open_or_die(arg)
42
- names[files.length-1] ||= File.basename(arg)
38
+ opts.on("-d", "--type TYPE", [:freed, :live, :both],
39
+ "Select data points to plot: (live, freed, both)") do |dp|
40
+ o.plot_type = dp
41
+ o.plot_freed = o.plot_live = false
42
+ case dp
43
+ when :live then o.plot_live = true
44
+ when :freed then o.plot_freed = true
45
+ when :both then o.plot_freed = true; o.plot_live = true
46
+ end
47
+ end
48
+
49
+ opts.on("-e", "--engine ENGINE", [:gruff, :gnuplot],
50
+ "Select plotting engine: (gruff, gnuplot)") do |e|
51
+ o.engine = e
52
+ end
53
+
54
+ opts.on("-f", "--font-size N", Integer,
55
+ "Overall font size to use in the plot (points)") do |n|
56
+ o.font_size = n
57
+ end
58
+
59
+ opts.on("-w", "--width W", Integer,
60
+ "Width of the plot (pixels)") do |w|
61
+ o.graph_width = w
43
62
  end
63
+
64
+ opts.on("-g", "--geometry WxH", /\d+x\d+/,
65
+ "Specify plot dimensions (pixels)") do |d|
66
+ o.graph_width = d
67
+ end
68
+
69
+ opts.on("-o", "--out FILE",
70
+ "Specify output file") do |f|
71
+ o.output_file = f
72
+ end
73
+
74
+ opts.on_tail("-h", "--help", "Show this message") do
75
+ puts opts
76
+ exit
77
+ end
78
+ end
79
+
80
+ # option compatibility with older versions
81
+ args=[]
82
+ ARGV.each do |arg|
83
+ arg = arg.sub("font_size", "font-size") if arg =~ /^-font_size/
84
+ arg = "-" + arg if arg =~ /^-(title|width|out|geometry|font-size|ignore)/
85
+ args << arg
44
86
  end
45
87
 
46
- files.length > 0 or die "usage: perf_plot_gc [options] file1 file2 ..."
88
+ parser.parse!(args)
89
+
90
+ o.output_file ||= if o.engine == :gruff
91
+ "graph.png"
92
+ elsif RUBY_PLATFORM =~ /darwin/
93
+ "graph.pdf"
94
+ else
95
+ "graph.ps"
96
+ end
97
+ o.files = []
98
+ o.names = []
99
+ args.each do |arg|
100
+ o.files << File.open_or_die(arg)
101
+ o.names[o.files.length-1] ||= File.basename(arg)
102
+ end
103
+
104
+ o.files.length > 0 or die(parser.banner)
47
105
 
48
- object_types = Set.new
49
- gcis = []
50
- files.each do |file|
51
- gcis << GCInfo.new(file)
52
- object_types.merge gcis.last.object_types
106
+ o.gcis = []
107
+ o.files.each do |file|
108
+ o.gcis << GCInfo.new(file)
53
109
  file.close
54
110
  end
55
111
 
56
- object_types = object_types.to_a
57
- gc_count_max = gcis.map{|gci| gci.collections}.max
58
- gc_max_processed = gcis.map{|gci| gci.processed_max}.max
112
+ # o.object_types = %w(NODE STRING ARRAY HASH SCOPE VARMAP CLASS ICLASS REGEXP FLOAT MATCH FILE DATA MODULE OBJECT)
113
+ o.colors = %w(ff0000 00c000 0080ff c000ff 00eeee c04000 ee0000 2020c0 ffc020 008040 a080ff 804000 ff80ff 00c060 006080 c06080 008000 40ff80 306080 806000).map{|c| "#" + c}
114
+ o.object_types = GCInfo.object_types(o.gcis)
115
+ o.gc_count_max = o.gcis.map{|gci| gci.collections}.max
116
+ o.gc_max_processed = o.gcis.map{|gci| gci.processed_max}.max
117
+ o.gc_max_freed = o.gcis.map{|gci| gci.freed_max}.max
118
+ o.gc_max_live = o.gcis.map{|gci| gci.live_max}.max
119
+
120
+ o.title << " ["
121
+ o.title << "freed" if o.plot_freed
122
+ o.title << "," if o.plot_freed && o.plot_live
123
+ o.title << "live" if o.plot_live
124
+ o.title << "]"
125
+ o.title << " (ignoring #{o.ignored_object_types.join(', ')})" unless o.ignored_object_types.empty?
126
+
127
+ # for very large logs, we need to ignore some entries
128
+ N = o.gc_count_max < 100 ? 1 : o.gc_count_max / 99
129
+
130
+ class Plotter
131
+ attr_reader :o
132
+ def initialize(options)
133
+ @o = options
134
+ end
59
135
 
60
- g = graph_type.new(graph_width)
136
+ def plot_with_gruff
137
+ require 'gruff'
61
138
 
62
- g.add_color("#FF0000")
63
- g.add_color("#00FF00")
64
- g.add_color("#0000FF")
139
+ g = Gruff::StackedBar.new(o.graph_width)
65
140
 
66
- g.instance_eval do
67
- count = @colors.length
68
- count.times{|i| add_color(@colors[i])}
69
- end
141
+ # on OS X, ImageMagick can't find it's default font (arial) sometimes, so specify some font
142
+ # g.font = 'Arial-Normal' if RUBY_PLATFORM =~ /darwin/
143
+ g.font = 'Helvetica-Narrow' if RUBY_PLATFORM =~ /darwin/
144
+
145
+ %w(#FF0000 #00FF00 #0000FF #D2FF77 #FF68C0 #D1FDFF #FFF0BD #15FFDC
146
+ ).each do |color|
147
+ g.add_color(color)
148
+ end
70
149
 
71
- g.title = title
72
- g.sort = false
73
- g.legend_font_size = font_size
74
- g.legend_box_size = font_size
75
- g.marker_font_size = font_size
76
- #g.minimum_value = 0
77
- #g.maximum_value = gc_max_processed
78
- g.labels = Hash[* (0...gc_count_max).map{|i| [2*i, i.to_s]}.flatten ]
79
- puts g.labels.inspect
80
-
81
- object_types.each do |ot|
82
- next if ignored_object_types.include?(ot)
83
- data = []
84
- gc_count_max.times do |gc_index|
85
- for gci in gcis
86
- map_at_this_gc = gci.freed_objects[gc_index]
87
- data << ((map_at_this_gc && map_at_this_gc[ot]) || 0)
88
- map_at_this_gc = gci.live_objects[gc_index]
89
- data << ((map_at_this_gc && map_at_this_gc[ot]) || 0)
150
+ g.title = o.title
151
+ g.sort = false
152
+ g.title_font_size = o.font_size+2
153
+ g.legend_font_size = o.font_size-2
154
+ g.legend_box_size = o.font_size-2
155
+ g.marker_font_size = o.font_size-2
156
+ if o.ignored_object_types.empty?
157
+ g.minimum_value = 0
158
+ maximums = []
159
+ maximums << o.gc_max_live if o.plot_live
160
+ maximums << o.gc_max_freed if o.plot_freed
161
+ g.maximum_value = maximums.max
162
+ end
163
+ label_step = 0
164
+ label_step += 1 if o.plot_live
165
+ label_step += 1 if o.plot_freed
166
+ g.labels = Hash[* (0...o.gc_count_max).map{|i| [label_step*i*o.files.length, i.to_s]}.flatten ]
167
+ # puts g.labels.inspect
168
+ # puts object_types.inspect
169
+ puts "ignoring #{o.ignored_object_types.join(', ')}" unless o.ignored_object_types.empty?
170
+
171
+ (o.object_types + %w(FREELIST)).each do |ot|
172
+ data = prepare_data(ot)
173
+ # puts "#{ot}: #{data.inspect}"
174
+ g.data(ot, data)
90
175
  end
176
+
177
+ g.write(o.output_file)
91
178
  end
92
- # puts "#{ot}: #{data.inspect}"
93
- g.data(ot, data)
94
- end
95
179
 
96
- g.write
180
+ def prepare_data(ot)
181
+ return if o.ignored_object_types.include?(ot)
182
+ data = []
183
+ o.gc_count_max.times do |gc_index|
184
+ next unless 0 == gc_index.modulo(N)
185
+ for gci in o.gcis
186
+ map_at_this_gc = gci.freed_objects[gc_index].merge('FREELIST' => gci.freelist[gc_index])
187
+ data << ((map_at_this_gc && map_at_this_gc[ot]) || 0) if o.plot_freed
188
+ map_at_this_gc = gci.live_objects[gc_index]
189
+ data << ((map_at_this_gc && map_at_this_gc[ot]) || 0) if o.plot_live
190
+ end
191
+ end
192
+ data
193
+ end
194
+
195
+ def plot_with_gnuplot
196
+ require 'gnuplot'
197
+ # there's a separate gnuplot binary which can read from stdin, but the gem doesn't know this
198
+ ENV['RB_GNUPLOT'] ||= 'pgnuplot.exe' if RUBY_PLATFORM =~ /mswin/
199
+ plot = Gnuplot::Plot.new
200
+ if o.output_file =~ /\.pdf$/
201
+ plot.terminal "pdf enhanced color font 'Helvetica,4'"
202
+ else
203
+ plot.terminal "postscript enhanced color font 'Helvetica,4'"
204
+ end
205
+ plot.output o.output_file
206
+ plot.xlabel "Collections"
207
+ plot.ylabel "Objects"
208
+ plot.title o.title
209
+ plot.style "fill solid 1.0 noborder"
210
+ plot.style "data histogram"
211
+ plot.style "histogram rowstacked"
212
+ plot.xtics "out nomirror"
213
+ plot.xrange "[-1:#{(o.gc_count_max/N)*((o.plot_type == :both) ? 2 : 1)}]"
214
+ plot.key "outside invert reverse"
215
+ plot.boxwidth "0.8"
216
+ plot.grid "nopolar"
217
+ plot.grid "noxtics nomxtics ytics nomytics noztics nomztics nox2tics nomx2tics noy2tics nomy2tics nocbtics nomcbtics"
218
+ plot.grid "back linetype 0 linewidth 0.7"
219
+
220
+ (o.object_types + %w(FREELIST)).each_with_index do |ot, i|
221
+ next unless data = prepare_data(ot)
222
+ plot.data << Gnuplot::DataSet.new([[ot.downcase] + data]) do |ds|
223
+ ds.using = "1 title 1 lc rgb '#{ot == 'FREELIST' ? '#666666' : o.colors[i]}'"
224
+ end
225
+ end
226
+
227
+ cmds = plot.to_gplot # puts cmds
228
+ Gnuplot.open(false){|gnuplot| gnuplot << cmds}
229
+ end
230
+
231
+ def plot
232
+ send "plot_with_#{o.engine}"
233
+ end
234
+ end
97
235
 
236
+ Plotter.new(o).plot
98
237
 
99
238
  __END__
100
239
 
101
- # Copyright (C) 2005, 2006, 2007 Stefan Kaes
240
+ # Copyright (C) 2005-2008 Stefan Kaes
102
241
  #
103
242
  # This program is free software; you can redistribute it and/or modify
104
243
  # it under the terms of the GNU General Public License as published by
data/script/perf_prof CHANGED
@@ -9,6 +9,8 @@ end
9
9
  bindir = File.dirname(__FILE__)
10
10
  require "#{bindir}/../lib/railsbench/perf_utils"
11
11
 
12
+ determine_rails_root_or_die!
13
+
12
14
  perf_data_dir = (ENV['RAILS_PERF_DATA'] ||= ENV['HOME'])
13
15
 
14
16
  iterations = ARGV[0]
@@ -16,20 +18,32 @@ options = ARGV[1]
16
18
  config = ARGV[2]
17
19
  benchmark = ""
18
20
 
19
- ruby_prof_opts="-ruby_prof=1"
21
+ ruby_prof_opts="-ruby_prof=0.1/1"
20
22
  warmup="-warmup"
21
23
 
22
24
  warmup = "" if options =~ /-warmup/
23
25
  benchmark = $1 if options =~ /-bm=([^ ]+)/
24
- ruby_prof_opts = $1 if options =~ /(-ruby_prof=[^ ]+)/
25
- options = options.sub(ruby_prof_opts, '')
26
-
26
+ if options =~ /(-ruby_prof=[^ ]+)/
27
+ ruby_prof_opts = $1
28
+ options = options.sub($1, '')
29
+ end
30
+ profile_type = 'graph'
31
+ if options =~ /(-profile_type=([^ ]+))/
32
+ profile_type = $2
33
+ ruby_prof_opts << " " << $1
34
+ options = options.sub($1, '')
35
+ end
36
+ extension = case profile_type
37
+ when 'grind' then 'dat'
38
+ when 'flat' then 'txt'
39
+ else 'html'
40
+ end
27
41
  date = Time.now.strftime '%m-%d'
28
42
 
29
43
  if config
30
- benchmark_file="#{perf_data_dir}/#{date}.#{benchmark}.#{config}.html"
44
+ benchmark_file="#{perf_data_dir}/#{date}.#{benchmark}.#{config}.#{profile_type}.#{extension}"
31
45
  else
32
- benchmark_file="#{perf_data_dir}/perf_run.#{benchmark}.html"
46
+ benchmark_file="#{perf_data_dir}/perf_run.#{benchmark}.#{profile_type}.#{extension}"
33
47
  end
34
48
  ENV['RAILS_BENCHMARK_FILE'] = benchmark_file
35
49
 
@@ -42,16 +56,21 @@ perf_options="#{iterations} #{options} #{warmup} #{ruby_prof_opts}"
42
56
  perf_cmd = "ruby #{bindir}/run_urls #{perf_options} >#{null}"
43
57
  system(perf_cmd) || die("perf_prof: #{perf_cmd} returned #{$?}")
44
58
 
59
+ benchmark_file.sub!('.multi.', '.stack.') if profile_type == 'multi'
45
60
  puts "profile data written to #{benchmark_file}"
46
61
 
47
- case RUBY_PLATFORM
48
- when /win32/ then system("start #{benchmark_file.gsub(/\//, '\\')}")
49
- when /darwin/ then system("open #{benchmark_file}")
62
+ if profile_type == 'grind'
63
+ system("kcachegrind #{benchmark_file} 2>/dev/null &") unless RUBY_PLATFORM =~ /win32/
64
+ else
65
+ case RUBY_PLATFORM
66
+ when /win32/ then system("start #{benchmark_file.gsub(/\//, '\\')}")
67
+ when /darwin/ then system("osascript -e 'tell application \"Safari\" to open location \"#{benchmark_file}\"'")
68
+ end
50
69
  end
51
70
 
52
71
  __END__
53
72
 
54
- # Copyright (C) 2005, 2006, 2007 Stefan Kaes
73
+ # Copyright (C) 2005-2008 Stefan Kaes
55
74
  #
56
75
  # This program is free software; you can redistribute it and/or modify
57
76
  # it under the terms of the GNU General Public License as published by
data/script/perf_run CHANGED
@@ -9,7 +9,7 @@ end
9
9
  bindir = File.dirname(__FILE__)
10
10
  require "#{bindir}/../lib/railsbench/perf_utils"
11
11
 
12
- die("perf_run: RAILS_ROOT not set") unless ENV['RAILS_ROOT']
12
+ determine_rails_root_or_die!
13
13
 
14
14
  iterations = ARGV[0]
15
15
  options = ARGV[1]
@@ -22,7 +22,7 @@ perf_run("perf_run", iterations, options, benchmark_file)
22
22
 
23
23
  __END__
24
24
 
25
- # Copyright (C) 2005, 2006, 2007 Stefan Kaes
25
+ # Copyright (C) 2005-2008 Stefan Kaes
26
26
  #
27
27
  # This program is free software; you can redistribute it and/or modify
28
28
  # it under the terms of the GNU General Public License as published by
data/script/perf_run_gc CHANGED
@@ -9,7 +9,7 @@ end
9
9
  bindir = File.dirname(__FILE__)
10
10
  require "#{bindir}/../lib/railsbench/perf_utils"
11
11
 
12
- die("perf_run_gc: RAILS_ROOT not set") unless ENV['RAILS_ROOT']
12
+ determine_rails_root_or_die!
13
13
 
14
14
  iterations = ARGV[0]
15
15
  options = ARGV[1]
@@ -23,7 +23,7 @@ perf_run_gc("perf_run_gc", iterations, options, benchmark_file)
23
23
 
24
24
  __END__
25
25
 
26
- # Copyright (C) 2005, 2006, 2007 Stefan Kaes
26
+ # Copyright (C) 2005-2008 Stefan Kaes
27
27
  #
28
28
  # This program is free software; you can redistribute it and/or modify
29
29
  # it under the terms of the GNU General Public License as published by
data/script/perf_table CHANGED
@@ -67,7 +67,7 @@ perf_data = perf_data.map{|d| d.restrict_to(selection.map{|i| i-1})}
67
67
  #puts labels.inspect
68
68
  #puts perf_data.length
69
69
  #puts perf_data.inspect
70
- #puts names.inspect
70
+ #puts names.inspect
71
71
 
72
72
  # puts labels.zip(perf_data).inspect
73
73
  puts "<table border=1>"
@@ -87,7 +87,7 @@ puts "</table>"
87
87
 
88
88
  __END__
89
89
 
90
- # Copyright (C) 2007 Stefan Kaes
90
+ # Copyright (C) 2007, 2008 Stefan Kaes
91
91
  #
92
92
  # This program is free software; you can redistribute it and/or modify
93
93
  # it under the terms of the GNU General Public License as published by
data/script/perf_tex CHANGED
@@ -41,7 +41,7 @@ end
41
41
 
42
42
  __END__
43
43
 
44
- # Copyright (C) 2005, 2006, 2007 Stefan Kaes
44
+ # Copyright (C) 2005-2008 Stefan Kaes
45
45
  #
46
46
  # This program is free software; you can redistribute it and/or modify
47
47
  # it under the terms of the GNU General Public License as published by