diff options
author | Carlhuda <carlhuda@engineyard.com> | 2009-12-02 20:01:01 -0800 |
---|---|---|
committer | Carlhuda <carlhuda@engineyard.com> | 2009-12-02 20:01:08 -0800 |
commit | c1304098cca8a9247a9ad1461a1a343354650843 (patch) | |
tree | 41bd12bcca3ce2062b10c043663a72662796cbd1 /activesupport/lib/active_support/testing/performance.rb | |
parent | 399909b11c094ab32542d300c72940b1b263b8e6 (diff) | |
download | rails-c1304098cca8a9247a9ad1461a1a343354650843.tar.gz rails-c1304098cca8a9247a9ad1461a1a343354650843.tar.bz2 rails-c1304098cca8a9247a9ad1461a1a343354650843.zip |
Reorganize autoloads:
* A new module (ActiveSupport::Autoload) is provide that extends
autoloading with new behavior.
* All autoloads in modules that have extended ActiveSupport::Autoload
will be eagerly required in threadsafe environments
* Autoloads can optionally leave off the path if the path is the same
as full_constant_name.underscore
* It is possible to specify that a group of autoloads live under an
additional path. For instance, all of ActionDispatch's middlewares
are ActionDispatch::MiddlewareName, but they live under
"action_dispatch/middlewares/middleware_name"
* It is possible to specify that a group of autoloads are all found
at the same path. For instance, a number of exceptions might all
be declared there.
* One consequence of this is that testing-related constants are not
autoloaded. To get the testing helpers for a given component,
require "component_name/test_case". For instance, "action_controller/test_case".
* test_help.rb, which is automatically required by a Rails application's
test helper, requires the test_case.rb for all active components, so
this change will not be disruptive in existing or new applications.
Diffstat (limited to 'activesupport/lib/active_support/testing/performance.rb')
-rw-r--r-- | activesupport/lib/active_support/testing/performance.rb | 678 |
1 files changed, 341 insertions, 337 deletions
diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index ab34f975f6..66e32fa5d7 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -1,450 +1,454 @@ -require 'ruby-prof' - -require 'fileutils' -require 'rails/version' - -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 +begin + require 'ruby-prof' + + require 'fileutils' + require 'rails/version' + require 'active_support/core_ext/class/delegating_attributes' + + 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 + 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 end - end - yield(self.class::FINISHED, name) - end + 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 - add_error($!) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each + 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 add_error($!) + ensure + begin + teardown + run_callbacks :teardown, :enumerator => :reverse_each + rescue ::Test::Unit::AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue StandardError, ScriptError + add_error($!) + 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 - - def run_profile(metric) - klass = profile_options[:benchmark] ? Benchmarker : Profiler - performer = klass.new(self, metric) + GC.start + end - performer.run - puts performer.report - performer.record - end + def run_profile(metric) + klass = profile_options[:benchmark] ? Benchmarker : Profiler + performer = klass.new(self, metric) - class Performer - delegate :run_test, :profile_options, :full_test_name, :to => :@harness + performer.run + puts performer.report + performer.record + end - def initialize(harness, metric) - @harness, @metric = harness, metric - end + class Performer + delegate :run_test, :profile_options, :full_test_name, :to => :@harness - def report - rate = @total / profile_options[:runs] - '%20s: %s' % [@metric.name, @metric.format(rate)] - end + def initialize(harness, metric) + @harness, @metric = harness, metric + end - protected - def output_filename - "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + def report + rate = @total / profile_options[:runs] + '%20s: %s' % [@metric.name, @metric.format(rate)] end - end - class Benchmarker < Performer - def run - profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } - @total = @metric.total + protected + def output_filename + "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + end end - 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}" + class Benchmarker < Performer + def run + profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } + @total = @metric.total end - end - def environment - unless defined? @env - app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - - 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 + 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 - - ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' - ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - - @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - @env - end + def environment + unless defined? @env + app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - protected - HEADER = 'measurement,created_at,app,rails,ruby,platform' + 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 - def with_output_file - fname = output_filename + ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' + ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - if new = !File.exist?(fname) - FileUtils.mkdir_p(File.dirname(fname)) + @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - File.open(fname, 'ab') do |file| - file.puts(HEADER) if new - yield file - end + @env end - def output_filename - "#{super}.csv" - end - end + protected + HEADER = 'measurement,created_at,app,rails,ruby,platform' - class Profiler < Performer - def initialize(*args) - super - @supported = @metric.measure_mode rescue false - end + def with_output_file + fname = output_filename + + if new = !File.exist?(fname) + FileUtils.mkdir_p(File.dirname(fname)) + end - def run - return unless @supported + File.open(fname, 'ab') do |file| + file.puts(HEADER) if new + yield file + end + 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.sort.last.total_time } + def output_filename + "#{super}.csv" + end end - def report - if @supported + class Profiler < Performer + def initialize(*args) super - else - '%20s: unsupported' % @metric.name + @supported = @metric.measure_mode rescue false end - end - def record - return unless @supported + def run + return unless @supported - klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact + 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.sort.last.total_time } + end - 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)) + def report + if @supported + super + else + '%20s: unsupported' % @metric.name end end - end - protected - 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' - else printer_class.name.sub(/Printer$/, '').underscore - end + def record + return unless @supported + + klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact - "#{super()}_#{suffix}" + 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 - end - module Metrics - def self.[](name) - const_get(name.to_s.camelize) - rescue NameError - nil + protected + 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' + else printer_class.name.sub(/Printer$/, '').underscore + end + + "#{super()}_#{suffix}" + end end - class Base - attr_reader :total - - def initialize - @total = 0 + module Metrics + def self.[](name) + const_get(name.to_s.camelize) + rescue NameError + nil end - def name - @name ||= self.class.name.demodulize.underscore - end + class Base + attr_reader :total - def measure_mode - self.class::Mode - end + def initialize + @total = 0 + end - def measure - 0 - end + def name + @name ||= self.class.name.demodulize.underscore + end - def benchmark - with_gc_stats do - before = measure - yield - @total += (measure - before) + def measure_mode + self.class::Mode end - end - def profile - RubyProf.resume - yield - ensure - RubyProf.pause - end + def measure + 0 + end - protected - if GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - elsif defined?(GC::Profiler) - def with_gc_stats - GC.start - GC.disable - GC::Profiler.enable - yield - ensure - GC::Profiler.disable - GC.enable - end - else - def with_gc_stats + def benchmark + with_gc_stats do + before = measure yield + @total += (measure - before) end end - end - class Time < Base - def measure - ::Time.now.to_f - end - - def format(measurement) - if measurement < 2 - '%d ms' % (measurement * 1000) - else - '%.2f sec' % measurement + def profile + RubyProf.resume + yield + ensure + RubyProf.pause end - end - end - - class ProcessTime < Time - Mode = RubyProf::PROCESS_TIME - - def measure - RubyProf.measure_process_time - end - end - - class WallTime < Time - Mode = RubyProf::WALL_TIME - def measure - RubyProf.measure_wall_time + protected + if GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats + end + elsif defined?(GC::Profiler) + def with_gc_stats + GC.start + GC.disable + GC::Profiler.enable + yield + ensure + GC::Profiler.disable + GC.enable + end + else + def with_gc_stats + yield + end + end 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 - super - end + class Time < Base + def measure + ::Time.now.to_f + end - def measure - RubyProf.measure_cpu_time + def format(measurement) + if measurement < 2 + '%d ms' % (measurement * 1000) + else + '%.2f sec' % measurement + end + end end - end - class Memory < Base - Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) + class ProcessTime < Time + Mode = RubyProf::PROCESS_TIME - # ruby-prof wrapper - if RubyProf.respond_to?(:measure_memory) def measure - RubyProf.measure_memory / 1024.0 + RubyProf.measure_process_time end + end - # Ruby 1.8 + railsbench patch - elsif GC.respond_to?(:allocated_size) - def measure - GC.allocated_size / 1024.0 - end + class WallTime < Time + Mode = RubyProf::WALL_TIME - # Ruby 1.8 + lloyd patch - elsif GC.respond_to?(:heap_info) def measure - GC.heap_info['heap_current_memory'] / 1024.0 + RubyProf.measure_wall_time end + end - # Ruby 1.9 with total_malloc_allocated_size patch - elsif GC.respond_to?(:malloc_total_allocated_size) - def measure - GC.total_malloc_allocated_size / 1024.0 - end + class CpuTime < Time + Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) - # Ruby 1.9 unpatched - elsif GC.respond_to?(:malloc_allocated_size) - def measure - GC.malloc_allocated_size / 1024.0 + def initialize(*args) + # FIXME: yeah my CPU is 2.33 GHz + RubyProf.cpu_frequency = 2.33e9 + super end - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) def measure - GC.enable - GC.start - kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0 - GC.disable - kb + RubyProf.measure_cpu_time end end - def format(measurement) - '%.2f KB' % measurement - end - end + class Memory < Base + Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - class Objects < Base - Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) + # ruby-prof wrapper + if RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 + end - if RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end + # Ruby 1.8 + railsbench patch + elsif GC.respond_to?(:allocated_size) + def measure + GC.allocated_size / 1024.0 + end - # Ruby 1.8 + railsbench patch - elsif ObjectSpace.respond_to?(:allocated_objects) - def measure - ObjectSpace.allocated_objects + # Ruby 1.8 + lloyd patch + elsif GC.respond_to?(:heap_info) + def measure + GC.heap_info['heap_current_memory'] / 1024.0 + end + + # Ruby 1.9 with total_malloc_allocated_size patch + elsif GC.respond_to?(:malloc_total_allocated_size) + def measure + GC.total_malloc_allocated_size / 1024.0 + end + + # Ruby 1.9 unpatched + elsif GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size / 1024.0 + end + + # Ruby 1.9 + GC profiler patch + elsif defined?(GC::Profiler) + def measure + GC.enable + GC.start + kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0 + GC.disable + kb + end end - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) - def measure - GC.enable - GC.start - last = GC::Profiler.data.last - count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS] - GC.disable - count + def format(measurement) + '%.2f KB' % measurement end end - def format(measurement) - measurement.to_i.to_s - end - end + class Objects < Base + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - class GcRuns < Base - Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + if RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end - if RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - elsif GC.respond_to?(:collections) - def measure - GC.collections - end - elsif GC.respond_to?(:heap_info) - def measure - GC.heap_info['num_gc_passes'] + # Ruby 1.8 + railsbench patch + elsif ObjectSpace.respond_to?(:allocated_objects) + def measure + ObjectSpace.allocated_objects + end + + # Ruby 1.9 + GC profiler patch + elsif defined?(GC::Profiler) + def measure + GC.enable + GC.start + last = GC::Profiler.data.last + count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS] + GC.disable + count + end end - end - def format(measurement) - measurement.to_i.to_s + def format(measurement) + measurement.to_i.to_s + end end - end - class GcTime < Base - Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + class GcRuns < Base + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - if RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time + if RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + elsif GC.respond_to?(:collections) + def measure + GC.collections + end + elsif GC.respond_to?(:heap_info) + def measure + GC.heap_info['num_gc_passes'] + end end - elsif GC.respond_to?(:time) - def measure - GC.time + + def format(measurement) + measurement.to_i.to_s end end - def format(measurement) - '%d ms' % (measurement / 1000) + class GcTime < Base + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + + if RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time + end + elsif GC.respond_to?(:time) + def measure + GC.time + end + end + + def format(measurement) + '%d ms' % (measurement / 1000) + end end end end end end -end +rescue LoadError +end
\ No newline at end of file |