rails-perftest 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE +22 -0
  4. data/README.md +696 -0
  5. data/Rakefile +37 -0
  6. data/bin/perftest +9 -0
  7. data/lib/generators/rails/app/templates/test/performance/browsing_test.rb +12 -0
  8. data/lib/generators/rails/performance_test/USAGE +10 -0
  9. data/lib/generators/rails/performance_test/performance_test_generator.rb +14 -0
  10. data/lib/generators/rails/performance_test/templates/performance_test.rb +12 -0
  11. data/lib/rails-perftest.rb +2 -0
  12. data/lib/rails/performance_test_help.rb +3 -0
  13. data/lib/rails/perftest/action_controller.rb +3 -0
  14. data/lib/rails/perftest/action_controller/performance_test.rb +3 -0
  15. data/lib/rails/perftest/action_dispatch.rb +3 -0
  16. data/lib/rails/perftest/action_dispatch/performance_test.rb +10 -0
  17. data/lib/rails/perftest/active_support/testing/performance.rb +271 -0
  18. data/lib/rails/perftest/active_support/testing/performance/jruby.rb +115 -0
  19. data/lib/rails/perftest/active_support/testing/performance/rubinius.rb +113 -0
  20. data/lib/rails/perftest/active_support/testing/performance/ruby.rb +173 -0
  21. data/lib/rails/perftest/commands.rb +30 -0
  22. data/lib/rails/perftest/commands/benchmarker.rb +34 -0
  23. data/lib/rails/perftest/commands/profiler.rb +32 -0
  24. data/lib/rails/perftest/railtie.rb +20 -0
  25. data/lib/rails/perftest/railties/testing.tasks +12 -0
  26. data/lib/rails/perftest/version.rb +5 -0
  27. data/rails-perftest.gemspec +26 -0
  28. data/rails-perftest.gemspec.erb +26 -0
  29. data/test/generators/generators_test_helper.rb +16 -0
  30. data/test/generators/performance_test_generator_test.rb +18 -0
  31. data/test/helper.rb +8 -0
  32. data/test/performance_test.rb +58 -0
  33. 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,5 @@
1
+ module Rails
2
+ module Perftest
3
+ VERSION = "0.0.1"
4
+ end
5
+ 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