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.
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