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