kamal-railsbench 0.9.9.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/script/perf_diff
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if ARGV.length < 4 || ARGV.first == 'help'
|
4
|
+
$stderr.puts 'usage: perf_diff iterations common-options options1 options4 [conf-name1] [conf-name2]'
|
5
|
+
$stderr.puts 'example: perf_diff 100 "-bm=all" "-log" "-nocache" c1 c2'
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
|
9
|
+
bindir = File.dirname(__FILE__)
|
10
|
+
require "#{bindir}/../lib/railsbench/perf_utils"
|
11
|
+
|
12
|
+
determine_rails_root_or_die!
|
13
|
+
|
14
|
+
iterations = ARGV[0]
|
15
|
+
common = ARGV[1]
|
16
|
+
options1 = ARGV[2]
|
17
|
+
options2 = ARGV[3]
|
18
|
+
|
19
|
+
benchmark = "default"
|
20
|
+
benchmark = $1 if common =~ /-bm=([^ ]+)/
|
21
|
+
|
22
|
+
file1 = benchmark_file_name(benchmark, ARGV[4], 1)
|
23
|
+
perf_run("perf_diff", iterations, "#{common} #{options1}", file1)
|
24
|
+
puts;puts
|
25
|
+
|
26
|
+
file2 = benchmark_file_name(benchmark, ARGV[5], 2)
|
27
|
+
perf_run("perf_diff", iterations, "#{common} #{options2}", file2)
|
28
|
+
puts;puts
|
29
|
+
|
30
|
+
system("ruby #{bindir}/perf_comp #{file1} #{file2}")
|
31
|
+
|
32
|
+
__END__
|
33
|
+
|
34
|
+
# Copyright (C) 2005-2008 Stefan Kaes
|
35
|
+
#
|
36
|
+
# This program is free software; you can redistribute it and/or modify
|
37
|
+
# it under the terms of the GNU General Public License as published by
|
38
|
+
# the Free Software Foundation; either version 2 of the License, or
|
39
|
+
# (at your option) any later version.
|
40
|
+
#
|
41
|
+
# This program is distributed in the hope that it will be useful,
|
42
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
43
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
44
|
+
# GNU General Public License for more details.
|
45
|
+
#
|
46
|
+
# You should have received a copy of the GNU General Public License
|
47
|
+
# along with this program; if not, write to the Free Software
|
48
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
data/script/perf_diff_gc
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if ARGV.length < 4 || ARGV.first == 'help'
|
4
|
+
$stderr.puts 'usage: perf_diff_gc iterations options1 options2 [conf-name1] [conf-name2]'
|
5
|
+
$stderr.puts 'example: perf_diff_gc 100 "-bm=default" "-log" "-nocache" c1 c2'
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
|
9
|
+
bindir = File.dirname(__FILE__)
|
10
|
+
require "#{bindir}/../lib/railsbench/perf_utils"
|
11
|
+
|
12
|
+
determine_rails_root_or_die!
|
13
|
+
|
14
|
+
iterations = ARGV[0]
|
15
|
+
common = ARGV[1]
|
16
|
+
options1 = ARGV[2]
|
17
|
+
options2 = ARGV[3]
|
18
|
+
|
19
|
+
benchmark = "default"
|
20
|
+
benchmark = $1 if common =~ /-bm=([^ ]+)/
|
21
|
+
|
22
|
+
file1 = benchmark_file_name(benchmark, ARGV[4], 1, :gc)
|
23
|
+
perf_run_gc("perf_run_gc", iterations, "#{common} #{options1}", file1)
|
24
|
+
|
25
|
+
puts;puts
|
26
|
+
|
27
|
+
file2 = benchmark_file_name(benchmark, ARGV[5], 2, :gc)
|
28
|
+
perf_run_gc("perf_run_gc", iterations, "#{common} #{options2}", file2)
|
29
|
+
|
30
|
+
puts;puts
|
31
|
+
|
32
|
+
puts "benchmark comparison data"
|
33
|
+
puts
|
34
|
+
|
35
|
+
system("ruby #{bindir}/perf_comp_gc #{file1} #{file2}")
|
36
|
+
|
37
|
+
__END__
|
38
|
+
|
39
|
+
# Copyright (C) 2005-2008 Stefan Kaes
|
40
|
+
#
|
41
|
+
# This program is free software; you can redistribute it and/or modify
|
42
|
+
# it under the terms of the GNU General Public License as published by
|
43
|
+
# the Free Software Foundation; either version 2 of the License, or
|
44
|
+
# (at your option) any later version.
|
45
|
+
#
|
46
|
+
# This program is distributed in the hope that it will be useful,
|
47
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
48
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
49
|
+
# GNU General Public License for more details.
|
50
|
+
#
|
51
|
+
# You should have received a copy of the GNU General Public License
|
52
|
+
# along with this program; if not, write to the Free Software
|
53
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
data/script/perf_html
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
unless ARGV.include?('-nocss')
|
4
|
+
puts <<'END'
|
5
|
+
<style type="text/css">
|
6
|
+
<!--
|
7
|
+
.perf_header, .perf_data, .perf_name { font-size:70%; padding-left:5px; padding-right:5px; }
|
8
|
+
.perf_header { text-align:center; vertical-align:top; font-weight:bold;}
|
9
|
+
.i { font-style:italic; }
|
10
|
+
.b { font-weight:bold; }
|
11
|
+
.c1 { background:#fff3df; }
|
12
|
+
.c2 { background:#dfedff; }
|
13
|
+
.factor { background:#efe0ef; }
|
14
|
+
.factor_red_alert { background:#CC303B; }
|
15
|
+
.factor_red { background:#FF6060; }
|
16
|
+
.factor_reddish { background:#FFA766; }
|
17
|
+
.factor_green_alert { background:#0B8900; }
|
18
|
+
.factor_green { background:#6AB788; }
|
19
|
+
.factor_greenish { background:#7FFFB0; }
|
20
|
+
.name { background:#dfdfdf; }
|
21
|
+
.perf_name { text-align:left; }
|
22
|
+
.perf_data { text-align:right; }
|
23
|
+
-->
|
24
|
+
</style>
|
25
|
+
END
|
26
|
+
end
|
27
|
+
|
28
|
+
def factor_class(v)
|
29
|
+
return 'factor' unless ARGV.include?('-colorize')
|
30
|
+
if v <= 0.85
|
31
|
+
'factor_red_alert'
|
32
|
+
elsif v <= 0.90
|
33
|
+
'factor_red'
|
34
|
+
elsif v <= 0.95
|
35
|
+
'factor_reddish'
|
36
|
+
elsif v >= 1.15
|
37
|
+
'factor_green_alert'
|
38
|
+
elsif v >= 1.10
|
39
|
+
'factor_green'
|
40
|
+
elsif v >= 1.05
|
41
|
+
'factor_greenish'
|
42
|
+
else
|
43
|
+
'factor'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
unless ARGV.include?('-notable')
|
48
|
+
puts "<table cellspacing=1px>"
|
49
|
+
$stdin.each_line do |l|
|
50
|
+
case l
|
51
|
+
when /^garbage collection/
|
52
|
+
unless ARGV.include?('-gc')
|
53
|
+
puts "</table>"
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
puts "<tr></tr><tr></tr>"
|
57
|
+
puts "<tr>"
|
58
|
+
puts "<th class='perf_header name' style='text-align:left'>GC statistics</th>"
|
59
|
+
puts "<th class='perf_header c1'>c1 total</th><th class='perf_header c2'>c2 total</th>"
|
60
|
+
puts "<th class='perf_header c1'>c1 #gc</th><th class='perf_header c2'>c2 #gc</th>"
|
61
|
+
puts "<th class='perf_header c1'>c1 gc%</th><th class='perf_header c2'>c2 #gc%</th>"
|
62
|
+
puts "<th class='perf_header factor'>c1/c2</th>"
|
63
|
+
puts "</tr>"
|
64
|
+
when /^page/
|
65
|
+
puts "<tr>"
|
66
|
+
puts "<th class='perf_header name' style='text-align:left;'>page</th>"
|
67
|
+
puts "<th class='perf_header c1'>c1 total</th><th class='perf_header c2'>c2 total</th>"
|
68
|
+
puts "<th class='perf_header c1'>c1 r/s</th><th class='perf_header c2'>c2 r/s</th>"
|
69
|
+
puts "<th class='perf_header c1'>c1 ms/r</th><th class='perf_header c2'>c2 ms/r</th>"
|
70
|
+
puts "<th class='perf_header factor'>c1/c2</th>"
|
71
|
+
puts "</tr>"
|
72
|
+
end
|
73
|
+
case l
|
74
|
+
when %r{^([A-Za-z0-9./?=_ ]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+$}
|
75
|
+
puts "<tr>"
|
76
|
+
puts "<td class='perf_name name#{" i" if $1.strip == "all requests"}'>#{$1}</td>"
|
77
|
+
puts "<td class='perf_data c1'>#{$2}</td><td class='perf_data c2'>#{$3}</td>"
|
78
|
+
puts "<td class='perf_data c1'>#{$4}</td><td class='perf_data c2'>#{$5}</td>"
|
79
|
+
puts "<td class='perf_data c1'>#{$6}</td><td class='perf_data c2'>#{$7}</td>"
|
80
|
+
puts "<td class='perf_data #{factor_class($8.to_f)}'>#{$8}</td>"
|
81
|
+
puts "</tr>"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
puts "</table>"
|
85
|
+
end
|
86
|
+
|
87
|
+
__END__
|
88
|
+
|
89
|
+
# Copyright (C) 2005-2008 Stefan Kaes
|
90
|
+
#
|
91
|
+
# This program is free software; you can redistribute it and/or modify
|
92
|
+
# it under the terms of the GNU General Public License as published by
|
93
|
+
# the Free Software Foundation; either version 2 of the License, or
|
94
|
+
# (at your option) any later version.
|
95
|
+
#
|
96
|
+
# This program is distributed in the hope that it will be useful,
|
97
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
98
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
99
|
+
# GNU General Public License for more details.
|
100
|
+
#
|
101
|
+
# You should have received a copy of the GNU General Public License
|
102
|
+
# along with this program; if not, write to the Free Software
|
103
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
data/script/perf_plot
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
9
|
+
require 'railsbench/perf_info'
|
10
|
+
|
11
|
+
# parse options
|
12
|
+
o = OpenStruct.new
|
13
|
+
o.selection = []
|
14
|
+
o.title = "Performance Graph"
|
15
|
+
o.files = []
|
16
|
+
o.names = []
|
17
|
+
o.labels = []
|
18
|
+
o.graph_type = :line
|
19
|
+
o.graph_width = '800x600'
|
20
|
+
o.font_size = 14
|
21
|
+
o.output_file = nil
|
22
|
+
o.colors = nil
|
23
|
+
o.theme = nil
|
24
|
+
o.engine = :gruff
|
25
|
+
|
26
|
+
parser = OptionParser.new do |opts|
|
27
|
+
opts.banner = "Usage: perf_plot [options] file1 file2 ..."
|
28
|
+
|
29
|
+
opts.separator ""
|
30
|
+
opts.separator "Options:"
|
31
|
+
|
32
|
+
opts.on("-t", "--title T",
|
33
|
+
"Specify the title for your plot") do |t|
|
34
|
+
o.title = t
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("--only LIST", Array,
|
38
|
+
"Restrict plot to a subset of the benchmarks") do |t|
|
39
|
+
o.selection = t.map{|s| s.to_i}
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("--line",
|
43
|
+
"Plot a line graph") do |t|
|
44
|
+
o.graph_type = :line
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--bar",
|
48
|
+
"Plot a bar graph") do |t|
|
49
|
+
o.graph_type = :bar
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("-w", "--width W", Integer,
|
53
|
+
"Width of the plot (pixels)") do |w|
|
54
|
+
o.graph_width = w
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("-g", "--geometry WxH", /\d+x\d+/,
|
58
|
+
"Specify plot dimensions (pixels)") do |d|
|
59
|
+
o.graph_width = d
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on("-c", "--colors LIST", Array,
|
63
|
+
"Use specified colors for lines/bars") do |t|
|
64
|
+
o.colors = t
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on("--theme NAME",
|
68
|
+
"Use specified theme") do |t|
|
69
|
+
o.theme = t
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on("-e", "--engine ENGINE", [:gruff, :gnuplot],
|
73
|
+
"Select plotting engine: (gruff, gnuplot)") do |e|
|
74
|
+
o.engine = e
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on("-n", "--names LIST", Array,
|
78
|
+
"Use specified names for the legend") do |t|
|
79
|
+
o.names = t
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.on("-l", "--labels LIST", Array,
|
83
|
+
"Use specified labels instead of benchmark names") do |t|
|
84
|
+
o.labels = t
|
85
|
+
end
|
86
|
+
|
87
|
+
opts.on("-o", "--out FILE",
|
88
|
+
"Specify output file") do |f|
|
89
|
+
o.output_file = f
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.on("-f", "--font-size N", Integer,
|
93
|
+
"Overall font size to use in the plot (points)") do |n|
|
94
|
+
o.font_size = n
|
95
|
+
end
|
96
|
+
|
97
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
98
|
+
puts opts
|
99
|
+
exit
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# option compatibility with older versions
|
104
|
+
args=[]
|
105
|
+
ARGV.each do |arg|
|
106
|
+
arg = arg.sub("font_size", "font-size") if arg =~ /^-font_size/
|
107
|
+
arg = "-" + arg if arg =~ /^-(title|width|out|geometry|font-size|names|labels|colors|line|bar|only)/
|
108
|
+
args << arg
|
109
|
+
end
|
110
|
+
|
111
|
+
parser.parse!(args)
|
112
|
+
|
113
|
+
o.output_file ||= o.engine == :gruff ? "graph.png" : "graph.pdf"
|
114
|
+
|
115
|
+
args.each do |arg|
|
116
|
+
o.files << File.open_or_die(arg)
|
117
|
+
o.names[o.files.length-1] ||= File.basename(arg).sub(/\.txt$/, '').sub(/^\d\d-\d\d\.[^\.]+./, '')
|
118
|
+
end
|
119
|
+
|
120
|
+
o.files.length > 0 or die(parser.banner)
|
121
|
+
|
122
|
+
class Plotter
|
123
|
+
attr_reader :o
|
124
|
+
def initialize(options)
|
125
|
+
@o = options
|
126
|
+
setup
|
127
|
+
end
|
128
|
+
|
129
|
+
def setup
|
130
|
+
pi = nil
|
131
|
+
@perf_data = []
|
132
|
+
o.files.each do |file|
|
133
|
+
pi = PerfInfo.new(file)
|
134
|
+
iter = pi.iterations
|
135
|
+
urls = pi.requests_per_key
|
136
|
+
@perf_data << pi.keys.map{ |key| iter*urls/pi.timings_mean(key) }
|
137
|
+
file.close
|
138
|
+
end
|
139
|
+
o.selection = (1..(@perf_data.last.length)).to_a if o.selection.empty?
|
140
|
+
if o.labels.empty?
|
141
|
+
o.labels = pi.keys.restrict_to(o.selection.map{|i| i-1})
|
142
|
+
end
|
143
|
+
@perf_data = @perf_data.map{|d| d.restrict_to(o.selection.map{|i| i-1})}
|
144
|
+
end
|
145
|
+
|
146
|
+
def plot_with_gruff
|
147
|
+
require 'gruff'
|
148
|
+
|
149
|
+
g = o.graph_type == :line ? Gruff::Line.new(o.graph_width) : Gruff::Bar.new(o.graph_width)
|
150
|
+
g.send "theme_#{o.theme}" if o.theme
|
151
|
+
|
152
|
+
# on OS X, ImagMagick can't find it's default font (arial) sometimes, so specify some font
|
153
|
+
#g.font = 'Arial-Normal' if RUBY_PLATFORM =~ /darwin/
|
154
|
+
g.font = 'Helvetica' if RUBY_PLATFORM =~ /darwin/
|
155
|
+
|
156
|
+
g.replace_colors(o.colors) if o.colors
|
157
|
+
g.title = o.title
|
158
|
+
g.sort = false
|
159
|
+
g.legend_font_size = o.font_size
|
160
|
+
g.legend_box_size = o.font_size
|
161
|
+
g.marker_font_size = o.font_size
|
162
|
+
g.minimum_value = 0
|
163
|
+
g.maximum_value = @perf_data.flatten.max.ceil
|
164
|
+
g.labels = o.labels.index_map
|
165
|
+
@perf_data.each_with_index{|d,i| g.data(o.names[i], d)}
|
166
|
+
g.write(o.output_file)
|
167
|
+
end
|
168
|
+
|
169
|
+
def plot_with_gnuplot
|
170
|
+
require 'gnuplot'
|
171
|
+
Gnuplot.open(false) do |gnuplot|
|
172
|
+
Gnuplot::Plot.new(gnuplot) do |plot|
|
173
|
+
plot.terminal "pdf noenhanced color fname 'Helvetica' fsize 5"
|
174
|
+
plot.output o.output_file
|
175
|
+
plot.xlabel "Benchmarks"
|
176
|
+
plot.ylabel "Requests per second"
|
177
|
+
plot.yrange "[0:*]"
|
178
|
+
plot.title o.title
|
179
|
+
plot.style "fill solid 1.0 noborder"
|
180
|
+
plot.style "data #{o.graph_type == :line ? "linespoints" : "histogram"}"
|
181
|
+
plot.style "histogram cluster gap 1"
|
182
|
+
plot.xtics "nomirror rotate by -45"
|
183
|
+
label_specs = []
|
184
|
+
o.labels.each_with_index{ |l,i| label_specs << "\"#{l}\" #{i}" }
|
185
|
+
plot.xtics "(#{label_specs.join(', ')})"
|
186
|
+
plot.key "outside reverse"
|
187
|
+
#plot.boxwidth "0.95"
|
188
|
+
plot.grid "nopolar"
|
189
|
+
plot.grid "noxtics nomxtics ytics nomytics noztics nomztics nox2tics nomx2tics noy2tics nomy2tics nocbtics nomcbtics"
|
190
|
+
plot.grid "back linetype 0 linewidth 0.7"
|
191
|
+
|
192
|
+
@perf_data.each_with_index do |d,i|
|
193
|
+
plot.data << Gnuplot::DataSet.new([[o.names[i]] + d]) do |ds|
|
194
|
+
ds.using = "1 title 1"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def plot
|
202
|
+
send "plot_with_#{o.engine}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
Plotter.new(o).plot
|
207
|
+
|
208
|
+
|
209
|
+
__END__
|
210
|
+
|
211
|
+
# Copyright (C) 2005-2008 Stefan Kaes
|
212
|
+
#
|
213
|
+
# This program is free software; you can redistribute it and/or modify
|
214
|
+
# it under the terms of the GNU General Public License as published by
|
215
|
+
# the Free Software Foundation; either version 2 of the License, or
|
216
|
+
# (at your option) any later version.
|
217
|
+
#
|
218
|
+
# This program is distributed in the hope that it will be useful,
|
219
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
220
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
221
|
+
# GNU General Public License for more details.
|
222
|
+
#
|
223
|
+
# You should have received a copy of the GNU General Public License
|
224
|
+
# along with this program; if not, write to the Free Software
|
225
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
data/script/perf_plot_gc
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'optparse'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
8
|
+
require 'railsbench/gc_info'
|
9
|
+
|
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
|
32
|
+
|
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
|
37
|
+
|
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
|
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
|
86
|
+
end
|
87
|
+
|
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)
|
105
|
+
|
106
|
+
o.gcis = []
|
107
|
+
o.files.each do |file|
|
108
|
+
o.gcis << GCInfo.new(file)
|
109
|
+
file.close
|
110
|
+
end
|
111
|
+
|
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
|
135
|
+
|
136
|
+
def plot_with_gruff
|
137
|
+
require 'gruff'
|
138
|
+
|
139
|
+
g = Gruff::StackedBar.new(o.graph_width)
|
140
|
+
|
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
|
149
|
+
|
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)
|
175
|
+
end
|
176
|
+
|
177
|
+
g.write(o.output_file)
|
178
|
+
end
|
179
|
+
|
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
|
235
|
+
|
236
|
+
Plotter.new(o).plot
|
237
|
+
|
238
|
+
__END__
|
239
|
+
|
240
|
+
# Copyright (C) 2005-2008 Stefan Kaes
|
241
|
+
#
|
242
|
+
# This program is free software; you can redistribute it and/or modify
|
243
|
+
# it under the terms of the GNU General Public License as published by
|
244
|
+
# the Free Software Foundation; either version 2 of the License, or
|
245
|
+
# (at your option) any later version.
|
246
|
+
#
|
247
|
+
# This program is distributed in the hope that it will be useful,
|
248
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
249
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
250
|
+
# GNU General Public License for more details.
|
251
|
+
#
|
252
|
+
# You should have received a copy of the GNU General Public License
|
253
|
+
# along with this program; if not, write to the Free Software
|
254
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|