rails-perftest 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|