diff options
Diffstat (limited to 'activesupport/lib/active_support/testing/performance/ruby.rb')
-rw-r--r-- | activesupport/lib/active_support/testing/performance/ruby.rb | 152 |
1 files changed, 152 insertions, 0 deletions
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..b29ec6719c --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -0,0 +1,152 @@ +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 + 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, :call_stack] } + end).freeze + + protected + def run_gc + GC.start + 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 + 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 + + def record + return unless @supported + + 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, full_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 + # overridden by each implementation + def with_gc_stats + yield + 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 < DigitalInformationUnit + Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) + end + + class Objects < Amount + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) + end + + class GcRuns < Amount + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + end + + class GcTime < Time + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + end + end + end + end +end + +if RUBY_VERSION.between?('1.9.2', '2.0') + require 'active_support/testing/performance/ruby/yarv' +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.' + exit +end |