railsbench 0.9.2 → 0.9.8

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 (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