rails-perftest 0.0.1
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/.gitignore +17 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +696 -0
- data/Rakefile +37 -0
- data/bin/perftest +9 -0
- data/lib/generators/rails/app/templates/test/performance/browsing_test.rb +12 -0
- data/lib/generators/rails/performance_test/USAGE +10 -0
- data/lib/generators/rails/performance_test/performance_test_generator.rb +14 -0
- data/lib/generators/rails/performance_test/templates/performance_test.rb +12 -0
- data/lib/rails-perftest.rb +2 -0
- data/lib/rails/performance_test_help.rb +3 -0
- data/lib/rails/perftest/action_controller.rb +3 -0
- data/lib/rails/perftest/action_controller/performance_test.rb +3 -0
- data/lib/rails/perftest/action_dispatch.rb +3 -0
- data/lib/rails/perftest/action_dispatch/performance_test.rb +10 -0
- data/lib/rails/perftest/active_support/testing/performance.rb +271 -0
- data/lib/rails/perftest/active_support/testing/performance/jruby.rb +115 -0
- data/lib/rails/perftest/active_support/testing/performance/rubinius.rb +113 -0
- data/lib/rails/perftest/active_support/testing/performance/ruby.rb +173 -0
- data/lib/rails/perftest/commands.rb +30 -0
- data/lib/rails/perftest/commands/benchmarker.rb +34 -0
- data/lib/rails/perftest/commands/profiler.rb +32 -0
- data/lib/rails/perftest/railtie.rb +20 -0
- data/lib/rails/perftest/railties/testing.tasks +12 -0
- data/lib/rails/perftest/version.rb +5 -0
- data/rails-perftest.gemspec +26 -0
- data/rails-perftest.gemspec.erb +26 -0
- data/test/generators/generators_test_helper.rb +16 -0
- data/test/generators/performance_test_generator_test.rb +18 -0
- data/test/helper.rb +8 -0
- data/test/performance_test.rb +58 -0
- metadata +171 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'rubinius/agent'
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
module Testing
|
5
|
+
module Performance
|
6
|
+
DEFAULTS.merge!(
|
7
|
+
if ARGV.include?('--benchmark')
|
8
|
+
{:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]}
|
9
|
+
else
|
10
|
+
{ :metrics => [:wall_time],
|
11
|
+
:formats => [:flat, :graph] }
|
12
|
+
end).freeze
|
13
|
+
|
14
|
+
protected
|
15
|
+
def run_gc
|
16
|
+
GC.run(true)
|
17
|
+
end
|
18
|
+
|
19
|
+
class Performer; end
|
20
|
+
|
21
|
+
class Profiler < Performer
|
22
|
+
def initialize(*args)
|
23
|
+
super
|
24
|
+
@supported = @metric.is_a?(Metrics::WallTime)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
return unless @supported
|
29
|
+
|
30
|
+
@profiler = Rubinius::Profiler::Instrumenter.new
|
31
|
+
|
32
|
+
@total = time_with_block do
|
33
|
+
@profiler.profile(false) do
|
34
|
+
full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def record
|
40
|
+
return unless @supported
|
41
|
+
|
42
|
+
if(full_profile_options[:formats].include?(:flat))
|
43
|
+
create_path_and_open_file(:flat) do |file|
|
44
|
+
@profiler.show(file)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if(full_profile_options[:formats].include?(:graph))
|
49
|
+
create_path_and_open_file(:graph) do |file|
|
50
|
+
@profiler.show(file)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def create_path_and_open_file(printer_name)
|
57
|
+
fname = "#{output_filename}_#{printer_name}.txt"
|
58
|
+
FileUtils.mkdir_p(File.dirname(fname))
|
59
|
+
File.open(fname, 'wb') do |file|
|
60
|
+
yield(file)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module Metrics
|
66
|
+
class Base
|
67
|
+
attr_reader :loopback
|
68
|
+
|
69
|
+
def profile
|
70
|
+
yield
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
def with_gc_stats
|
75
|
+
@loopback = Rubinius::Agent.loopback
|
76
|
+
GC.run(true)
|
77
|
+
yield
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class WallTime < Time
|
82
|
+
def measure
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Memory < DigitalInformationUnit
|
88
|
+
def measure
|
89
|
+
loopback.get("system.memory.counter.bytes").last
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Objects < Amount
|
94
|
+
def measure
|
95
|
+
loopback.get("system.memory.counter.objects").last
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class GcRuns < Amount
|
100
|
+
def measure
|
101
|
+
loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class GcTime < Time
|
106
|
+
def measure
|
107
|
+
(loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last) / 1000.0
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
begin
|
2
|
+
require 'ruby-prof'
|
3
|
+
rescue LoadError
|
4
|
+
$stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.'
|
5
|
+
raise
|
6
|
+
end
|
7
|
+
|
8
|
+
module ActiveSupport
|
9
|
+
module Testing
|
10
|
+
module Performance
|
11
|
+
DEFAULTS.merge!(
|
12
|
+
if ARGV.include?('--benchmark')
|
13
|
+
{ :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time] }
|
14
|
+
else
|
15
|
+
{ :min_percent => 0.01,
|
16
|
+
:metrics => [:process_time, :memory, :objects],
|
17
|
+
:formats => [:flat, :graph_html, :call_tree, :call_stack] }
|
18
|
+
end).freeze
|
19
|
+
|
20
|
+
protected
|
21
|
+
remove_method :run_gc
|
22
|
+
def run_gc
|
23
|
+
GC.start
|
24
|
+
end
|
25
|
+
|
26
|
+
class Profiler < Performer
|
27
|
+
def initialize(*args)
|
28
|
+
super
|
29
|
+
@supported = @metric.measure_mode rescue false
|
30
|
+
end
|
31
|
+
|
32
|
+
remove_method :run
|
33
|
+
def run
|
34
|
+
return unless @supported
|
35
|
+
|
36
|
+
RubyProf.measure_mode = @metric.measure_mode
|
37
|
+
RubyProf.start
|
38
|
+
RubyProf.pause
|
39
|
+
full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
|
40
|
+
@data = RubyProf.stop
|
41
|
+
@total = @data.threads.sum(0) { |thread| thread.methods.max.total_time }
|
42
|
+
end
|
43
|
+
|
44
|
+
remove_method :record
|
45
|
+
def record
|
46
|
+
return unless @supported
|
47
|
+
|
48
|
+
klasses = full_profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact
|
49
|
+
|
50
|
+
klasses.each do |klass|
|
51
|
+
fname = output_filename(klass)
|
52
|
+
FileUtils.mkdir_p(File.dirname(fname))
|
53
|
+
File.open(fname, 'wb') do |file|
|
54
|
+
klass.new(@data).print(file, full_profile_options.slice(:min_percent))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
def output_filename(printer_class)
|
61
|
+
suffix =
|
62
|
+
case printer_class.name.demodulize
|
63
|
+
when 'FlatPrinter'; 'flat.txt'
|
64
|
+
when 'FlatPrinterWithLineNumbers'; 'flat_line_numbers.txt'
|
65
|
+
when 'GraphPrinter'; 'graph.txt'
|
66
|
+
when 'GraphHtmlPrinter'; 'graph.html'
|
67
|
+
when 'GraphYamlPrinter'; 'graph.yml'
|
68
|
+
when 'CallTreePrinter'; 'tree.txt'
|
69
|
+
when 'CallStackPrinter'; 'stack.html'
|
70
|
+
when 'DotPrinter'; 'graph.dot'
|
71
|
+
else printer_class.name.sub(/Printer$/, '').underscore
|
72
|
+
end
|
73
|
+
|
74
|
+
"#{super()}_#{suffix}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module Metrics
|
79
|
+
class Base
|
80
|
+
def measure_mode
|
81
|
+
self.class::Mode
|
82
|
+
end
|
83
|
+
|
84
|
+
remove_method :profile
|
85
|
+
def profile
|
86
|
+
RubyProf.resume
|
87
|
+
yield
|
88
|
+
ensure
|
89
|
+
RubyProf.pause
|
90
|
+
end
|
91
|
+
|
92
|
+
protected
|
93
|
+
remove_method :with_gc_stats
|
94
|
+
def with_gc_stats
|
95
|
+
GC::Profiler.enable
|
96
|
+
GC.start
|
97
|
+
yield
|
98
|
+
ensure
|
99
|
+
GC::Profiler.disable
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class ProcessTime < Time
|
104
|
+
Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME)
|
105
|
+
|
106
|
+
def measure
|
107
|
+
RubyProf.measure_process_time
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class WallTime < Time
|
112
|
+
Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_TIME)
|
113
|
+
|
114
|
+
def measure
|
115
|
+
RubyProf.measure_wall_time
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class CpuTime < Time
|
120
|
+
Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME)
|
121
|
+
|
122
|
+
def initialize(*args)
|
123
|
+
# FIXME: yeah my CPU is 2.33 GHz
|
124
|
+
RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0
|
125
|
+
super
|
126
|
+
end
|
127
|
+
|
128
|
+
def measure
|
129
|
+
RubyProf.measure_cpu_time
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Memory < DigitalInformationUnit
|
134
|
+
Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
|
135
|
+
|
136
|
+
# Ruby 1.9 + GCdata patch
|
137
|
+
if GC.respond_to?(:malloc_allocated_size)
|
138
|
+
def measure
|
139
|
+
GC.malloc_allocated_size
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Objects < Amount
|
145
|
+
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
|
146
|
+
|
147
|
+
# Ruby 1.9 + GCdata patch
|
148
|
+
if GC.respond_to?(:malloc_allocations)
|
149
|
+
def measure
|
150
|
+
GC.malloc_allocations
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class GcRuns < Amount
|
156
|
+
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
|
157
|
+
|
158
|
+
def measure
|
159
|
+
GC.count
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class GcTime < Time
|
164
|
+
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
|
165
|
+
|
166
|
+
def measure
|
167
|
+
GC::Profiler.total_time
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
ARGV << '--help' if ARGV.empty?
|
2
|
+
|
3
|
+
help_message = <<-EOT
|
4
|
+
Usage: rperf COMMAND [ARGS]
|
5
|
+
|
6
|
+
benchmarker See how fast a piece of code runs
|
7
|
+
profiler Get profile information from a piece of code
|
8
|
+
|
9
|
+
All commands can be run with -h (or --help) for more information.
|
10
|
+
EOT
|
11
|
+
|
12
|
+
command = ARGV.shift
|
13
|
+
|
14
|
+
case command
|
15
|
+
|
16
|
+
when 'benchmarker', 'profiler'
|
17
|
+
require APP_PATH
|
18
|
+
Rails.application.require_environment!
|
19
|
+
require "rails/perftest/commands/#{command}"
|
20
|
+
|
21
|
+
when '-h', '--help'
|
22
|
+
puts help_message
|
23
|
+
else
|
24
|
+
puts "Error: Command '#{command}' not recognized"
|
25
|
+
if %x{rake #{command} --dry-run 2>&1 } && $?.success?
|
26
|
+
puts "Did you mean: `$ rake #{command}` ?\n\n"
|
27
|
+
end
|
28
|
+
puts help_message
|
29
|
+
exit(1)
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'rails/test_help'
|
3
|
+
require 'rails/performance_test_help'
|
4
|
+
|
5
|
+
ARGV.push('--benchmark') # HAX
|
6
|
+
require 'rails/perftest/active_support/testing/performance'
|
7
|
+
ARGV.pop
|
8
|
+
|
9
|
+
def options
|
10
|
+
options = {}
|
11
|
+
defaults = ActiveSupport::Testing::Performance::DEFAULTS
|
12
|
+
|
13
|
+
OptionParser.new do |opt|
|
14
|
+
opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]"
|
15
|
+
opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r }
|
16
|
+
opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o }
|
17
|
+
opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) }
|
18
|
+
opt.parse!(ARGV)
|
19
|
+
end
|
20
|
+
|
21
|
+
options
|
22
|
+
end
|
23
|
+
|
24
|
+
class BenchmarkerTest < ActionDispatch::PerformanceTest #:nodoc:
|
25
|
+
self.profile_options = options
|
26
|
+
|
27
|
+
ARGV.each do |expression|
|
28
|
+
eval <<-RUBY
|
29
|
+
def test_#{expression.parameterize('_')}
|
30
|
+
#{expression}
|
31
|
+
end
|
32
|
+
RUBY
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'rails/test_help'
|
3
|
+
require 'rails/performance_test_help'
|
4
|
+
require 'rails/perftest/active_support/testing/performance'
|
5
|
+
|
6
|
+
def options
|
7
|
+
options = {}
|
8
|
+
defaults = ActiveSupport::Testing::Performance::DEFAULTS
|
9
|
+
|
10
|
+
OptionParser.new do |opt|
|
11
|
+
opt.banner = "Usage: rails profiler 'Ruby.code' 'Ruby.more_code' ... [OPTS]"
|
12
|
+
opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r }
|
13
|
+
opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o }
|
14
|
+
opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) }
|
15
|
+
opt.on('-f', '--formats x,y,z', Array, 'Formats to output to.', "Default: #{defaults[:formats].join(",")}") { |m| options[:formats] = m.map(&:to_sym) }
|
16
|
+
opt.parse!(ARGV)
|
17
|
+
end
|
18
|
+
|
19
|
+
options
|
20
|
+
end
|
21
|
+
|
22
|
+
class ProfilerTest < ActionDispatch::PerformanceTest #:nodoc:
|
23
|
+
self.profile_options = options
|
24
|
+
|
25
|
+
ARGV.each do |expression|
|
26
|
+
eval <<-RUBY
|
27
|
+
def test_#{expression.parameterize('_')}
|
28
|
+
#{expression}
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module PerformanceTest
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
|
7
|
+
rake_tasks do
|
8
|
+
load 'rails/perftest/railties/testing.tasks'
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer "action_dispatch.performance_test" do
|
12
|
+
ActiveSupport.on_load(:action_controller) do
|
13
|
+
require "rails/perftest/action_dispatch"
|
14
|
+
require "rails/perftest/action_controller"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
namespace :test do
|
2
|
+
Rails::SubTestTask.new(benchmark: 'test:prepare') do |t|
|
3
|
+
t.libs << 'test'
|
4
|
+
t.pattern = 'test/performance/**/*_test.rb'
|
5
|
+
t.options = '-- --benchmark'
|
6
|
+
end
|
7
|
+
|
8
|
+
Rails::SubTestTask.new(profile: 'test:prepare') do |t|
|
9
|
+
t.libs << 'test'
|
10
|
+
t.pattern = 'test/performance/**/*_test.rb'
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/rails/perftest/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Yves Senn"]
|
6
|
+
gem.email = ["yves.senn@gmail.com"]
|
7
|
+
gem.description = %q{Rails performance tests (removed from core in Rails 4.0)}
|
8
|
+
gem.summary = %q{ActionDispatch::PerformanceTest, ActiveSupport::Testing::Performance extracted from Rails.}
|
9
|
+
gem.homepage = "https://github.com/rails/rails-perftest"
|
10
|
+
|
11
|
+
gem.files = [".gitignore","Gemfile","LICENSE","README.md","Rakefile","bin/perftest","lib/generators/rails/app/templates/test/performance/browsing_test.rb","lib/generators/rails/performance_test/USAGE","lib/generators/rails/performance_test/performance_test_generator.rb","lib/generators/rails/performance_test/templates/performance_test.rb","lib/rails-perftest.rb","lib/rails/performance_test_help.rb","lib/rails/perftest/action_controller.rb","lib/rails/perftest/action_controller/performance_test.rb","lib/rails/perftest/action_dispatch.rb","lib/rails/perftest/action_dispatch/performance_test.rb","lib/rails/perftest/active_support/testing/performance.rb","lib/rails/perftest/active_support/testing/performance/jruby.rb","lib/rails/perftest/active_support/testing/performance/rubinius.rb","lib/rails/perftest/active_support/testing/performance/ruby.rb","lib/rails/perftest/commands.rb","lib/rails/perftest/commands/benchmarker.rb","lib/rails/perftest/commands/profiler.rb","lib/rails/perftest/railtie.rb","lib/rails/perftest/railties/testing.tasks","lib/rails/perftest/version.rb","rails-perftest.gemspec","rails-perftest.gemspec.erb","test/generators/generators_test_helper.rb","test/generators/performance_test_generator_test.rb","test/helper.rb","test/performance_test.rb"]
|
12
|
+
gem.executables = ["perftest"]
|
13
|
+
gem.test_files = ["test/generators/generators_test_helper.rb","test/generators/performance_test_generator_test.rb","test/helper.rb","test/performance_test.rb"]
|
14
|
+
gem.name = "rails-perftest"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Rails::Perftest::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'ruby-prof'
|
19
|
+
gem.add_development_dependency 'minitest', '>= 3'
|
20
|
+
gem.add_development_dependency 'railties', '~> 4.0.0.beta'
|
21
|
+
gem.add_development_dependency 'activerecord', '~> 4.0.0.beta'
|
22
|
+
gem.add_development_dependency 'activemodel', '~> 4.0.0.beta'
|
23
|
+
gem.add_development_dependency 'actionmailer', '~> 4.0.0.beta'
|
24
|
+
gem.add_development_dependency 'actionpack', '~> 4.0.0.beta'
|
25
|
+
gem.add_development_dependency 'sqlite3', '~> 1.3'
|
26
|
+
end
|