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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
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
def with_gc_stats
GC::Profiler.enable
GC.start
yield
ensure
GC::Profiler.disable
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)
# Ruby 1.9 + GCdata patch
if GC.respond_to?(:malloc_allocated_size)
def measure
GC.malloc_allocated_size
end
end
end
class Objects < Amount
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
# Ruby 1.9 + GCdata patch
if GC.respond_to?(:malloc_allocations)
def measure
GC.malloc_allocations
end
end
end
class GcRuns < Amount
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
def measure
GC.count
end
end
class GcTime < Time
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
def measure
GC::Profiler.total_time
end
end
end
end
end
end
|