1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
begin
require 'ruby-prof'
rescue LoadError
$stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.'
exit
end
module ActiveSupport
module Testing
module Performance
DEFAULTS.merge!(
if ARGV.include?('--benchmark')
{ :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time] }
else
{ :min_percent => 0.01,
:metrics => [:process_time, :memory, :objects],
:formats => [:flat, :graph_html, :call_tree, :call_stack] }
end).freeze
protected
def run_gc
GC.start
end
class Profiler < Performer
def initialize(*args)
super
@supported = @metric.measure_mode rescue false
end
def run
return unless @supported
RubyProf.measure_mode = @metric.measure_mode
RubyProf.start
RubyProf.pause
full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
@data = RubyProf.stop
@total = @data.threads.sum(0) { |thread| thread.methods.max.total_time }
end
def record
return unless @supported
klasses = full_profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact
klasses.each do |klass|
fname = output_filename(klass)
FileUtils.mkdir_p(File.dirname(fname))
File.open(fname, 'wb') do |file|
klass.new(@data).print(file, full_profile_options.slice(:min_percent))
end
end
end
protected
def output_filename(printer_class)
suffix =
case printer_class.name.demodulize
when 'FlatPrinter'; 'flat.txt'
when 'FlatPrinterWithLineNumbers'; 'flat_line_numbers.txt'
when 'GraphPrinter'; 'graph.txt'
when 'GraphHtmlPrinter'; 'graph.html'
when 'GraphYamlPrinter'; 'graph.yml'
when 'CallTreePrinter'; 'tree.txt'
when 'CallStackPrinter'; 'stack.html'
when 'DotPrinter'; 'graph.dot'
else printer_class.name.sub(/Printer$/, '').underscore
end
"#{super()}_#{suffix}"
end
end
module Metrics
class Base
def measure_mode
self.class::Mode
end
def profile
RubyProf.resume
yield
ensure
RubyProf.pause
end
protected
# overridden by each implementation
def with_gc_stats
yield
end
end
class ProcessTime < Time
Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME)
def measure
RubyProf.measure_process_time
end
end
class WallTime < Time
Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_TIME)
def measure
RubyProf.measure_wall_time
end
end
class CpuTime < Time
Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME)
def initialize(*args)
# FIXME: yeah my CPU is 2.33 GHz
RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0
super
end
def measure
RubyProf.measure_cpu_time
end
end
class Memory < DigitalInformationUnit
Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
end
class Objects < Amount
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
end
class GcRuns < Amount
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
end
class GcTime < Time
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
end
end
end
end
end
if RUBY_VERSION.between?('1.9.2', '2.0')
require 'active_support/testing/performance/ruby/yarv'
elsif RUBY_VERSION.between?('1.8.6', '1.9')
require 'active_support/testing/performance/ruby/mri'
else
$stderr.puts 'Update your ruby interpreter to be able to run benchmarks.'
exit
end
|