From 92c0f17d6d2a958d3a6285b0e5408e9e0e7122e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 18 Jun 2010 22:23:11 +0100 Subject: performance tests now working accurately on 1.9, using Ruby with the GCdata patch --- .../lib/active_support/testing/performance.rb | 54 ++++++++-------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index cd628a956d..a124c6e0ca 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -260,16 +260,14 @@ begin end protected - # Ruby 1.9 + extented GC profiler patch - if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) def with_gc_stats - GC.start - GC.disable GC::Profiler.enable + GC.start yield ensure GC::Profiler.disable - GC.enable end # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) @@ -294,7 +292,7 @@ begin end def format(measurement) - if measurement < 2 + if measurement < 1 '%d ms' % (measurement * 1000) else '%.2f sec' % measurement @@ -335,14 +333,10 @@ begin class Memory < Base Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - # Ruby 1.9 + extended GC profiler patch - if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocated_size) def measure - GC.enable - GC.start - kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0 - GC.disable - kb + GC.malloc_allocated_size / 1024.0 end # Ruby 1.8 + ruby-prof wrapper @@ -360,14 +354,10 @@ begin class Objects < Base Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - # Ruby 1.9 + extented GC profiler patch - if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocations) def measure - GC.enable - GC.start - count = GC::Profiler.data.last[:HEAP_TOTAL_OBJECTS] - GC.disable - count + GC.malloc_allocations end # Ruby 1.8 + ruby-prof wrapper @@ -385,14 +375,10 @@ begin class GcRuns < Base Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - # Ruby 1.9 + extented GC profiler patch - if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) + # Ruby 1.9 + if GC.respond_to?(:count) def measure - GC.enable - GC.start - count = GC::Profiler.data.last[:GC_RUNS] - GC.disable - count + GC.count end # Ruby 1.8 + ruby-prof wrapper @@ -410,25 +396,21 @@ begin class GcTime < Base Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - # Ruby 1.9 + extented GC profiler patch - if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) + # Ruby 1.9 with GC::Profiler + if GC.respond_to?(:total_time) def measure - GC.enable - GC.start - sec = GC::Profiler.data.inject(0) { |total, run| total += run[:GC_TIME] } - GC.disable - sec + GC::Profiler.total_time end # Ruby 1.8 + ruby-prof wrapper elsif RubyProf.respond_to?(:measure_gc_time) def measure - RubyProf.measure_gc_time + RubyProf.measure_gc_time / 1000 end end def format(measurement) - '%d ms' % (measurement / 1000) + '%.2f ms' % measurement end end end -- cgit v1.2.3 From aff821508a16245ebc03510ba29c70379718dfb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 10 Aug 2010 18:21:51 +0100 Subject: added support for more printers --- activesupport/lib/active_support/testing/performance.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index a124c6e0ca..f7ddf6421d 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -178,7 +178,7 @@ begin RubyProf.pause profile_options[:runs].to_i.times { run_test(@metric, :profile) } @data = RubyProf.stop - @total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time } + @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } end def report @@ -207,10 +207,14 @@ begin def output_filename(printer_class) suffix = case printer_class.name.demodulize - when 'FlatPrinter'; 'flat.txt' - when 'GraphPrinter'; 'graph.txt' - when 'GraphHtmlPrinter'; 'graph.html' - when 'CallTreePrinter'; 'tree.txt' + 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 -- cgit v1.2.3 From 23be2c613490a06170ae50a72889957b006d36a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Thu, 24 Mar 2011 23:38:17 +0000 Subject: check if RubyProf's WALL_TIME and PROCESS_TIME are available before using them (similarly to other constants) --- activesupport/lib/active_support/testing/performance.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 8c91a061fb..42b58c4f25 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -305,7 +305,7 @@ begin end class ProcessTime < Time - Mode = RubyProf::PROCESS_TIME + Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME) def measure RubyProf.measure_process_time @@ -313,7 +313,7 @@ begin end class WallTime < Time - Mode = RubyProf::WALL_TIME + Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_TIME) def measure RubyProf.measure_wall_time -- cgit v1.2.3 From a6e22229b9501f846185306c49009404b4f8466e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Thu, 24 Mar 2011 23:57:57 +0000 Subject: move "check for ruby-prof" from AD/testing/performance_test to AS/testing/performance, where it is actually required --- .../action_dispatch/testing/performance_test.rb | 22 +- .../lib/active_support/testing/performance.rb | 618 +++++++++++---------- 2 files changed, 319 insertions(+), 321 deletions(-) diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb index e7aeb45fb3..465bf40568 100644 --- a/actionpack/lib/action_dispatch/testing/performance_test.rb +++ b/actionpack/lib/action_dispatch/testing/performance_test.rb @@ -1,17 +1,13 @@ require 'active_support/testing/performance' -begin - module ActionDispatch - # An integration test that runs a code profiler on your test methods. - # Profiling output for combinations of each test method, measurement, and - # output format are written to your tmp/performance directory. - # - # By default, process_time is measured and both flat and graph_html output - # formats are written, so you'll have two output files per test method. - class PerformanceTest < ActionDispatch::IntegrationTest - include ActiveSupport::Testing::Performance - end +module ActionDispatch + # An integration test that runs a code profiler on your test methods. + # Profiling output for combinations of each test method, measurement, and + # output format are written to your tmp/performance directory. + # + # By default, process_time is measured and both flat and graph_html output + # formats are written, so you'll have two output files per test method. + class PerformanceTest < ActionDispatch::IntegrationTest + include ActiveSupport::Testing::Performance end -rescue NameError - $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 42b58c4f25..9e46e5963e 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -1,425 +1,427 @@ +require 'fileutils' +require 'rails/version' +require 'active_support/core_ext/class/delegating_attributes' +require 'active_support/core_ext/string/inflections' + begin require 'ruby-prof' +rescue LoadError + $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." + exit +end - require 'fileutils' - require 'rails/version' - require 'active_support/core_ext/class/delegating_attributes' - require 'active_support/core_ext/string/inflections' - - module ActiveSupport - module Testing - module Performance - DEFAULTS = - if benchmark = ARGV.include?('--benchmark') # HAX for rake test - { :benchmark => true, - :runs => 4, - :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], - :output => 'tmp/performance' } - else - { :benchmark => false, - :runs => 1, - :min_percent => 0.01, - :metrics => [:process_time, :memory, :objects], - :formats => [:flat, :graph_html, :call_tree], - :output => 'tmp/performance' } - end.freeze - - def self.included(base) - base.superclass_delegating_accessor :profile_options - base.profile_options = DEFAULTS - end +module ActiveSupport + module Testing + module Performance + DEFAULTS = + if benchmark = ARGV.include?('--benchmark') # HAX for rake test + { :benchmark => true, + :runs => 4, + :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], + :output => 'tmp/performance' } + else + { :benchmark => false, + :runs => 1, + :min_percent => 0.01, + :metrics => [:process_time, :memory, :objects], + :formats => [:flat, :graph_html, :call_tree], + :output => 'tmp/performance' } + end.freeze + + def self.included(base) + base.superclass_delegating_accessor :profile_options + base.profile_options = DEFAULTS + end - def full_test_name - "#{self.class.name}##{method_name}" - end + def full_test_name + "#{self.class.name}##{method_name}" + end - def run(result) - return if method_name =~ /^default_test$/ + def run(result) + return if method_name =~ /^default_test$/ - yield(self.class::STARTED, name) - @_result = result + yield(self.class::STARTED, name) + @_result = result - run_warmup - if profile_options && metrics = profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - result.add_run - end + run_warmup + if profile_options && metrics = profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) + result.add_run end end - - yield(self.class::FINISHED, name) end - def run_test(metric, mode) - run_callbacks :setup - setup - metric.send(mode) { __send__ @method_name } + yield(self.class::FINISHED, name) + end + + def run_test(metric, mode) + run_callbacks :setup + setup + metric.send(mode) { __send__ @method_name } + rescue ::Test::Unit::AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue StandardError, ScriptError => e + add_error(e) + ensure + begin + teardown + run_callbacks :teardown, :enumerator => :reverse_each rescue ::Test::Unit::AssertionFailedError => e add_failure(e.message, e.backtrace) rescue StandardError, ScriptError => e add_error(e) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError => e - add_error(e) - end end + end - protected - def run_warmup - GC.start + protected + def run_warmup + GC.start - time = Metrics::Time.new - run_test(time, :benchmark) - puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] + time = Metrics::Time.new + run_test(time, :benchmark) + puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - GC.start - end + GC.start + end - def run_profile(metric) - klass = profile_options[:benchmark] ? Benchmarker : Profiler - performer = klass.new(self, metric) + def run_profile(metric) + klass = profile_options[:benchmark] ? Benchmarker : Profiler + performer = klass.new(self, metric) - performer.run - puts performer.report - performer.record - end + performer.run + puts performer.report + performer.record + end - class Performer - delegate :run_test, :profile_options, :full_test_name, :to => :@harness + class Performer + delegate :run_test, :profile_options, :full_test_name, :to => :@harness - def initialize(harness, metric) - @harness, @metric = harness, metric - end + def initialize(harness, metric) + @harness, @metric = harness, metric + end - def report - rate = @total / profile_options[:runs] - '%20s: %s' % [@metric.name, @metric.format(rate)] + def report + rate = @total / profile_options[:runs] + '%20s: %s' % [@metric.name, @metric.format(rate)] + end + + protected + def output_filename + "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" end + end - protected - def output_filename - "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" - end + class Benchmarker < Performer + def run + profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } + @total = @metric.total end - class Benchmarker < Performer - def run - profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } - @total = @metric.total + def record + avg = @metric.total / profile_options[:runs].to_i + now = Time.now.utc.xmlschema + with_output_file do |file| + file.puts "#{avg},#{now},#{environment}" end + end + + def environment + unless defined? @env + app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - def record - avg = @metric.total / profile_options[:runs].to_i - now = Time.now.utc.xmlschema - with_output_file do |file| - file.puts "#{avg},#{now},#{environment}" + rails = Rails::VERSION::STRING + if File.directory?('vendor/rails/.git') + Dir.chdir('vendor/rails') do + rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ + end end + + ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' + ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" + + @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - def environment - unless defined? @env - app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ + @env + end - rails = Rails::VERSION::STRING - if File.directory?('vendor/rails/.git') - Dir.chdir('vendor/rails') do - rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - end - end + protected + HEADER = 'measurement,created_at,app,rails,ruby,platform' - ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' - ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" + def with_output_file + fname = output_filename - @env = [app, rails, ruby, RUBY_PLATFORM] * ',' + if new = !File.exist?(fname) + FileUtils.mkdir_p(File.dirname(fname)) end - @env + File.open(fname, 'ab') do |file| + file.puts(HEADER) if new + yield file + end end - protected - HEADER = 'measurement,created_at,app,rails,ruby,platform' - - def with_output_file - fname = output_filename + def output_filename + "#{super}.csv" + end + end - if new = !File.exist?(fname) - FileUtils.mkdir_p(File.dirname(fname)) - end + class Profiler < Performer + def initialize(*args) + super + @supported = @metric.measure_mode rescue false + end - File.open(fname, 'ab') do |file| - file.puts(HEADER) if new - yield file - end - end + def run + return unless @supported - def output_filename - "#{super}.csv" - end + RubyProf.measure_mode = @metric.measure_mode + RubyProf.start + RubyProf.pause + profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @data = RubyProf.stop + @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } end - class Profiler < Performer - def initialize(*args) + def report + if @supported super - @supported = @metric.measure_mode rescue false + else + '%20s: unsupported' % @metric.name end + end - def run - return unless @supported + def record + return unless @supported - RubyProf.measure_mode = @metric.measure_mode - RubyProf.start - RubyProf.pause - profile_options[:runs].to_i.times { run_test(@metric, :profile) } - @data = RubyProf.stop - @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } - end + klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact - def report - if @supported - super - else - '%20s: unsupported' % @metric.name + 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, profile_options.slice(:min_percent)) end end + end - def record - return unless @supported - - klasses = 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, profile_options.slice(:min_percent)) + 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 - end + + "#{super()}_#{suffix}" 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 + module Metrics + def self.[](name) + const_get(name.to_s.camelize) + rescue NameError + nil end - module Metrics - def self.[](name) - const_get(name.to_s.camelize) - rescue NameError - nil + class Base + attr_reader :total + + def initialize + @total = 0 end - class Base - attr_reader :total + def name + @name ||= self.class.name.demodulize.underscore + end - def initialize - @total = 0 - end + def measure_mode + self.class::Mode + end - def name - @name ||= self.class.name.demodulize.underscore - end + def measure + 0 + end - def measure_mode - self.class::Mode + def benchmark + with_gc_stats do + before = measure + yield + @total += (measure - before) end + end - def measure - 0 - end + def profile + RubyProf.resume + yield + ensure + RubyProf.pause + end - def benchmark - with_gc_stats do - before = measure + protected + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) + def with_gc_stats + GC::Profiler.enable + GC.start yield - @total += (measure - before) + ensure + GC::Profiler.disable end - end - - def profile - RubyProf.resume - yield - ensure - RubyProf.pause - end - protected - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end - - # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) - elsif GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - - else - def with_gc_stats - yield - end + # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) + elsif GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats end - end - class Time < Base - def measure - ::Time.now.to_f - end - - def format(measurement) - if measurement < 1 - '%d ms' % (measurement * 1000) - else - '%.2f sec' % measurement + else + def with_gc_stats + yield end end - end + end - class ProcessTime < Time - Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME) + class Time < Base + def measure + ::Time.now.to_f + end - def measure - RubyProf.measure_process_time + def format(measurement) + if measurement < 1 + '%d ms' % (measurement * 1000) + else + '%.2f sec' % measurement end end + end - class WallTime < Time - Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_TIME) + class ProcessTime < Time + Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME) - def measure - RubyProf.measure_wall_time - end + def measure + RubyProf.measure_process_time end + end - class CpuTime < Time - Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) + class WallTime < Time + Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_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_wall_time + end + end - def measure - RubyProf.measure_cpu_time - 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 - class Memory < Base - Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) + def measure + RubyProf.measure_cpu_time + end + end - # Ruby 1.9 + GCdata patch - if GC.respond_to?(:malloc_allocated_size) - def measure - GC.malloc_allocated_size / 1024.0 - end + class Memory < Base + Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_memory) - def measure - RubyProf.measure_memory / 1024.0 - end + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size / 1024.0 end - def format(measurement) - '%.2f KB' % measurement + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 end end - class Objects < Base - Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) + def format(measurement) + '%.2f KB' % measurement + end + end - # Ruby 1.9 + GCdata patch - if GC.respond_to?(:malloc_allocations) - def measure - GC.malloc_allocations - end + class Objects < Base + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocations) + def measure + GC.malloc_allocations end - def format(measurement) - measurement.to_i.to_s + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations end end - class GcRuns < Base - Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + def format(measurement) + measurement.to_i.to_s + end + end - # Ruby 1.9 - if GC.respond_to?(:count) - def measure - GC.count - end + class GcRuns < Base + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end + # Ruby 1.9 + if GC.respond_to?(:count) + def measure + GC.count end - def format(measurement) - measurement.to_i.to_s + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs end end - class GcTime < Base - Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + def format(measurement) + measurement.to_i.to_s + end + end - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) - def measure - GC::Profiler.total_time - end + class GcTime < Base + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time / 1000 - end + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) + def measure + GC::Profiler.total_time end - def format(measurement) - '%.2f ms' % measurement + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time / 1000 end end + + def format(measurement) + '%.2f ms' % measurement + end end end end end -rescue LoadError end -- cgit v1.2.3 From 6e99bb6acaf30f2b8cfacb82143dc1d1c761d47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 00:02:25 +0000 Subject: ruby-prof only makes sense in an MRI context --- activesupport/lib/active_support/testing/performance.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 9e46e5963e..fe68428a8a 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -3,11 +3,13 @@ require 'rails/version' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/string/inflections' -begin - require 'ruby-prof' -rescue LoadError - $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." - exit +if !defined?(RUBY_ENGINE) or RUBY_ENGINE == "ruby" # MRI 1.8 or 1.9 + begin + require 'ruby-prof' + rescue LoadError + $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." + exit + end end module ActiveSupport -- cgit v1.2.3 From c50df6351cf0d8ff50e62995b1124fd8183eafc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 01:49:21 +0000 Subject: initial separation of RubyProf-specific code --- .../lib/active_support/testing/performance.rb | 223 +------------------ .../lib/active_support/testing/performance/mri.rb | 236 +++++++++++++++++++++ 2 files changed, 241 insertions(+), 218 deletions(-) create mode 100644 activesupport/lib/active_support/testing/performance/mri.rb diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index fe68428a8a..18e9898fe5 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -10,11 +10,13 @@ if !defined?(RUBY_ENGINE) or RUBY_ENGINE == "ruby" # MRI 1.8 or 1.9 $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." exit end + + require 'active_support/testing/performance/mri' end module ActiveSupport module Testing - module Performance + module Performance DEFAULTS = if benchmark = ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, @@ -78,16 +80,6 @@ module ActiveSupport end protected - def run_warmup - GC.start - - time = Metrics::Time.new - run_test(time, :benchmark) - puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - - GC.start - end - def run_profile(metric) klass = profile_options[:benchmark] ? Benchmarker : Profiler performer = klass.new(self, metric) @@ -169,65 +161,7 @@ module ActiveSupport "#{super}.csv" end 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 - profile_options[:runs].to_i.times { run_test(@metric, :profile) } - @data = RubyProf.stop - @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } - end - - def report - if @supported - super - else - '%20s: unsupported' % @metric.name - end - end - - def record - return unless @supported - - klasses = 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, 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 def self.[](name) const_get(name.to_s.camelize) @@ -261,41 +195,8 @@ module ActiveSupport @total += (measure - before) end end - - def profile - RubyProf.resume - yield - ensure - RubyProf.pause - end - - protected - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end - - # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) - elsif GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - - else - def with_gc_stats - yield - end - end end - + class Time < Base def measure ::Time.now.to_f @@ -309,120 +210,6 @@ module ActiveSupport end 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 < Base - 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 / 1024.0 - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_memory) - def measure - RubyProf.measure_memory / 1024.0 - end - end - - def format(measurement) - '%.2f KB' % measurement - end - end - - class Objects < Base - 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 - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end - end - - def format(measurement) - measurement.to_i.to_s - end - end - - class GcRuns < Base - Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - - # Ruby 1.9 - if GC.respond_to?(:count) - def measure - GC.count - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - end - - def format(measurement) - measurement.to_i.to_s - end - end - - class GcTime < Base - Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) - def measure - GC::Profiler.total_time - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time / 1000 - end - end - - def format(measurement) - '%.2f ms' % measurement - end - end end end end diff --git a/activesupport/lib/active_support/testing/performance/mri.rb b/activesupport/lib/active_support/testing/performance/mri.rb new file mode 100644 index 0000000000..cfa48890dc --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/mri.rb @@ -0,0 +1,236 @@ +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 + protected + def run_warmup + GC.start + + time = Metrics::Time.new + run_test(time, :benchmark) + puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] + + GC.start + end + + class Performer; 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 + profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @data = RubyProf.stop + @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } + end + + def report + if @supported + super + else + '%20s: unsupported' % @metric.name + end + end + + def record + return unless @supported + + klasses = 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, 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 profile + RubyProf.resume + yield + ensure + RubyProf.pause + end + + protected + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) + def with_gc_stats + GC::Profiler.enable + GC.start + yield + ensure + GC::Profiler.disable + end + + # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) + elsif GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats + end + + else + def with_gc_stats + yield + end + end + end + + class Time < Base; 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 < Base + 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 / 1024.0 + end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 + end + end + + def format(measurement) + '%.2f KB' % measurement + end + end + + class Objects < Base + 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 + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end + end + + def format(measurement) + measurement.to_i.to_s + end + end + + class GcRuns < Base + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + + # Ruby 1.9 + if GC.respond_to?(:count) + def measure + GC.count + end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + end + + def format(measurement) + measurement.to_i.to_s + end + end + + class GcTime < Base + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) + def measure + GC::Profiler.total_time + end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time / 1000 + end + end + + def format(measurement) + '%.2f ms' % measurement + end + end + end + end + end +end -- cgit v1.2.3 From e57d264f3795537cc3a5be61d766f956f882a5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 02:28:38 +0000 Subject: extracted more generic methods, added a note for methods that need to be overridden by each implementation --- .../lib/active_support/testing/performance.rb | 99 +++++++++++++++++----- .../lib/active_support/testing/performance/mri.rb | 36 ++------ 2 files changed, 87 insertions(+), 48 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 18e9898fe5..db7611c4fa 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -3,17 +3,6 @@ require 'rails/version' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/string/inflections' -if !defined?(RUBY_ENGINE) or RUBY_ENGINE == "ruby" # MRI 1.8 or 1.9 - begin - require 'ruby-prof' - rescue LoadError - $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." - exit - end - - require 'active_support/testing/performance/mri' -end - module ActiveSupport module Testing module Performance @@ -80,6 +69,19 @@ module ActiveSupport end protected + # overridden by each implementation + def run_gc; end + + def run_warmup + run_gc + + time = Metrics::Time.new + run_test(time, :benchmark) + puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] + + run_gc + end + def run_profile(metric) klass = profile_options[:benchmark] ? Benchmarker : Profiler performer = klass.new(self, metric) @@ -106,6 +108,13 @@ module ActiveSupport "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" end end + + # overridden by each implementation + class Profiler < Performer + def run; end + def report; end + def record; end + end class Benchmarker < Performer def run @@ -180,14 +189,6 @@ module ActiveSupport @name ||= self.class.name.demodulize.underscore end - def measure_mode - self.class::Mode - end - - def measure - 0 - end - def benchmark with_gc_stats do before = measure @@ -195,6 +196,13 @@ module ActiveSupport @total += (measure - before) end end + + # overridden by each implementation + def profile; end + + protected + # overridden by each implementation + def with_gc_stats; end end class Time < Base @@ -210,7 +218,60 @@ module ActiveSupport end end end + + class ProcessTime < Time + # overridden by each implementation + def measure; end + end + + class WallTime < Time + # overridden by each implementation + def measure; end + end + + class CpuTime < Time + # overridden by each implementation + def measure; end + end + + class Memory < Base + # overridden by each implementation + def measure; end + + def format(measurement) + '%.2f KB' % measurement + end + end + + class Objects < Base + # overridden by each implementation + def measure; end + + def format(measurement) + measurement.to_i.to_s + end + end + + class GcRuns < Base + # overridden by each implementation + def measure; end + + def format(measurement) + measurement.to_i.to_s + end + end + + class GcTime < Base + # overridden by each implementation + def measure; end + + def format(measurement) + '%.2f ms' % measurement + end + end end end end end + +require 'active_support/testing/performance/mri' diff --git a/activesupport/lib/active_support/testing/performance/mri.rb b/activesupport/lib/active_support/testing/performance/mri.rb index cfa48890dc..914e546090 100644 --- a/activesupport/lib/active_support/testing/performance/mri.rb +++ b/activesupport/lib/active_support/testing/performance/mri.rb @@ -8,17 +8,12 @@ end module ActiveSupport module Testing module Performance + protected - def run_warmup - GC.start - - time = Metrics::Time.new - run_test(time, :benchmark) - puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - + def run_gc GC.start end - + class Performer; end class Profiler < Performer @@ -81,6 +76,10 @@ module ActiveSupport module Metrics class Base + def measure_mode + self.class::Mode + end + def profile RubyProf.resume yield @@ -107,11 +106,6 @@ module ActiveSupport ensure GC.disable_stats end - - else - def with_gc_stats - yield - end end end @@ -162,10 +156,6 @@ module ActiveSupport RubyProf.measure_memory / 1024.0 end end - - def format(measurement) - '%.2f KB' % measurement - end end class Objects < Base @@ -183,10 +173,6 @@ module ActiveSupport RubyProf.measure_allocations end end - - def format(measurement) - measurement.to_i.to_s - end end class GcRuns < Base @@ -204,10 +190,6 @@ module ActiveSupport RubyProf.measure_gc_runs end end - - def format(measurement) - measurement.to_i.to_s - end end class GcTime < Base @@ -225,10 +207,6 @@ module ActiveSupport RubyProf.measure_gc_time / 1000 end end - - def format(measurement) - '%.2f ms' % measurement - end end end end -- cgit v1.2.3 From 11516089212a67e154ec426787feb6f24ed7e129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 02:39:43 +0000 Subject: using number_helper to pretty print some results --- .../lib/active_support/testing/performance.rb | 29 ++++++++++------------ .../lib/active_support/testing/performance/mri.rb | 6 ++--- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index db7611c4fa..0d7d405127 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -2,6 +2,7 @@ require 'fileutils' require 'rails/version' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/string/inflections' +require 'action_view/helpers/number_helper' module ActiveSupport module Testing @@ -179,6 +180,8 @@ module ActiveSupport end class Base + include ActionView::Helpers::NumberHelper + attr_reader :total def initialize @@ -219,6 +222,12 @@ module ActiveSupport end end + class Amount < Base + def format(measurement) + number_with_delimiter(measurement) + end + end + class ProcessTime < Time # overridden by each implementation def measure; end @@ -239,35 +248,23 @@ module ActiveSupport def measure; end def format(measurement) - '%.2f KB' % measurement + number_to_human_size(measurement, :precision => 2) end end - class Objects < Base + class Objects < Amount # overridden by each implementation def measure; end - - def format(measurement) - measurement.to_i.to_s - end end - class GcRuns < Base + class GcRuns < Amount # overridden by each implementation def measure; end - - def format(measurement) - measurement.to_i.to_s - end end - class GcTime < Base + class GcTime < Time # overridden by each implementation def measure; end - - def format(measurement) - '%.2f ms' % measurement - end end end end diff --git a/activesupport/lib/active_support/testing/performance/mri.rb b/activesupport/lib/active_support/testing/performance/mri.rb index 914e546090..859588c735 100644 --- a/activesupport/lib/active_support/testing/performance/mri.rb +++ b/activesupport/lib/active_support/testing/performance/mri.rb @@ -158,7 +158,7 @@ module ActiveSupport end end - class Objects < Base + class Objects < Amount Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) # Ruby 1.9 + GCdata patch @@ -175,7 +175,7 @@ module ActiveSupport end end - class GcRuns < Base + class GcRuns < Amount Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) # Ruby 1.9 @@ -192,7 +192,7 @@ module ActiveSupport end end - class GcTime < Base + class GcTime < Time Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) # Ruby 1.9 with GC::Profiler -- cgit v1.2.3 From 18a81db6fca2994d2c3872b0cb6bfa78bb94b383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 02:46:57 +0000 Subject: check whether the user is using a supported ruby interpreter for benchmarking --- activesupport/lib/active_support/testing/performance.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 0d7d405127..351835553f 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -271,4 +271,10 @@ module ActiveSupport end end -require 'active_support/testing/performance/mri' +RUBY_ENGINE ||= "ruby" +case RUBY_ENGINE + when "ruby" then require 'active_support/testing/performance/mri' + else + $stderr.puts "Your ruby interpreter is not supported for benchmarking." + exit +end -- cgit v1.2.3 From 3ec79e4288df602301e26ae7b2f2b651ff745862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 02:59:11 +0000 Subject: renamed "mri" to "ruby", for consistency with RUBY_ENGINE --- .../lib/active_support/testing/performance.rb | 4 +- .../lib/active_support/testing/performance/mri.rb | 214 --------------------- .../lib/active_support/testing/performance/ruby.rb | 214 +++++++++++++++++++++ .../active_support/testing/performance/ruby/mri.rb | 0 .../testing/performance/ruby/yarv.rb | 0 5 files changed, 216 insertions(+), 216 deletions(-) delete mode 100644 activesupport/lib/active_support/testing/performance/mri.rb create mode 100644 activesupport/lib/active_support/testing/performance/ruby.rb create mode 100644 activesupport/lib/active_support/testing/performance/ruby/mri.rb create mode 100644 activesupport/lib/active_support/testing/performance/ruby/yarv.rb diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 351835553f..a7faf6267a 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -271,9 +271,9 @@ module ActiveSupport end end -RUBY_ENGINE ||= "ruby" +RUBY_ENGINE = "ruby" unless defined?(RUBY_ENGINE) case RUBY_ENGINE - when "ruby" then require 'active_support/testing/performance/mri' + when "ruby" then require 'active_support/testing/performance/ruby' else $stderr.puts "Your ruby interpreter is not supported for benchmarking." exit diff --git a/activesupport/lib/active_support/testing/performance/mri.rb b/activesupport/lib/active_support/testing/performance/mri.rb deleted file mode 100644 index 859588c735..0000000000 --- a/activesupport/lib/active_support/testing/performance/mri.rb +++ /dev/null @@ -1,214 +0,0 @@ -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 - - protected - def run_gc - GC.start - end - - class Performer; 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 - profile_options[:runs].to_i.times { run_test(@metric, :profile) } - @data = RubyProf.stop - @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } - end - - def report - if @supported - super - else - '%20s: unsupported' % @metric.name - end - end - - def record - return unless @supported - - klasses = 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, 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 - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end - - # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) - elsif GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - end - end - - class Time < Base; 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 < Base - 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 / 1024.0 - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_memory) - def measure - RubyProf.measure_memory / 1024.0 - 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 - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end - end - end - - class GcRuns < Amount - Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - - # Ruby 1.9 - if GC.respond_to?(:count) - def measure - GC.count - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - end - end - - class GcTime < Time - Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) - def measure - GC::Profiler.total_time - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time / 1000 - end - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb new file mode 100644 index 0000000000..859588c735 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -0,0 +1,214 @@ +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 + + protected + def run_gc + GC.start + end + + class Performer; 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 + profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @data = RubyProf.stop + @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } + end + + def report + if @supported + super + else + '%20s: unsupported' % @metric.name + end + end + + def record + return unless @supported + + klasses = 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, 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 + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) + def with_gc_stats + GC::Profiler.enable + GC.start + yield + ensure + GC::Profiler.disable + end + + # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) + elsif GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats + end + end + end + + class Time < Base; 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 < Base + 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 / 1024.0 + end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 + 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 + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end + end + end + + class GcRuns < Amount + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + + # Ruby 1.9 + if GC.respond_to?(:count) + def measure + GC.count + end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + end + end + + class GcTime < Time + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) + def measure + GC::Profiler.total_time + end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time / 1000 + end + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb new file mode 100644 index 0000000000..e69de29bb2 -- cgit v1.2.3 From 2fa426c9444dc6dd1b701592cf4d636739556b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 03:26:43 +0000 Subject: divide the ruby interpreter into mri and yarv for improved modularity --- .../lib/active_support/testing/performance/ruby.rb | 93 ++++++---------------- .../active_support/testing/performance/ruby/mri.rb | 60 ++++++++++++++ .../testing/performance/ruby/yarv.rb | 62 +++++++++++++++ 3 files changed, 145 insertions(+), 70 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 859588c735..86287b2d45 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -8,7 +8,6 @@ end module ActiveSupport module Testing module Performance - protected def run_gc GC.start @@ -88,24 +87,9 @@ module ActiveSupport end protected - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) - def with_gc_stats - GC::Profiler.enable - GC.start - yield - ensure - GC::Profiler.disable - end - - # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) - elsif GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end + # overridden by each implementation + def with_gc_stats + yield end end @@ -143,72 +127,41 @@ module ActiveSupport class Memory < Base 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 / 1024.0 - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_memory) - def measure - RubyProf.measure_memory / 1024.0 - end - end + + # overridden by each implementation + def measure; 0; 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 - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end - end + + # overridden by each implementation + def measure; 0; end end class GcRuns < Amount Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - - # Ruby 1.9 - if GC.respond_to?(:count) - def measure - GC.count - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - end + + # overridden by each implementation + def measure; 0; end end class GcTime < Time Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - # Ruby 1.9 with GC::Profiler - if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) - def measure - GC::Profiler.total_time - end - - # Ruby 1.8 + ruby-prof wrapper - elsif RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time / 1000 - end - end + # overridden by each implementation + def measure; 0; end end end end end end + +if RUBY_VERSION >= "1.9.2" + require 'active_support/testing/performance/ruby/yarv' +elsif RUBY_VERSION >= "1.8.6" + require 'active_support/testing/performance/ruby/mri' +else + $stderr.puts "Update your ruby interpreter to be able to run benchmarks." + exit +end diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb index e69de29bb2..d12482b58f 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/mri.rb @@ -0,0 +1,60 @@ +module ActiveSupport + module Testing + module Performance + module Metrics + class Base + protected + # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) + if GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats + end + end + end + + class Memory < Base + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 + end + end + end + + class Amount < Base; end + + class Objects < Amount + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end + end + end + + class GcRuns < Amount + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + end + end + + class GcTime < Time + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time / 1000 + end + end + end + end + end + end +end + + diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index e69de29bb2..1e6fa2f289 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -0,0 +1,62 @@ +module ActiveSupport + module Testing + module Performance + module Metrics + class Base + protected + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) + def with_gc_stats + GC::Profiler.enable + GC.start + yield + ensure + GC::Profiler.disable + end + end + end + + class Memory < Base + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size / 1024.0 + end + end + end + + class Amount < Base; end + + class Objects < Amount + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocations) + def measure + GC.malloc_allocations + end + end + end + + class GcRuns < Amount + # Ruby 1.9 + if GC.respond_to?(:count) + def measure + GC.count + end + end + end + + class GcTime < Time + # Ruby 1.9 with GC::Profiler + if defined?(GC::Profiler) && GC::Profiler.respond_to?(:total_time) + def measure + GC::Profiler.total_time + end + end + end + end + end + end +end + + + -- cgit v1.2.3 From 995d543b6458f84b05bd75464c7c270f1dfb14d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 17:18:06 +0000 Subject: added basic structure for Rubinius --- .../lib/active_support/testing/performance.rb | 7 +-- .../active_support/testing/performance/rubinius.rb | 61 ++++++++++++++++++++++ .../lib/active_support/testing/performance/ruby.rb | 6 +-- 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 activesupport/lib/active_support/testing/performance/rubinius.rb diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index a7faf6267a..ae78fafe78 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -271,10 +271,11 @@ module ActiveSupport end end -RUBY_ENGINE = "ruby" unless defined?(RUBY_ENGINE) +RUBY_ENGINE = 'ruby' unless defined?(RUBY_ENGINE) # mri 1.8 case RUBY_ENGINE - when "ruby" then require 'active_support/testing/performance/ruby' + when 'ruby' then require 'active_support/testing/performance/ruby' + when 'rubinius' then require 'active_support/testing/performance/rubinius' else - $stderr.puts "Your ruby interpreter is not supported for benchmarking." + $stderr.puts 'Your ruby interpreter is not supported for benchmarking.' exit end diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb new file mode 100644 index 0000000000..c6725b988a --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -0,0 +1,61 @@ +module ActiveSupport + module Testing + module Performance + protected + def run_gc + end + + module Metrics + class Base + def profile + yield + end + + protected + def with_gc_stats + yield + end + end + + class Time < Base; end + + class ProcessTime < Time + def measure; 0; end + end + + class WallTime < Time + def measure; 0; end + end + + class CpuTime < Time + def measure; 0; end + end + + class Memory < Base + def measure; 0; end + end + + class Objects < Amount + def measure; 0; end + end + + class GcRuns < Amount + def measure; 0; end + end + + class GcTime < Time + def measure; 0; end + end + end + end + end +end + +if RUBY_VERSION >= '1.9.2' + require 'active_support/testing/performance/ruby/yarv' +elsif RUBY_VERSION >= '1.8.6' + require 'active_support/testing/performance/ruby/mri' +else + $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.' + exit +end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 86287b2d45..f786b85925 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -157,11 +157,11 @@ module ActiveSupport end end -if RUBY_VERSION >= "1.9.2" +if RUBY_VERSION >= '1.9.2' require 'active_support/testing/performance/ruby/yarv' -elsif RUBY_VERSION >= "1.8.6" +elsif RUBY_VERSION >= '1.8.6' require 'active_support/testing/performance/ruby/mri' else - $stderr.puts "Update your ruby interpreter to be able to run benchmarks." + $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.' exit end -- cgit v1.2.3 From b0beba2621a98af3bc00a8f932c330b82b676af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 17:21:32 +0000 Subject: rubinius returns "rbx" for RUBY_ENGINE, not "rubinius" --- activesupport/lib/active_support/testing/performance.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index ae78fafe78..9a759433b9 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -273,8 +273,8 @@ end RUBY_ENGINE = 'ruby' unless defined?(RUBY_ENGINE) # mri 1.8 case RUBY_ENGINE - when 'ruby' then require 'active_support/testing/performance/ruby' - when 'rubinius' then require 'active_support/testing/performance/rubinius' + when 'ruby' then require 'active_support/testing/performance/ruby' + when 'rbx' then require 'active_support/testing/performance/rubinius' else $stderr.puts 'Your ruby interpreter is not supported for benchmarking.' exit -- cgit v1.2.3 From 4fe86a3ea243a16e69bcb406e733fe500fe5ede8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 17:23:39 +0000 Subject: removed leftovers from the copy-paste from mri --- activesupport/lib/active_support/testing/performance/rubinius.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index c6725b988a..f939719d30 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -50,12 +50,3 @@ module ActiveSupport end end end - -if RUBY_VERSION >= '1.9.2' - require 'active_support/testing/performance/ruby/yarv' -elsif RUBY_VERSION >= '1.8.6' - require 'active_support/testing/performance/ruby/mri' -else - $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.' - exit -end -- cgit v1.2.3 From 9e9090f887b61f4dca1cf6ea74cb64e649e95719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 17:27:26 +0000 Subject: added missing require --- activesupport/lib/active_support/testing/performance/rubinius.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index f939719d30..d91337c469 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -1,3 +1,5 @@ +require 'rubinius/agent' + module ActiveSupport module Testing module Performance -- cgit v1.2.3 From cf29d90ef1894d8a3dc7291fd1a2068c5f04f738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 17:59:22 +0000 Subject: added time (wall, process, cpu) to rubinius' benchmarks --- .../lib/active_support/testing/performance/rubinius.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index d91337c469..55fdd9fc9c 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -5,16 +5,20 @@ module ActiveSupport module Performance protected def run_gc + GC.run(true) end - module Metrics - class Base + module Metrics + class Base + # TODO def profile yield end protected + # overridden by each implementation def with_gc_stats + GC.run(true) yield end end @@ -22,14 +26,18 @@ module ActiveSupport class Time < Base; end class ProcessTime < Time + # unsupported def measure; 0; end end class WallTime < Time - def measure; 0; end + def measure + super + end end class CpuTime < Time + # unsupported def measure; 0; end end -- cgit v1.2.3 From c58e063da0358bc30b3d2fb90c4f08fcd32831fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 18:00:29 +0000 Subject: added memory size and allocations to rubinius' benchmarks --- .../lib/active_support/testing/performance/rubinius.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index 55fdd9fc9c..2dd866aa4a 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -14,6 +14,10 @@ module ActiveSupport def profile yield end + + def loopback + @loopback ||= Rubinius::Agent.loopback + end protected # overridden by each implementation @@ -42,11 +46,15 @@ module ActiveSupport end class Memory < Base - def measure; 0; end + def measure + loopback.get("system.memory.counter.bytes").last + end end class Objects < Amount - def measure; 0; end + def measure + loopback.get("system.memory.counter.bytes").last + end end class GcRuns < Amount -- cgit v1.2.3 From 417b2baf0fafc48dbfdab8a81b15836599a0613e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 18:01:39 +0000 Subject: added gc info (runs, time) to rubinius' benchmarks --- activesupport/lib/active_support/testing/performance/rubinius.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index 2dd866aa4a..ef4d2bd493 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -58,11 +58,15 @@ module ActiveSupport end class GcRuns < Amount - def measure; 0; end + def measure + loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last + end end class GcTime < Time - def measure; 0; end + def measure + loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last + end end end end -- cgit v1.2.3 From 278344b3fab67fcc471f475992a86c3748a83e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Fri, 25 Mar 2011 19:03:59 +0000 Subject: fixed time and size units (should be seconds / bytes), fixed typo when counting memory allocations under rubinius --- .../lib/active_support/testing/performance/rubinius.rb | 13 ++++++------- .../lib/active_support/testing/performance/ruby.rb | 2 +- .../lib/active_support/testing/performance/ruby/mri.rb | 4 ++-- .../lib/active_support/testing/performance/ruby/yarv.rb | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index ef4d2bd493..b9c7061571 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -9,19 +9,18 @@ module ActiveSupport end module Metrics - class Base + class Base + attr_reader :loopback + # TODO def profile yield end - - def loopback - @loopback ||= Rubinius::Agent.loopback - end protected # overridden by each implementation def with_gc_stats + @loopback = Rubinius::Agent.loopback GC.run(true) yield end @@ -53,7 +52,7 @@ module ActiveSupport class Objects < Amount def measure - loopback.get("system.memory.counter.bytes").last + loopback.get("system.memory.counter.objects").last end end @@ -65,7 +64,7 @@ module ActiveSupport class GcTime < Time def measure - loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last + (loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last) / 1000.0 end end end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index f786b85925..892baceeb2 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -1,7 +1,7 @@ begin require 'ruby-prof' rescue LoadError - $stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks." + $stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.' exit end diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb index d12482b58f..5fa0269b6a 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/mri.rb @@ -19,7 +19,7 @@ module ActiveSupport # Ruby 1.8 + ruby-prof wrapper if RubyProf.respond_to?(:measure_memory) def measure - RubyProf.measure_memory / 1024.0 + RubyProf.measure_memory end end end @@ -48,7 +48,7 @@ module ActiveSupport # Ruby 1.8 + ruby-prof wrapper if RubyProf.respond_to?(:measure_gc_time) def measure - RubyProf.measure_gc_time / 1000 + RubyProf.measure_gc_time / 1000.0 / 1000.0 end end end diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index 1e6fa2f289..d226e75d0d 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -20,7 +20,7 @@ module ActiveSupport # Ruby 1.9 + GCdata patch if GC.respond_to?(:malloc_allocated_size) def measure - GC.malloc_allocated_size / 1024.0 + GC.malloc_allocated_size end end end -- cgit v1.2.3 From 3872cc4a739db7e0fb5b4a910be371c048667ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Sat, 26 Mar 2011 03:10:00 +0000 Subject: added support for profiling under rubinius --- .../lib/active_support/testing/performance.rb | 9 ++-- .../active_support/testing/performance/rubinius.rb | 58 +++++++++++++++++++++- .../lib/active_support/testing/performance/ruby.rb | 6 +++ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 9a759433b9..bba05d0a51 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -6,9 +6,10 @@ require 'action_view/helpers/number_helper' module ActiveSupport module Testing - module Performance + module Performance + # modified by each implementation DEFAULTS = - if benchmark = ARGV.include?('--benchmark') # HAX for rake test + if ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, :runs => 4, :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], @@ -17,10 +18,8 @@ module ActiveSupport { :benchmark => false, :runs => 1, :min_percent => 0.01, - :metrics => [:process_time, :memory, :objects], - :formats => [:flat, :graph_html, :call_tree], :output => 'tmp/performance' } - end.freeze + end def self.included(base) base.superclass_delegating_accessor :profile_options diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index b9c7061571..8f1047381b 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -3,16 +3,70 @@ require 'rubinius/agent' module ActiveSupport module Testing module Performance + if !ARGV.include?('--benchmark') + DEFAULTS.merge!( + { :metrics => [:wall_time], + :formats => [:flat, :graph] }) + end + protected def run_gc GC.run(true) end + + class Performer; end + + class Profiler < Performer + def initialize(*args) + super + end + + def run + @profiler = Rubinius::Profiler::Instrumenter.new + + @profiler.profile(false) do + profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end + + @total = @profiler.info[:runtime] / 1000 / 1000 / 1000.0 # seconds + end + + def report + super + end + + def record + if(profile_options[:formats].include?(:flat)) + File.open(output_filename('FlatPrinter'), 'wb') do |file| + @profiler.show(file) + end + end + + if(profile_options[:formats].include?(:graph)) + @profiler.set_options({:graph => true}) + File.open(output_filename('GraphPrinter'), 'wb') do |file| + @profiler.show(file) + end + end + end + + protected + def output_filename(printer) + suffix = + case printer + when 'FlatPrinter'; 'flat.txt' + when 'GraphPrinter'; 'graph.txt' + else printer.sub(/Printer$/, '').underscore + end + + "#{super()}_#{suffix}" + end + end module Metrics class Base attr_reader :loopback - - # TODO + def profile yield end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 892baceeb2..13dfebbece 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -8,6 +8,12 @@ end module ActiveSupport module Testing module Performance + if !ARGV.include?('--benchmark') + DEFAULTS.merge!( + { :metrics => [:process_time, :memory, :objects], + :formats => [:flat, :graph_html, :call_tree] }) + end + protected def run_gc GC.start -- cgit v1.2.3 From 84fe2b8b4266f5ef4e3e5f2880a18b8e44a070f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Sat, 26 Mar 2011 19:12:04 +0000 Subject: added basic structure for jruby --- .../lib/active_support/testing/performance.rb | 9 ++- .../active_support/testing/performance/jruby.rb | 70 ++++++++++++++++++++++ .../active_support/testing/performance/rubinius.rb | 7 +-- 3 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 activesupport/lib/active_support/testing/performance/jruby.rb diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index bba05d0a51..0472f44a20 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -111,6 +111,10 @@ module ActiveSupport # overridden by each implementation class Profiler < Performer + def initialize(*args) + super + end + def run; end def report; end def record; end @@ -272,8 +276,9 @@ end RUBY_ENGINE = 'ruby' unless defined?(RUBY_ENGINE) # mri 1.8 case RUBY_ENGINE - when 'ruby' then require 'active_support/testing/performance/ruby' - when 'rbx' then require 'active_support/testing/performance/rubinius' + when 'ruby' then require 'active_support/testing/performance/ruby' + when 'rbx' then require 'active_support/testing/performance/rubinius' + when 'jruby' then require 'active_support/testing/performance/jruby' else $stderr.puts 'Your ruby interpreter is not supported for benchmarking.' exit diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb new file mode 100644 index 0000000000..5985e942f1 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -0,0 +1,70 @@ +module ActiveSupport + module Testing + module Performance + protected + def run_gc + GC.start + end + + class Performer; end + + class Profiler < Performer + def run + end + + def report + end + + def record + end + + protected + def output_filename(printer_class) + end + end + + module Metrics + class Base + def profile + end + + protected + def with_gc_stats + end + + class Time < Base; end + + class ProcessTime < Time + def measure; 0; end + end + + class WallTime < Time + def measure + super + end + end + + class CpuTime < Time + def measure; 0; end + end + + class Memory < Base + def measure; 0; end + end + + class Objects < Amount + def measure; 0; end + end + + class GcRuns < Amount + def measure; 0; end + end + + class GcTime < Time + def measure; 0; end + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index 8f1047381b..c8813a22df 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -16,11 +16,7 @@ module ActiveSupport class Performer; end - class Profiler < Performer - def initialize(*args) - super - end - + class Profiler < Performer def run @profiler = Rubinius::Profiler::Instrumenter.new @@ -72,7 +68,6 @@ module ActiveSupport end protected - # overridden by each implementation def with_gc_stats @loopback = Rubinius::Agent.loopback GC.run(true) -- cgit v1.2.3 From 1a9b1edb49437076b0b9033876a7b7f57d38f4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 00:32:00 +0100 Subject: remove deprecated documentation --- actionpack/lib/action_dispatch/testing/performance_test.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb index 465bf40568..13fe693c32 100644 --- a/actionpack/lib/action_dispatch/testing/performance_test.rb +++ b/actionpack/lib/action_dispatch/testing/performance_test.rb @@ -4,9 +4,6 @@ module ActionDispatch # An integration test that runs a code profiler on your test methods. # Profiling output for combinations of each test method, measurement, and # output format are written to your tmp/performance directory. - # - # By default, process_time is measured and both flat and graph_html output - # formats are written, so you'll have two output files per test method. class PerformanceTest < ActionDispatch::IntegrationTest include ActiveSupport::Testing::Performance end -- cgit v1.2.3 From 93a583eb184f0e3ba21adeab789c7d441ab98a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 00:32:21 +0100 Subject: :min_percent only makes sense in a RubyProf context --- activesupport/lib/active_support/testing/performance.rb | 1 - activesupport/lib/active_support/testing/performance/ruby.rb | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 0472f44a20..ec11c60534 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -17,7 +17,6 @@ module ActiveSupport else { :benchmark => false, :runs => 1, - :min_percent => 0.01, :output => 'tmp/performance' } end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 13dfebbece..dc77f22ba2 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -10,7 +10,8 @@ module ActiveSupport module Performance if !ARGV.include?('--benchmark') DEFAULTS.merge!( - { :metrics => [:process_time, :memory, :objects], + { :min_percent => 0.01, + :metrics => [:process_time, :memory, :objects], :formats => [:flat, :graph_html, :call_tree] }) end -- cgit v1.2.3 From 15bff5a86e916ee250f800b7c67d79fed9d5757b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 00:33:07 +0100 Subject: fix a bug where the GC wasn't being triggered at the start of the MRI benchmarks --- activesupport/lib/active_support/testing/performance/ruby/mri.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb index 5fa0269b6a..512eff30e2 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/mri.rb @@ -8,6 +8,7 @@ module ActiveSupport if GC.respond_to?(:enable_stats) def with_gc_stats GC.enable_stats + GC.start yield ensure GC.disable_stats -- cgit v1.2.3 From ef988e12d8dc4fb82f46f953cac8ad64bbc08c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 00:33:36 +0100 Subject: removed duplication in rubinius' benchmark code --- .../active_support/testing/performance/rubinius.rb | 24 +++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index c8813a22df..67d21e2a43 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -13,8 +13,8 @@ module ActiveSupport def run_gc GC.run(true) end - - class Performer; end + + class Performer; end class Profiler < Performer def run @@ -33,29 +33,25 @@ module ActiveSupport def record if(profile_options[:formats].include?(:flat)) - File.open(output_filename('FlatPrinter'), 'wb') do |file| + create_path_and_open_file(:flat) do |file| @profiler.show(file) end end if(profile_options[:formats].include?(:graph)) - @profiler.set_options({:graph => true}) - File.open(output_filename('GraphPrinter'), 'wb') do |file| + create_path_and_open_file(:graph) do |file| @profiler.show(file) end end end protected - def output_filename(printer) - suffix = - case printer - when 'FlatPrinter'; 'flat.txt' - when 'GraphPrinter'; 'graph.txt' - else printer.sub(/Printer$/, '').underscore - end - - "#{super()}_#{suffix}" + def create_path_and_open_file(printer_name) + fname = "#{output_filename}_#{printer_name}.txt" + FileUtils.mkdir_p(fname) + File.open(fname, 'wb') do |file| + yield(file) + end end end -- cgit v1.2.3 From e17f60879782b3b0d8a4bc2444e1d024c65ec59e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 01:32:32 +0100 Subject: warn the user about unsupported benchmarking metrics he might have asked for --- activesupport/lib/active_support/testing/performance.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index ec11c60534..6a0fc9af35 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -12,7 +12,6 @@ module ActiveSupport if ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, :runs => 4, - :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], :output => 'tmp/performance' } else { :benchmark => false, @@ -41,6 +40,8 @@ module ActiveSupport if klass = Metrics[metric_name.to_sym] run_profile(klass.new) result.add_run + else + puts '%20s: unsupported' % metric_name end end end -- cgit v1.2.3 From e6cb3962bb51aa1f91487117b99e660cc2c43e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 01:32:56 +0100 Subject: benchmarking metrics are also implementation-specific --- .../lib/active_support/testing/performance/jruby.rb | 16 ++++++++++++++-- .../lib/active_support/testing/performance/rubinius.rb | 4 +++- .../lib/active_support/testing/performance/ruby.rb | 4 +++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index 5985e942f1..51c716ef4a 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -1,9 +1,21 @@ +require 'jruby/profiler' +require 'java' +import java.lang.management.ManagementFactory + module ActiveSupport module Testing - module Performance + module Performance + if ARGV.include?('--benchmark') + DEFAULTS.merge!({:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time, :ola]}) + else + DEFAULTS.merge!( + { :metrics => [:wall_time], + :formats => [:flat, :graph] }) + end + protected def run_gc - GC.start + ManagementFactory.memory_mx_bean.gc end class Performer; end diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index 67d21e2a43..bdbb530b37 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -3,7 +3,9 @@ require 'rubinius/agent' module ActiveSupport module Testing module Performance - if !ARGV.include?('--benchmark') + if ARGV.include?('--benchmark') + DEFAULTS.merge!({:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]}) + else DEFAULTS.merge!( { :metrics => [:wall_time], :formats => [:flat, :graph] }) diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index dc77f22ba2..b36b201531 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -8,7 +8,9 @@ end module ActiveSupport module Testing module Performance - if !ARGV.include?('--benchmark') + if ARGV.include?('--benchmark') + DEFAULTS.merge!({:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]}) + else DEFAULTS.merge!( { :min_percent => 0.01, :metrics => [:process_time, :memory, :objects], -- cgit v1.2.3 From d6d059f2807f3cf3d29652b02d5940abd4561bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 01:33:43 +0100 Subject: added profiling and benchmarking capabilities to jruby (profiling is crippled because of a jruby bug) --- .../active_support/testing/performance/jruby.rb | 71 ++++++++++++++++------ 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index 51c716ef4a..6d94dacdd6 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -6,7 +6,7 @@ module ActiveSupport module Testing module Performance if ARGV.include?('--benchmark') - DEFAULTS.merge!({:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time, :ola]}) + DEFAULTS.merge!({:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]}) else DEFAULTS.merge!( { :metrics => [:wall_time], @@ -22,33 +22,59 @@ module ActiveSupport class Profiler < Performer def run + @data = JRuby::Profiler.profile do + profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end + + profile_printer = JRuby::Profiler::GraphProfilePrinter.new(@data) + profile_printer.printProfile(STDOUT) + + @total = @data.getDuration / 1000 / 1000 / 1000.0 # seconds end - + def report + super end - + def record + klasses = profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact + + klasses.each do |klass| + fname = output_filename(klass) + FileUtils.mkdir_p(File.dirname(fname)) + file = File.open(fname, 'wb') do |file| + klass.new(@data).printProfile(file) + end + end end - + protected def output_filename(printer_class) + suffix = + case printer_class.name.demodulize + when 'FlatProfilePrinter'; 'flat.txt' + when 'GraphProfilePrinter'; 'graph.txt' + else printer_class.name.sub(/ProfilePrinter$/, '').underscore + end + + "#{super()}_#{suffix}" end end module Metrics class Base def profile + yield end protected def with_gc_stats + ManagementFactory.memory_mx_bean.gc + yield end + end class Time < Base; end - - class ProcessTime < Time - def measure; 0; end - end class WallTime < Time def measure @@ -57,24 +83,33 @@ module ActiveSupport end class CpuTime < Time - def measure; 0; end + def measure + ManagementFactory.thread_mx_bean.get_current_thread_cpu_time / 1000 / 1000 / 1000.0 # seconds + end end - - class Memory < Base - def measure; 0; end + + class UserTime < Time + def measure + ManagementFactory.thread_mx_bean.get_current_thread_user_time / 1000 / 1000 / 1000.0 # seconds + end end - class Objects < Amount - def measure; 0; end + class Memory < Base + def measure + ManagementFactory.memory_mx_bean.non_heap_memory_usage.used + ManagementFactory.memory_mx_bean.heap_memory_usage.used + end end - + class GcRuns < Amount - def measure; 0; end + def measure + ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_runs, current_gc| total_runs += current_gc.collection_count } + end end class GcTime < Time - def measure; 0; end - end + def measure + ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_time, current_gc| total_time += current_gc.collection_time } / 1000.0 # seconds + end end end end -- cgit v1.2.3 From 983bd01f42a9a3f106c27b37b1074c648263e861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 03:08:08 +0100 Subject: we need test-unit on mri 1.9 --- .../lib/active_support/testing/performance/ruby/yarv.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index d226e75d0d..ed2b8d6803 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -1,3 +1,10 @@ +begin + require 'test-unit' +rescue LoadError + $stderr.puts 'Specify test-unit as application\'s dependency in Gemfile to run benchmarks.' + exit +end + module ActiveSupport module Testing module Performance @@ -57,6 +64,3 @@ module ActiveSupport end end end - - - -- cgit v1.2.3 From c401ad98536b8038bf20f000b41e3f185de0ab7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 03:11:42 +0100 Subject: improved options handling in performance tests --- .../lib/active_support/testing/performance.rb | 25 ++++++++++++---------- .../active_support/testing/performance/jruby.rb | 16 +++++++------- .../active_support/testing/performance/rubinius.rb | 18 ++++++++-------- .../lib/active_support/testing/performance/ruby.rb | 22 +++++++++---------- 4 files changed, 42 insertions(+), 39 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 6a0fc9af35..3195750841 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -7,7 +7,7 @@ require 'action_view/helpers/number_helper' module ActiveSupport module Testing module Performance - # modified by each implementation + # each implementation should define metrics and freeze the defaults DEFAULTS = if ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, @@ -21,13 +21,16 @@ module ActiveSupport def self.included(base) base.superclass_delegating_accessor :profile_options - base.profile_options = DEFAULTS + end + + def full_profile_options + DEFAULTS.merge(profile_options) end def full_test_name "#{self.class.name}##{method_name}" end - + def run(result) return if method_name =~ /^default_test$/ @@ -35,13 +38,13 @@ module ActiveSupport @_result = result run_warmup - if profile_options && metrics = profile_options[:metrics] + if full_profile_options && metrics = full_profile_options[:metrics] metrics.each do |metric_name| if klass = Metrics[metric_name.to_sym] run_profile(klass.new) result.add_run else - puts '%20s: unsupported' % metric_name + puts '%20s: unsupported' % @metric.name end end end @@ -83,7 +86,7 @@ module ActiveSupport end def run_profile(metric) - klass = profile_options[:benchmark] ? Benchmarker : Profiler + klass = full_profile_options[:benchmark] ? Benchmarker : Profiler performer = klass.new(self, metric) performer.run @@ -92,20 +95,20 @@ module ActiveSupport end class Performer - delegate :run_test, :profile_options, :full_test_name, :to => :@harness + delegate :run_test, :full_profile_options, :full_test_name, :to => :@harness def initialize(harness, metric) @harness, @metric = harness, metric end def report - rate = @total / profile_options[:runs] + rate = @total / full_profile_options[:runs] '%20s: %s' % [@metric.name, @metric.format(rate)] end protected def output_filename - "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + "#{full_profile_options[:output]}/#{full_test_name}_#{@metric.name}" end end @@ -122,12 +125,12 @@ module ActiveSupport class Benchmarker < Performer def run - profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } + full_profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } @total = @metric.total end def record - avg = @metric.total / profile_options[:runs].to_i + avg = @metric.total / full_profile_options[:runs].to_i now = Time.now.utc.xmlschema with_output_file do |file| file.puts "#{avg},#{now},#{environment}" diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index 6d94dacdd6..e489c1f0dd 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -5,13 +5,13 @@ import java.lang.management.ManagementFactory module ActiveSupport module Testing module Performance - if ARGV.include?('--benchmark') - DEFAULTS.merge!({:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]}) - else - DEFAULTS.merge!( + DEFAULTS.merge!( + if ARGV.include?('--benchmark') + {:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]} + else { :metrics => [:wall_time], - :formats => [:flat, :graph] }) - end + :formats => [:flat, :graph] } + end).freeze protected def run_gc @@ -23,7 +23,7 @@ module ActiveSupport class Profiler < Performer def run @data = JRuby::Profiler.profile do - profile_options[:runs].to_i.times { run_test(@metric, :profile) } + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } end profile_printer = JRuby::Profiler::GraphProfilePrinter.new(@data) @@ -37,7 +37,7 @@ module ActiveSupport end def record - klasses = profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact + klasses = full_profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact klasses.each do |klass| fname = output_filename(klass) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index bdbb530b37..55b142e20c 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -3,13 +3,13 @@ require 'rubinius/agent' module ActiveSupport module Testing module Performance - if ARGV.include?('--benchmark') - DEFAULTS.merge!({:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]}) - else - DEFAULTS.merge!( + DEFAULTS.merge!( + if ARGV.include?('--benchmark') + {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} + else { :metrics => [:wall_time], - :formats => [:flat, :graph] }) - end + :formats => [:flat, :graph] } + end).freeze protected def run_gc @@ -23,7 +23,7 @@ module ActiveSupport @profiler = Rubinius::Profiler::Instrumenter.new @profiler.profile(false) do - profile_options[:runs].to_i.times { run_test(@metric, :profile) } + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } end @total = @profiler.info[:runtime] / 1000 / 1000 / 1000.0 # seconds @@ -34,13 +34,13 @@ module ActiveSupport end def record - if(profile_options[:formats].include?(:flat)) + if(full_profile_options[:formats].include?(:flat)) create_path_and_open_file(:flat) do |file| @profiler.show(file) end end - if(profile_options[:formats].include?(:graph)) + if(full_profile_options[:formats].include?(:graph)) create_path_and_open_file(:graph) do |file| @profiler.show(file) end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index b36b201531..a8b26b85eb 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -8,14 +8,14 @@ end module ActiveSupport module Testing module Performance - if ARGV.include?('--benchmark') - DEFAULTS.merge!({:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]}) - else - DEFAULTS.merge!( + 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] }) - end + :formats => [:flat, :graph_html, :call_tree] } + end).freeze protected def run_gc @@ -36,7 +36,7 @@ module ActiveSupport RubyProf.measure_mode = @metric.measure_mode RubyProf.start RubyProf.pause - profile_options[:runs].to_i.times { run_test(@metric, :profile) } + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } @data = RubyProf.stop @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } end @@ -52,13 +52,13 @@ module ActiveSupport def record return unless @supported - klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact + 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, profile_options.slice(:min_percent)) + klass.new(@data).print(file, full_profile_options.slice(:min_percent)) end end end @@ -166,9 +166,9 @@ module ActiveSupport end end -if RUBY_VERSION >= '1.9.2' +if RUBY_VERSION.between?('1.9.2', '2.0') require 'active_support/testing/performance/ruby/yarv' -elsif RUBY_VERSION >= '1.8.6' +elsif RUBY_VERSION.between?('1.8.6', '1.9') require 'active_support/testing/performance/ruby/mri' else $stderr.puts 'Update your ruby interpreter to be able to run benchmarks.' -- cgit v1.2.3 From 09bd0eeefc5cb3c2cb26e17b486dd0965afbf4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 04:03:47 +0100 Subject: fixed a couple of hidden bugs --- activesupport/lib/active_support/testing/performance.rb | 3 ++- activesupport/lib/active_support/testing/performance/ruby.rb | 2 +- activesupport/lib/active_support/testing/performance/ruby/yarv.rb | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 3195750841..495fcea381 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -21,6 +21,7 @@ module ActiveSupport def self.included(base) base.superclass_delegating_accessor :profile_options + base.profile_options = {} end def full_profile_options @@ -44,7 +45,7 @@ module ActiveSupport run_profile(klass.new) result.add_run else - puts '%20s: unsupported' % @metric.name + puts '%20s: unsupported' % metric_name end end end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index a8b26b85eb..36dc75ca8f 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -10,7 +10,7 @@ module ActiveSupport module Performance DEFAULTS.merge!( if ARGV.include?('--benchmark') - ({:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} + {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} else { :min_percent => 0.01, :metrics => [:process_time, :memory, :objects], diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index ed2b8d6803..38b6e67767 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -1,5 +1,5 @@ begin - require 'test-unit' + gem 'test-unit' rescue LoadError $stderr.puts 'Specify test-unit as application\'s dependency in Gemfile to run benchmarks.' exit -- cgit v1.2.3 From 726b7ede54031eecfcee34eec80040553e9ad19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 28 Mar 2011 04:19:39 +0100 Subject: improved detection and feedback of supported/unsupported metrics --- .../lib/active_support/testing/performance.rb | 35 ++-------------------- .../active_support/testing/performance/jruby.rb | 4 +-- .../active_support/testing/performance/rubinius.rb | 14 +-------- .../lib/active_support/testing/performance/ruby.rb | 4 +-- .../active_support/testing/performance/ruby/mri.rb | 2 +- .../testing/performance/ruby/yarv.rb | 2 +- 6 files changed, 7 insertions(+), 54 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 495fcea381..f6945e8466 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -235,44 +235,13 @@ module ActiveSupport end end - class ProcessTime < Time - # overridden by each implementation - def measure; end - end - - class WallTime < Time - # overridden by each implementation - def measure; end - end - - class CpuTime < Time - # overridden by each implementation - def measure; end - end - - class Memory < Base - # overridden by each implementation - def measure; end - + class DigitalInformationUnit < Base def format(measurement) number_to_human_size(measurement, :precision => 2) end end - class Objects < Amount - # overridden by each implementation - def measure; end - end - - class GcRuns < Amount - # overridden by each implementation - def measure; end - end - - class GcTime < Time - # overridden by each implementation - def measure; end - end + # each implementation provides its own metrics like ProcessTime, Memory or GcRuns end end end diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index e489c1f0dd..b76c11f12b 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -73,8 +73,6 @@ module ActiveSupport yield end end - - class Time < Base; end class WallTime < Time def measure @@ -94,7 +92,7 @@ module ActiveSupport end end - class Memory < Base + class Memory < DigitalInformationUnit def measure ManagementFactory.memory_mx_bean.non_heap_memory_usage.used + ManagementFactory.memory_mx_bean.heap_memory_usage.used end diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index 55b142e20c..a0511f42ed 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -72,13 +72,6 @@ module ActiveSupport yield end end - - class Time < Base; end - - class ProcessTime < Time - # unsupported - def measure; 0; end - end class WallTime < Time def measure @@ -86,12 +79,7 @@ module ActiveSupport end end - class CpuTime < Time - # unsupported - def measure; 0; end - end - - class Memory < Base + class Memory < DigitalInformationUnit def measure loopback.get("system.memory.counter.bytes").last end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 36dc75ca8f..d08eedc653 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -102,8 +102,6 @@ module ActiveSupport end end - class Time < Base; end - class ProcessTime < Time Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME) @@ -134,7 +132,7 @@ module ActiveSupport end end - class Memory < Base + class Memory < DigitalInformationUnit Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) # overridden by each implementation diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb index 512eff30e2..22c31927f0 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/mri.rb @@ -16,7 +16,7 @@ module ActiveSupport end end - class Memory < Base + class Memory < DigitalInformationUnit # Ruby 1.8 + ruby-prof wrapper if RubyProf.respond_to?(:measure_memory) def measure diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index 38b6e67767..dcbea17a22 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -23,7 +23,7 @@ module ActiveSupport end end - class Memory < Base + class Memory < DigitalInformationUnit # Ruby 1.9 + GCdata patch if GC.respond_to?(:malloc_allocated_size) def measure -- cgit v1.2.3 From be0a2b8f01732bacaa652eff6f286112ace2adca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 29 Mar 2011 22:41:49 +0100 Subject: performance tests inherit from AD::PT and not AC::PT, fixed performance test generator invocation (guide) --- railties/guides/source/performance_testing.textile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index 5679bae531..c9d494b150 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -23,7 +23,7 @@ require 'test_helper' require 'rails/performance_test_help' # Profiling results for each test method are written to tmp/performance. -class BrowsingTest < ActionController::PerformanceTest +class BrowsingTest < ActionDispatch::PerformanceTest def test_homepage get '/' end @@ -34,10 +34,10 @@ This example is a simple performance test case for profiling a GET request to th h4. Generating Performance Tests -Rails provides a generator called +test_unit:performance+ for creating new performance tests: +Rails provides a generator called +performance_test+ for creating new performance tests: -$ rails generate test_unit:performance homepage +$ rails generate performance_test homepage This generates +homepage_test.rb+ in the +test/performance+ directory: @@ -46,7 +46,7 @@ This generates +homepage_test.rb+ in the +test/performance+ directory: require 'test_helper' require 'rails/performance_test_help' -class HomepageTest < ActionController::PerformanceTest +class HomepageTest < ActionDispatch::PerformanceTest # Replace this with your real tests. def test_homepage get '/' @@ -105,7 +105,7 @@ Here's the performance test for +HomeController#dashboard+ and +PostsController# require 'test_helper' require 'rails/performance_test_help' -class PostPerformanceTest < ActionController::PerformanceTest +class PostPerformanceTest < ActionDispatch::PerformanceTest def setup # Application requires logged-in user login_as(:lifo) @@ -133,7 +133,7 @@ Performance test for +Post+ model: require 'test_helper' require 'rails/performance_test_help' -class PostModelTest < ActionController::PerformanceTest +class PostModelTest < ActionDispatch::PerformanceTest def test_creation Post.create :body => 'still fooling you', :cost => '100' end -- cgit v1.2.3 From a35ba6cf5a91b0cde0a22007f8532313754d0cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 29 Mar 2011 23:43:26 +0100 Subject: don't allow decimal places on the amount of objects and gc runs --- activesupport/lib/active_support/testing/performance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index f6945e8466..71d49459ef 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -231,7 +231,7 @@ module ActiveSupport class Amount < Base def format(measurement) - number_with_delimiter(measurement) + number_with_delimiter(measurement.floor) end end -- cgit v1.2.3 From 391ccdaf35f41e38905a940a72e30b484cb0baf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Wed, 30 Mar 2011 00:00:26 +0100 Subject: fixed a bug where rubinius was creating the profiling files as directories --- activesupport/lib/active_support/testing/performance/rubinius.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index a0511f42ed..547b7cf048 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -50,7 +50,7 @@ module ActiveSupport protected def create_path_and_open_file(printer_name) fname = "#{output_filename}_#{printer_name}.txt" - FileUtils.mkdir_p(fname) + FileUtils.mkdir_p(File.dirname(fname)) File.open(fname, 'wb') do |file| yield(file) end -- cgit v1.2.3 From 09cdd1cac149cf8e18f16e39937d659146e61f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Wed, 30 Mar 2011 03:23:16 +0100 Subject: check if there is support for the specified metric when profiling --- .../lib/active_support/testing/performance.rb | 18 ++++++++++++------ .../lib/active_support/testing/performance/jruby.rb | 15 ++++++++++----- .../lib/active_support/testing/performance/rubinius.rb | 15 ++++++++++----- .../lib/active_support/testing/performance/ruby.rb | 8 -------- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 71d49459ef..5e9bc40976 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -10,12 +10,10 @@ module ActiveSupport # each implementation should define metrics and freeze the defaults DEFAULTS = if ARGV.include?('--benchmark') # HAX for rake test - { :benchmark => true, - :runs => 4, + { :runs => 4, :output => 'tmp/performance' } else - { :benchmark => false, - :runs => 1, + { :runs => 1, :output => 'tmp/performance' } end @@ -87,7 +85,7 @@ module ActiveSupport end def run_profile(metric) - klass = full_profile_options[:benchmark] ? Benchmarker : Profiler + klass = ARGV.include?('--benchmark') ? Benchmarker : Profiler performer = klass.new(self, metric) performer.run @@ -117,10 +115,18 @@ module ActiveSupport class Profiler < Performer def initialize(*args) super + @supported = false + end + + def report + if @supported + super + else + '%20s: unsupported' % @metric.name + end end def run; end - def report; end def record; end end diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index b76c11f12b..6993db1a3b 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -10,7 +10,7 @@ module ActiveSupport {:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]} else { :metrics => [:wall_time], - :formats => [:flat, :graph] } + :formats => [:flat, :graph] } end).freeze protected @@ -21,7 +21,14 @@ module ActiveSupport class Performer; end class Profiler < Performer + def initialize(*args) + super + @supported = @metric.is_a?(Metrics::WallTime) + end + def run + return unless @supported + @data = JRuby::Profiler.profile do full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } end @@ -32,11 +39,9 @@ module ActiveSupport @total = @data.getDuration / 1000 / 1000 / 1000.0 # seconds end - def report - super - end - def record + return unless @supported + klasses = full_profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact klasses.each do |klass| diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index 547b7cf048..b2e77b3ce9 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -18,8 +18,15 @@ module ActiveSupport class Performer; end - class Profiler < Performer + class Profiler < Performer + def initialize(*args) + super + @supported = @metric.is_a?(Metrics::WallTime) + end + def run + return unless @supported + @profiler = Rubinius::Profiler::Instrumenter.new @profiler.profile(false) do @@ -29,11 +36,9 @@ module ActiveSupport @total = @profiler.info[:runtime] / 1000 / 1000 / 1000.0 # seconds end - def report - super - end - def record + return unless @supported + if(full_profile_options[:formats].include?(:flat)) create_path_and_open_file(:flat) do |file| @profiler.show(file) diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index d08eedc653..e887af1979 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -41,14 +41,6 @@ module ActiveSupport @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time } end - def report - if @supported - super - else - '%20s: unsupported' % @metric.name - end - end - def record return unless @supported -- cgit v1.2.3 From 6212749bd48d84e79b728b128bc8a0eabb572106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Wed, 30 Mar 2011 03:23:49 +0100 Subject: updated documentation because of the benchmark/profile improvements --- railties/guides/source/performance_testing.textile | 183 +++++++++++++++------ 1 file changed, 134 insertions(+), 49 deletions(-) diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index c9d494b150..ca11fbeec6 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -4,7 +4,7 @@ This guide covers the various ways of performance testing a Ruby on Rails applic * Understand the various types of benchmarking and profiling metrics * Generate performance and benchmarking tests -* Use a GC-patched Ruby binary to measure memory usage and object allocation +* Install and use a GC-patched Ruby binary to measure memory usage and object allocation * Understand the benchmarking information provided by Rails inside the log files * Learn about various tools facilitating benchmarking and profiling @@ -151,7 +151,7 @@ Performance tests can be run in two modes: Benchmarking and Profiling. h5. Benchmarking -Benchmarking helps find out how fast each performance test runs. Each test case is run +4 times+ in benchmarking mode. +Benchmarking makes it easy to quickly gather a few metrics about each test tun. By default, each test case is run +4 times+ in benchmarking mode. To run performance tests in benchmarking mode: @@ -161,7 +161,7 @@ $ rake test:benchmark h5. Profiling -Profiling helps you see the details of a performance test and provide an in-depth picture of the slow and memory hungry parts. Each test case is run +1 time+ in profiling mode. +Profiling allows you to make an in-depth analysis of each of your tests by using an external profiler. Depending on your Ruby interpreter, this profiler can be native (Rubinius, JRuby) or not (MRI, which uses RubyProf). By default, each test case is run +1 time+ in profiling mode. To run performance tests in profiling mode: @@ -171,43 +171,59 @@ $ rake test:profile h4. Metrics -Benchmarking and profiling run performance tests in various modes described below. +Benchmarking and profiling run performance tests and give you multiple metrics. The availability of each metric is determined by the interpreter being used—none of them support all metrics—and by the mode in use. A brief description of each metric and their availability across interpreters/modes is given below. h5. Wall Time Wall time measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system. -Mode: Benchmarking - h5. Process Time Process time measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load. -Mode: Profiling +h5. CPU Time + +Similar to process time, but leverages the more accurate CPU clock counter available on the Pentium and PowerPC platforms. + +h5. User Time + +User time measures the amount of time the CPU spent in user-mode, i.e. within the process. This is not affected by other processes and by the time it possibly spends blocked. h5. Memory Memory measures the amount of memory used for the performance test case. -Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby - h5. Objects Objects measures the number of objects allocated for the performance test case. -Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby - h5. GC Runs GC Runs measures the number of times GC was invoked for the performance test case. -Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby - h5. GC Time GC Time measures the amount of time spent in GC for the performance test case. -Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby +h5. Metric Availability + +h6. Benchmarking + +|_.Interpreter|_.Wall Time|_.Process Time|_.CPU Time|_.User Time|_.Memory|_.Objects|_.GC Runs|_.GC Time| +|_.MRI | yes | yes | yes | no | yes | yes | yes | yes | +|_.REE | yes | yes | yes | no | yes | yes | yes | yes | +|_.Rubinius | yes | no | no | no | yes | yes | yes | yes | +|_.JRuby | yes | no | no | yes | yes | yes | yes | yes | + +h6. Profiling + +|_.Interpreter|_.Wall Time|_.Process Time|_.CPU Time|_.User Time|_.Memory|_.Objects|_.GC Runs|_.GC Time| +|_.MRI | yes | yes | no | no | yes | yes | yes | yes | +|_.REE | yes | yes | no | no | yes | yes | yes | yes | +|_.Rubinius | yes | no | no | no | no | no | no | no | +|_.JRuby | yes | no | no | no | no | no | no | no | + +WARNING: Profiling under JRuby is currently unavailable because of a bug with rake and JRuby's +--profile.api+ option. You should resort exclusively to benchmarking. h4. Understanding the Output @@ -215,7 +231,7 @@ Performance tests generate different outputs inside +tmp/performance+ directory h5(#output-benchmarking). Benchmarking -In benchmarking mode, performance tests generate two types of outputs: +In benchmarking mode, performance tests generate two types of outputs. h6(#output-command-line). Command Line @@ -225,7 +241,7 @@ This is the primary form of output in benchmarking mode. Example: BrowsingTest#test_homepage (31 ms warmup) wall_time: 6 ms memory: 437.27 KB - objects: 5514 + objects: 5,514 gc_runs: 0 gc_time: 19 ms @@ -260,7 +276,7 @@ measurement,created_at,app,rails,ruby,platform h5(#output-profiling). Profiling -In profiling mode, you can choose from four types of output. +In profiling mode, performance tests can generate multiple types of outputs. The command line output is always presented but support for the others is dependant on the interpreter in use. A brief description of each type and their availability across interpreters is given below. h6. Command Line @@ -270,26 +286,69 @@ This is a very basic form of output in profiling mode: BrowsingTest#test_homepage (58 ms warmup) process_time: 63 ms memory: 832.13 KB - objects: 7882 + objects: 7,882 h6. Flat -Flat output shows the total amount of time spent in each method. "Check ruby prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/flat_txt.html. +Flat output shows the metric—time, memory, etc—measure in each method. "Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/flat_txt.html. h6. Graph -Graph output shows how long each method takes to run, which methods call it and which methods it calls. "Check ruby prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/graph_txt.html. +Graph output shows the metric measure in each method, which methods call it and which methods it calls. "Check Ruby-Prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/graph_txt.html. h6. Tree Tree output is profiling information in calltree format for use by "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html and similar tools. +h6. Output Availability + +|_. |_.Flat|_.Graph|_.Tree| +|_.MRI | yes | yes | yes | +|_.REE | yes | yes | yes | +|_.Rubinius | yes | yes | no | +|_.JRuby | yes | yes | no | + +WARNING: Again, profiling under JRuby is currently unavailable because of a bug with rake and JRuby's +--profile.api+ option. + h4. Tuning Test Runs -By default, each performance test is run +4 times+ in benchmarking mode and +1 time+ in profiling. However, test runs can easily be configured. +Test runs can be tuned by setting the +profile_options+ class variable on your test class. + + +require 'test_helper' +require 'rails/performance_test_help' + +# Profiling results for each test method are written to tmp/performance. +class BrowsingTest < ActionDispatch::PerformanceTest + self.profile_options = { :runs => 5, + :metrics => [:wall_time, :memory] } + + def test_homepage + get '/' + end +end + + +In this example, the test would run 5 times and measure wall time and memory. There are a few configurable options: + +|_.Option |_.Description|_.Default|_.Mode| +|+:runs+ |Number of runs.|Benchmarking: 4, Profiling: 1|Both| +|+:output+ |Directory to use when writing the results.|+tmp/performance+|Both| +|+:metrics+ |Metrics to use.|See below.|Both| +|+:formats+ |Formats to output to.|See below.|Profiling| + +Metrics and formats have different defaults depending on the interpreter in use. + +|_.Interpreter|_.Mode|_.Default metrics|_.Default formats| +|/2.MRI/REE |Benchmarking|+:[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| +|Profiling |+:[:process_time, :memory, :objects]+|+[:flat, :graph_html, :call_tree]+| +|/2.Rubinius|Benchmarking|+[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| +|Profiling |+[:wall_time]+|+[:flat, :graph]+| +|/2.JRuby |Benchmarking|+[:wall_time, :user_time, :memory, :gc_runs, :gc_time]+|N/A| +|Profiling |+[:wall_time]+|+[:flat, :graph]+| -WARNING: Performance test configurability is not yet enabled in Rails. But it will be soon. +As you've probably noticed by now, metrics and formats are specified using a symbol array with each name "underscored.":http://api.rubyonrails.org/classes/String.html#method-i-underscore h4. Performance Test Environment @@ -303,41 +362,71 @@ Rails.logger.level = ActiveSupport::BufferedLogger::INFO As +ActionController::Base.perform_caching+ is set to +true+, performance tests will behave much as they do in the +production+ environment. -h4. Installing GC-Patched Ruby +h4. Installing GC-Patched MRI -To get the best from Rails performance tests, you need to build a special Ruby binary with some super powers - "GC patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch for measuring GC Runs/Time and memory/object allocation. +To get the best from Rails' performance tests under MRI, you'll need to build a special Ruby binary with some super powers. -The process is fairly straightforward. If you've never compiled a Ruby binary before, follow these steps to build a ruby binary inside your home directory: +The recommended patches for each MRI version are: -h5. Installation +|_.Version|_.Patch| +|1.8.6|ruby186gc| +|1.8.7|ruby187gc| +|1.9.2 and above|gcdata| -Compile Ruby and apply this "GC Patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch. +All of these can be found on "RVM's _patches_ directory":https://github.com/wayneeseguin/rvm/tree/master/patches/ruby under each specific interpreter version. + +Concerning the installation itself, you can either do this easily by using "RVM":http://rvm.beginrescueend.com or you can build everything from source, which is a little bit harder. + +h5. Install Using RVM + +The process of installing a patched Ruby interpreter is very easy if you let RVM do the hard work. All of the following RVM commands will provide you with a patched Ruby interpreter: + + +$ rvm install 1.9.2-p180 --patch gcdata +$ rvm install 1.8.7 --patch ruby187gc +$ rvm install 1.9.2-p180 --patch ~/Downloads/downloaded_gcdata_patch.patch + -h5. Download and Extract +You can even keep your regular interpreter by assigning a name to the patched one: + + +$ rvm install 1.9.2-p180 --patch gcdata --name gcdata +$ rvm use 1.9.2-p180 # your regular ruby +$ rvm use 1.9.2-p180-gcdata # your patched ruby + + +And it's done! You have installed a patched Ruby interpreter. + +h5. Install From Source + +This process is a bit more complicated, but straightforward nonetheless. If you've never compiled a Ruby binary before, follow these steps to build a Ruby binary inside your home directory. + +h6. Download and Extract $ mkdir rubygc -$ wget +$ wget $ tar -xzvf $ cd -h5. Apply the Patch +h6. Apply the Patch -$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0 +$ curl http://github.com/wayneeseguin/rvm/raw/master/patches/ruby/1.9.2/p180/gcdata.patch | patch -p0 # if you're on 1.9.2! +$ curl http://github.com/wayneeseguin/rvm/raw/master/patches/ruby/1.8.7/ruby187gc.patch | patch -p0 # if you're on 1.8.7! -h5. Configure and Install +h6. Configure and Install -The following will install ruby in your home directory's +/rubygc+ directory. Make sure to replace +<homedir>+ with a full patch to your actual home directory. +The following will install Ruby in your home directory's +/rubygc+ directory. Make sure to replace +<homedir>+ with a full patch to your actual home directory. $ ./configure --prefix=//rubygc $ make && make install -h5. Prepare Aliases +h6. Prepare Aliases For convenience, add the following lines in your +~/.profile+: @@ -349,26 +438,21 @@ alias gcirb='~/rubygc/bin/irb' alias gcrails='~/rubygc/bin/rails' -h5. Install Rubygems and Dependency Gems +Don't forget to use your aliases from now on. -Download "Rubygems":http://rubyforge.org/projects/rubygems and install it from source. Rubygem's README file should have necessary installation instructions. +h6. Install Rubygems (1.8 only!) -Additionally, install the following gems: +Download "Rubygems":http://rubyforge.org/projects/rubygems and install it from source. Rubygem's README file should have necessary installation instructions. Please note that this step isn't necessary if you've installed Ruby 1.9 and above. -* +rake+ -* +rails+ -* +ruby-prof+ -* +rack+ -* +mysql+ +h4. Using Ruby-Prof on MRI and REE -If installing +mysql+ fails, you can try to install it manually: +Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile under MRI or REE: - -$ gcruby extconf.rb --with-mysql-config -$ make && make install - + +gem 'ruby-prof', :path => 'git://github.com/wycats/ruby-prof.git' + -And you're ready to go. Don't forget to use +gcruby+ and +gcrake+ aliases when running the performance tests. +Now run +bundle install+ and you're ready to go. h3. Command Line Tools @@ -517,12 +601,13 @@ h4. Tutorials and Documentation h3. Commercial Products -Rails has been lucky to have two startups dedicated to Rails specific performance tools: +Rails has been lucky to have a few companies dedicated to Rails-specific performance tools. A couple of those are: * "New Relic":http://www.newrelic.com * "Scout":http://scoutapp.com h3. Changelog +* March 30, 2011: Documented the recent improvements (multiple interpreters, options, etc) and necessary adjustments. Other minor improvements. "Gonçalo Silva":http://goncalossilva.com. * January 9, 2009: Complete rewrite by "Pratik":credits.html#lifo * September 6, 2008: Initial version by Matthew Bergman -- cgit v1.2.3 From 677ce63d92b7725b0a11facddc1ba95f50f865df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Sun, 3 Apr 2011 01:09:34 +0100 Subject: profiling enabled under JRuby. Added documentation for workaround described in JRUBY-5650. --- activesupport/lib/active_support/testing/performance/jruby.rb | 7 +------ .../lib/active_support/testing/performance/rubinius.rb | 6 +++--- activesupport/lib/active_support/testing/performance/ruby.rb | 10 ++++------ .../lib/active_support/testing/performance/ruby/mri.rb | 2 -- railties/guides/source/performance_testing.textile | 4 +--- 5 files changed, 9 insertions(+), 20 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index 6993db1a3b..9beadba310 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -16,9 +16,7 @@ module ActiveSupport protected def run_gc ManagementFactory.memory_mx_bean.gc - end - - class Performer; end + end class Profiler < Performer def initialize(*args) @@ -33,9 +31,6 @@ module ActiveSupport full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } end - profile_printer = JRuby::Profiler::GraphProfilePrinter.new(@data) - profile_printer.printProfile(STDOUT) - @total = @data.getDuration / 1000 / 1000 / 1000.0 # seconds end diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index b2e77b3ce9..fafa782e8d 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -84,19 +84,19 @@ module ActiveSupport end end - class Memory < DigitalInformationUnit + class Memory < DigitalInformationUnit def measure loopback.get("system.memory.counter.bytes").last end end - class Objects < Amount + class Objects < Amount def measure loopback.get("system.memory.counter.objects").last end end - class GcRuns < Amount + class GcRuns < Amount def measure loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index e887af1979..0d40dcd22a 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -21,8 +21,6 @@ module ActiveSupport def run_gc GC.start end - - class Performer; end class Profiler < Performer def initialize(*args) @@ -128,28 +126,28 @@ module ActiveSupport Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) # overridden by each implementation - def measure; 0; end + def measure; end end class Objects < Amount Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) # overridden by each implementation - def measure; 0; end + def measure; end end class GcRuns < Amount Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) # overridden by each implementation - def measure; 0; end + def measure; end end class GcTime < Time Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) # overridden by each implementation - def measure; 0; end + def measure; end end end end diff --git a/activesupport/lib/active_support/testing/performance/ruby/mri.rb b/activesupport/lib/active_support/testing/performance/ruby/mri.rb index 22c31927f0..86e650050b 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/mri.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/mri.rb @@ -24,8 +24,6 @@ module ActiveSupport end end end - - class Amount < Base; end class Objects < Amount # Ruby 1.8 + ruby-prof wrapper diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index ca11fbeec6..a515226b9a 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -223,7 +223,7 @@ h6. Profiling |_.Rubinius | yes | no | no | no | no | no | no | no | |_.JRuby | yes | no | no | no | no | no | no | no | -WARNING: Profiling under JRuby is currently unavailable because of a bug with rake and JRuby's +--profile.api+ option. You should resort exclusively to benchmarking. +NOTE: To profile under JRuby you'll need to run +export JRUBY_OPTS="-Xlaunch.inproc=false --profile.api"+ *before* the performance tests. h4. Understanding the Output @@ -309,8 +309,6 @@ h6. Output Availability |_.Rubinius | yes | yes | no | |_.JRuby | yes | yes | no | -WARNING: Again, profiling under JRuby is currently unavailable because of a bug with rake and JRuby's +--profile.api+ option. - h4. Tuning Test Runs Test runs can be tuned by setting the +profile_options+ class variable on your test class. -- cgit v1.2.3 From 65022acda0d407b28249988ab2c715d2a8256702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Sun, 3 Apr 2011 02:09:53 +0100 Subject: removed unused class declaration --- activesupport/lib/active_support/testing/performance/ruby/yarv.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index dcbea17a22..ad0e882214 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -31,8 +31,6 @@ module ActiveSupport end end end - - class Amount < Base; end class Objects < Amount # Ruby 1.9 + GCdata patch -- cgit v1.2.3 From 8d558cb1b069410c8f693295c9c4e2ffc9661e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Sun, 3 Apr 2011 03:16:35 +0100 Subject: don't use internal profiler methods on Rubinius and JRuby since we're only measuring wall clock for now (and the APIs will/can change, respectively) --- activesupport/lib/active_support/testing/performance.rb | 6 ++++++ activesupport/lib/active_support/testing/performance/jruby.rb | 8 ++++---- activesupport/lib/active_support/testing/performance/rubinius.rb | 8 ++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 5e9bc40976..598eb330eb 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -126,6 +126,12 @@ module ActiveSupport end end + def time_with_block + before = Time.now + yield + Time.now - before + end + def run; end def record; end end diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb index 9beadba310..6b27959840 100644 --- a/activesupport/lib/active_support/testing/performance/jruby.rb +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -27,11 +27,11 @@ module ActiveSupport def run return unless @supported - @data = JRuby::Profiler.profile do - full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @total = time_with_block do + @data = JRuby::Profiler.profile do + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end end - - @total = @data.getDuration / 1000 / 1000 / 1000.0 # seconds end def record diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index fafa782e8d..fbb58a2b3a 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -29,11 +29,11 @@ module ActiveSupport @profiler = Rubinius::Profiler::Instrumenter.new - @profiler.profile(false) do - full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @total = time_with_block do + @profiler.profile(false) do + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end end - - @total = @profiler.info[:runtime] / 1000 / 1000 / 1000.0 # seconds end def record -- cgit v1.2.3 From 181c8422d776bb85cb18a7023d3524307a31417c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Sun, 8 May 2011 04:20:14 +0100 Subject: explicitly return from run() under minitest (metrics were printed with the previous solution) --- activesupport/lib/active_support/testing/performance.rb | 2 ++ activesupport/lib/active_support/testing/performance/ruby.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 4db59680b0..4e58b6a564 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -51,6 +51,8 @@ module ActiveSupport end end end + + return end def run_test(metric, mode) diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 0d40dcd22a..3dc668cd92 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -10,7 +10,7 @@ module ActiveSupport module Performance DEFAULTS.merge!( if ARGV.include?('--benchmark') - {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} + { :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time] } else { :min_percent => 0.01, :metrics => [:process_time, :memory, :objects], -- cgit v1.2.3 From a43f95d235190139159dab3cf4863252b0e9bf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 9 May 2011 01:00:04 +0100 Subject: added a note about profiling options to the generators --- .../generators/rails/app/templates/test/performance/browsing_test.rb | 5 ++++- .../generators/test_unit/performance/templates/performance_test.rb | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb index 867fc8c985..5d1be041a5 100644 --- a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb +++ b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb @@ -1,8 +1,11 @@ require 'test_helper' require 'rails/performance_test_help' -# Profiling results for each test method are written to tmp/performance. class BrowsingTest < ActionDispatch::PerformanceTest + # Refer to the documentation for all available options + # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] + # :output => 'tmp/performance', :formats => [:flat] } + def test_homepage get '/' end diff --git a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb index e827aa918f..14a878328b 100644 --- a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb +++ b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb @@ -2,7 +2,10 @@ require 'test_helper' require 'rails/performance_test_help' class <%= class_name %>Test < ActionDispatch::PerformanceTest - # Replace this with your real tests. + # Refer to the documentation for all available options + # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] + # :output => 'tmp/performance', :formats => [:flat] } + def test_homepage get '/' end -- cgit v1.2.3 From 0d1f7584ba25e7289871b5503f010874bc2aef49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 9 May 2011 23:29:00 +0100 Subject: contemplate unsupported metrics --- .../lib/active_support/testing/performance.rb | 34 +++++++++++----------- .../lib/active_support/testing/performance/ruby.rb | 12 -------- .../testing/performance/ruby/yarv.rb | 2 +- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 4e58b6a564..b62d9b16e1 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -142,12 +142,16 @@ module ActiveSupport delegate :run_test, :full_profile_options, :full_test_name, :to => :@harness def initialize(harness, metric) - @harness, @metric = harness, metric + @harness, @metric, @supported = harness, metric, false end def report - rate = @total / full_profile_options[:runs] - '%20s: %s' % [@metric.name, @metric.format(rate)] + if @supported + rate = @total / full_profile_options[:runs] + '%20s: %s' % [@metric.name, @metric.format(rate)] + else + '%20s: unsupported' % @metric.name + end end protected @@ -158,19 +162,6 @@ module ActiveSupport # overridden by each implementation class Profiler < Performer - def initialize(*args) - super - @supported = false - end - - def report - if @supported - super - else - '%20s: unsupported' % @metric.name - end - end - def time_with_block before = Time.now yield @@ -181,8 +172,15 @@ module ActiveSupport def record; end end - class Benchmarker < Performer + class Benchmarker < Performer + def initialize(*args) + super + @supported = @metric.respond_to?('measure') + end + def run + return unless @supported + full_profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } @total = @metric.total end @@ -257,6 +255,8 @@ module ActiveSupport end def benchmark + @unsureturn if measure.nil? + with_gc_stats do before = measure yield diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 3dc668cd92..9f56a412a1 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -124,30 +124,18 @@ module ActiveSupport class Memory < DigitalInformationUnit Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - - # overridden by each implementation - def measure; end end class Objects < Amount Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - - # overridden by each implementation - def measure; end end class GcRuns < Amount Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - - # overridden by each implementation - def measure; end end class GcTime < Time Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - - # overridden by each implementation - def measure; end end end end diff --git a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb index 498bc6b4db..62095a8fe4 100644 --- a/activesupport/lib/active_support/testing/performance/ruby/yarv.rb +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -17,7 +17,7 @@ module ActiveSupport end class Memory < DigitalInformationUnit - # Ruby 1.9 + GCdata patch + # Ruby 1.9 + GCdata patch if GC.respond_to?(:malloc_allocated_size) def measure GC.malloc_allocated_size -- cgit v1.2.3 From 9ca97a6baf1e75c3ef4b69a993e6713b88c09e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 9 May 2011 23:46:07 +0100 Subject: fixed a typo in the guide --- railties/guides/source/performance_testing.textile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index a515226b9a..bbf200e66f 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -339,8 +339,8 @@ In this example, the test would run 5 times and measure wall time and memory. Th Metrics and formats have different defaults depending on the interpreter in use. |_.Interpreter|_.Mode|_.Default metrics|_.Default formats| -|/2.MRI/REE |Benchmarking|+:[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| -|Profiling |+:[:process_time, :memory, :objects]+|+[:flat, :graph_html, :call_tree]+| +|/2.MRI/REE |Benchmarking|+[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| +|Profiling |+[:process_time, :memory, :objects]+|+[:flat, :graph_html, :call_tree]+| |/2.Rubinius|Benchmarking|+[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| |Profiling |+[:wall_time]+|+[:flat, :graph]+| |/2.JRuby |Benchmarking|+[:wall_time, :user_time, :memory, :gc_runs, :gc_time]+|N/A| -- cgit v1.2.3 From 810fb2b5273591ff396ae9bcc1cf78b5d575c33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 10 May 2011 01:38:21 +0100 Subject: benchmarker and profiler now use the new performance testing tools (support for Rubinius and JRuby and high configurability) --- .../lib/active_support/testing/performance.rb | 11 ++-- railties/guides/source/performance_testing.textile | 50 +++++++--------- railties/lib/rails/commands/benchmarker.rb | 47 +++++++++------ railties/lib/rails/commands/profiler.rb | 70 +++++++++------------- 4 files changed, 83 insertions(+), 95 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index b62d9b16e1..4e970c034f 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -23,12 +23,15 @@ module ActiveSupport # each implementation should define metrics and freeze the defaults DEFAULTS = - if ARGV.include?('--benchmark') # HAX for rake test + if ARGV.include?('--benchmark') # HAX for rake test { :runs => 4, - :output => 'tmp/performance' } + :output => 'tmp/performance', + :benchmark => true } else + puts "not" { :runs => 1, - :output => 'tmp/performance' } + :output => 'tmp/performance', + :benchmark => false } end def full_profile_options @@ -130,7 +133,7 @@ module ActiveSupport end def run_profile(metric) - klass = ARGV.include?('--benchmark') ? Benchmarker : Profiler + klass = full_profile_options[:benchmark] ? Benchmarker : Profiler performer = klass.new(self, metric) performer.run diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index bbf200e66f..0f49bffdf7 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -458,55 +458,47 @@ Writing performance test cases could be an overkill when you are looking for one h4. +benchmarker+ -+benchmarker+ is a wrapper around Ruby's "Benchmark":http://ruby-doc.org/core/classes/Benchmark.html standard library. - Usage: -$ rails benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ... +Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS] + -r, --runs N Number of runs. + Default: 4 + -o, --output PATH Directory to use when writing the results. + Default: tmp/performance + -m, --metrics a,b,c Metrics to use. + Default: wall_time,memory,objects,gc_runs,gc_time -Examples: +Example: -$ rails benchmarker 10 'Item.all' 'CouchItem.all' - - -If the +[times]+ argument is omitted, supplied methods are run just once: - - -$ rails benchmarker 'Item.first' 'Item.last' +$ rails benchmarker 'Item.all' 'CouchItem.all' --runs 3 --metrics wall_time,memory h4. +profiler+ -+profiler+ is a wrapper around the "ruby-prof":http://ruby-prof.rubyforge.org gem. - Usage: -$ rails profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html] - - -Examples: - - -$ rails profiler 'Item.all' +Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS] + -r, --runs N Number of runs. + Default: 1 + -o, --output PATH Directory to use when writing the results. + Default: tmp/performance + --metrics a,b,c Metrics to use. + Default: process_time,memory,objects + -m, --formats x,y,z Formats to output to. + Default: flat,graph_html,call_tree -This will profile +Item.all+ in +RubyProf::WALL_TIME+ measure mode. By default, it prints flat output to the shell. +Example: -$ rails profiler 'Item.all' 10 graph +$ rails profiler 'Item.all' 'CouchItem.all' --runs 2 --metrics process_time --formats flat -This will profile +10.times { Item.all }+ with +RubyProf::WALL_TIME+ measure mode and print graph output to the shell. - -If you want to store the output in a file: - - -$ rails profiler 'Item.all' 10 graph 2> graph.txt - +NOTE: Metrics and formats vary from interpreter to interpreter. Pass +--help+ to each tool to see the defaults for your interpreter. h3. Helper Methods diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb index f230f405c0..b06c915ac3 100644 --- a/railties/lib/rails/commands/benchmarker.rb +++ b/railties/lib/rails/commands/benchmarker.rb @@ -1,25 +1,34 @@ -require 'active_support/core_ext/object/inclusion' +require 'optparse' +require 'rails/test_help' +require 'rails/performance_test_help' -if ARGV.first.in?([nil, "-h", "--help"]) - puts "Usage: rails benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..." - exit 1 -end +ARGV.push('--benchmark') # HAX +require 'active_support/testing/performance' +ARGV.pop -begin - N = Integer(ARGV.first) - ARGV.shift -rescue ArgumentError - N = 1 +def options + options = {} + defaults = ActiveSupport::Testing::Performance::DEFAULTS + + OptionParser.new do |opt| + opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]" + opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r } + opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o } + opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) } + opt.parse!(ARGV) + end + + options end -require 'benchmark' -include Benchmark - -# Don't include compilation in the benchmark -ARGV.each { |expression| eval(expression) } - -bm(6) do |x| - ARGV.each_with_index do |expression, idx| - x.report("##{idx + 1}") { N.times { eval(expression) } } +class BenchmarkerTest < ActionDispatch::PerformanceTest + self.profile_options = options + + ARGV.each do |expression| + eval <<-RUBY + def test_#{expression.parameterize('_')} + #{expression} + end + RUBY end end diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb index 7959d8a981..94cf32d32d 100644 --- a/railties/lib/rails/commands/profiler.rb +++ b/railties/lib/rails/commands/profiler.rb @@ -1,48 +1,32 @@ -require 'active_support/core_ext/object/inclusion' +require 'optparse' +require 'rails/test_help' +require 'rails/performance_test_help' +require 'active_support/testing/performance' -if ARGV.first.in?([nil, "-h", "--help"]) - $stderr.puts "Usage: rails profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]" - exit(1) -end - -# Define a method to profile. -if ARGV[1] and ARGV[1].to_i > 1 - eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end" -else - eval "def profile_me() #{ARGV[0]} end" +def options + options = {} + defaults = ActiveSupport::Testing::Performance::DEFAULTS + + OptionParser.new do |opt| + opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]" + opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r } + opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o } + opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) } + opt.on('-f', '--formats x,y,z', Array, 'Formats to output to.', "Default: #{defaults[:formats].join(",")}") { |m| options[:formats] = m.map(&:to_sym) } + opt.parse!(ARGV) + end + + options end -# Use the ruby-prof extension if available. Fall back to stdlib profiler. -begin - begin - require "ruby-prof" - $stderr.puts 'Using the ruby-prof extension.' - RubyProf.measure_mode = RubyProf::WALL_TIME - RubyProf.start - profile_me - results = RubyProf.stop - if ARGV[2] - printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify) - else - printer_class = RubyProf::FlatPrinter - end - printer = printer_class.new(results) - printer.print($stderr) - rescue LoadError - require "prof" - $stderr.puts 'Using the old ruby-prof extension.' - Prof.clock_mode = Prof::GETTIMEOFDAY - Prof.start - profile_me - results = Prof.stop - require 'rubyprof_ext' - Prof.print_profile(results, $stderr) +class ProfilerTest < ActionDispatch::PerformanceTest + self.profile_options = options + + ARGV.each do |expression| + eval <<-RUBY + def test_#{expression.parameterize('_')} + #{expression} + end + RUBY end -rescue LoadError - require 'profiler' - $stderr.puts 'Using the standard Ruby profiler.' - Profiler__.start_profile - profile_me - Profiler__.stop_profile - Profiler__.print_profile($stderr) end -- cgit v1.2.3 From dcc99e23cddab727fec2f7026bc30be18b07cee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 10 May 2011 02:33:01 +0100 Subject: benchmarker and profiler now use the new performance testing tools (support for Rubinius and JRuby and high configurability) --- .../lib/active_support/testing/performance.rb | 10 ++-- railties/guides/source/performance_testing.textile | 50 +++++++--------- railties/lib/rails/commands/benchmarker.rb | 47 +++++++++------ railties/lib/rails/commands/profiler.rb | 70 +++++++++------------- 4 files changed, 82 insertions(+), 95 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index b62d9b16e1..f0287b0440 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -23,12 +23,14 @@ module ActiveSupport # each implementation should define metrics and freeze the defaults DEFAULTS = - if ARGV.include?('--benchmark') # HAX for rake test + if ARGV.include?('--benchmark') # HAX for rake test { :runs => 4, - :output => 'tmp/performance' } + :output => 'tmp/performance', + :benchmark => true } else { :runs => 1, - :output => 'tmp/performance' } + :output => 'tmp/performance', + :benchmark => false } end def full_profile_options @@ -130,7 +132,7 @@ module ActiveSupport end def run_profile(metric) - klass = ARGV.include?('--benchmark') ? Benchmarker : Profiler + klass = full_profile_options[:benchmark] ? Benchmarker : Profiler performer = klass.new(self, metric) performer.run diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index bbf200e66f..0f49bffdf7 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -458,55 +458,47 @@ Writing performance test cases could be an overkill when you are looking for one h4. +benchmarker+ -+benchmarker+ is a wrapper around Ruby's "Benchmark":http://ruby-doc.org/core/classes/Benchmark.html standard library. - Usage: -$ rails benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ... +Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS] + -r, --runs N Number of runs. + Default: 4 + -o, --output PATH Directory to use when writing the results. + Default: tmp/performance + -m, --metrics a,b,c Metrics to use. + Default: wall_time,memory,objects,gc_runs,gc_time -Examples: +Example: -$ rails benchmarker 10 'Item.all' 'CouchItem.all' - - -If the +[times]+ argument is omitted, supplied methods are run just once: - - -$ rails benchmarker 'Item.first' 'Item.last' +$ rails benchmarker 'Item.all' 'CouchItem.all' --runs 3 --metrics wall_time,memory h4. +profiler+ -+profiler+ is a wrapper around the "ruby-prof":http://ruby-prof.rubyforge.org gem. - Usage: -$ rails profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html] - - -Examples: - - -$ rails profiler 'Item.all' +Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS] + -r, --runs N Number of runs. + Default: 1 + -o, --output PATH Directory to use when writing the results. + Default: tmp/performance + --metrics a,b,c Metrics to use. + Default: process_time,memory,objects + -m, --formats x,y,z Formats to output to. + Default: flat,graph_html,call_tree -This will profile +Item.all+ in +RubyProf::WALL_TIME+ measure mode. By default, it prints flat output to the shell. +Example: -$ rails profiler 'Item.all' 10 graph +$ rails profiler 'Item.all' 'CouchItem.all' --runs 2 --metrics process_time --formats flat -This will profile +10.times { Item.all }+ with +RubyProf::WALL_TIME+ measure mode and print graph output to the shell. - -If you want to store the output in a file: - - -$ rails profiler 'Item.all' 10 graph 2> graph.txt - +NOTE: Metrics and formats vary from interpreter to interpreter. Pass +--help+ to each tool to see the defaults for your interpreter. h3. Helper Methods diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb index f230f405c0..b06c915ac3 100644 --- a/railties/lib/rails/commands/benchmarker.rb +++ b/railties/lib/rails/commands/benchmarker.rb @@ -1,25 +1,34 @@ -require 'active_support/core_ext/object/inclusion' +require 'optparse' +require 'rails/test_help' +require 'rails/performance_test_help' -if ARGV.first.in?([nil, "-h", "--help"]) - puts "Usage: rails benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..." - exit 1 -end +ARGV.push('--benchmark') # HAX +require 'active_support/testing/performance' +ARGV.pop -begin - N = Integer(ARGV.first) - ARGV.shift -rescue ArgumentError - N = 1 +def options + options = {} + defaults = ActiveSupport::Testing::Performance::DEFAULTS + + OptionParser.new do |opt| + opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]" + opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r } + opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o } + opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) } + opt.parse!(ARGV) + end + + options end -require 'benchmark' -include Benchmark - -# Don't include compilation in the benchmark -ARGV.each { |expression| eval(expression) } - -bm(6) do |x| - ARGV.each_with_index do |expression, idx| - x.report("##{idx + 1}") { N.times { eval(expression) } } +class BenchmarkerTest < ActionDispatch::PerformanceTest + self.profile_options = options + + ARGV.each do |expression| + eval <<-RUBY + def test_#{expression.parameterize('_')} + #{expression} + end + RUBY end end diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb index 7959d8a981..94cf32d32d 100644 --- a/railties/lib/rails/commands/profiler.rb +++ b/railties/lib/rails/commands/profiler.rb @@ -1,48 +1,32 @@ -require 'active_support/core_ext/object/inclusion' +require 'optparse' +require 'rails/test_help' +require 'rails/performance_test_help' +require 'active_support/testing/performance' -if ARGV.first.in?([nil, "-h", "--help"]) - $stderr.puts "Usage: rails profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]" - exit(1) -end - -# Define a method to profile. -if ARGV[1] and ARGV[1].to_i > 1 - eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end" -else - eval "def profile_me() #{ARGV[0]} end" +def options + options = {} + defaults = ActiveSupport::Testing::Performance::DEFAULTS + + OptionParser.new do |opt| + opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]" + opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r } + opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o } + opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) } + opt.on('-f', '--formats x,y,z', Array, 'Formats to output to.', "Default: #{defaults[:formats].join(",")}") { |m| options[:formats] = m.map(&:to_sym) } + opt.parse!(ARGV) + end + + options end -# Use the ruby-prof extension if available. Fall back to stdlib profiler. -begin - begin - require "ruby-prof" - $stderr.puts 'Using the ruby-prof extension.' - RubyProf.measure_mode = RubyProf::WALL_TIME - RubyProf.start - profile_me - results = RubyProf.stop - if ARGV[2] - printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify) - else - printer_class = RubyProf::FlatPrinter - end - printer = printer_class.new(results) - printer.print($stderr) - rescue LoadError - require "prof" - $stderr.puts 'Using the old ruby-prof extension.' - Prof.clock_mode = Prof::GETTIMEOFDAY - Prof.start - profile_me - results = Prof.stop - require 'rubyprof_ext' - Prof.print_profile(results, $stderr) +class ProfilerTest < ActionDispatch::PerformanceTest + self.profile_options = options + + ARGV.each do |expression| + eval <<-RUBY + def test_#{expression.parameterize('_')} + #{expression} + end + RUBY end -rescue LoadError - require 'profiler' - $stderr.puts 'Using the standard Ruby profiler.' - Profiler__.start_profile - profile_me - Profiler__.stop_profile - Profiler__.print_profile($stderr) end -- cgit v1.2.3 From 0304d8076adefe63b07bbc7e9f464f99ac24ca59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 10 May 2011 02:49:32 +0100 Subject: ruby-prof's awesome call stack printer is now used by default --- activesupport/lib/active_support/testing/performance/ruby.rb | 2 +- railties/guides/source/performance_testing.textile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 9f56a412a1..b29ec6719c 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -14,7 +14,7 @@ module ActiveSupport else { :min_percent => 0.01, :metrics => [:process_time, :memory, :objects], - :formats => [:flat, :graph_html, :call_tree] } + :formats => [:flat, :graph_html, :call_tree, :call_stack] } end).freeze protected diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile index 0f49bffdf7..2b79237c59 100644 --- a/railties/guides/source/performance_testing.textile +++ b/railties/guides/source/performance_testing.textile @@ -340,7 +340,7 @@ Metrics and formats have different defaults depending on the interpreter in use. |_.Interpreter|_.Mode|_.Default metrics|_.Default formats| |/2.MRI/REE |Benchmarking|+[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| -|Profiling |+[:process_time, :memory, :objects]+|+[:flat, :graph_html, :call_tree]+| +|Profiling |+[:process_time, :memory, :objects]+|+[:flat, :graph_html, :call_tree, :call_stack]+| |/2.Rubinius|Benchmarking|+[:wall_time, :memory, :objects, :gc_runs, :gc_time]+|N/A| |Profiling |+[:wall_time]+|+[:flat, :graph]+| |/2.JRuby |Benchmarking|+[:wall_time, :user_time, :memory, :gc_runs, :gc_time]+|N/A| -- cgit v1.2.3 From aaa85cde6068f4db6fabe7bfef664f73bbc6938f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 10 May 2011 02:52:10 +0100 Subject: remove forgotten puts --- activesupport/lib/active_support/testing/performance.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 4e970c034f..f0287b0440 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -28,7 +28,6 @@ module ActiveSupport :output => 'tmp/performance', :benchmark => true } else - puts "not" { :runs => 1, :output => 'tmp/performance', :benchmark => false } -- cgit v1.2.3 From 7fcf0ca69e18841be9fb0dcce2721f4ed2c8eca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Tue, 10 May 2011 03:40:44 +0100 Subject: removed redundant metric support check (and print when it isn't) --- activesupport/lib/active_support/testing/performance.rb | 6 +++--- activesupport/lib/active_support/testing/performance/rubinius.rb | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index f0287b0440..02c19448fd 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -90,6 +90,8 @@ module ActiveSupport if klass = Metrics[metric_name.to_sym] run_profile(klass.new) result.add_run + else + puts '%20s: unsupported' % metric_name end end end @@ -256,9 +258,7 @@ module ActiveSupport @name ||= self.class.name.demodulize.underscore end - def benchmark - @unsureturn if measure.nil? - + def benchmark with_gc_stats do before = measure yield diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb index fbb58a2b3a..198d235548 100644 --- a/activesupport/lib/active_support/testing/performance/rubinius.rb +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -84,19 +84,19 @@ module ActiveSupport end end - class Memory < DigitalInformationUnit + class Memory < DigitalInformationUnit def measure loopback.get("system.memory.counter.bytes").last end end - class Objects < Amount + class Objects < Amount def measure loopback.get("system.memory.counter.objects").last end end - class GcRuns < Amount + class GcRuns < Amount def measure loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last end -- cgit v1.2.3