diff options
Diffstat (limited to 'activesupport/lib/active_support/testing/performance')
5 files changed, 496 insertions, 0 deletions
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..6b27959840 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/jruby.rb @@ -0,0 +1,115 @@ +require 'jruby/profiler' +require 'java' +import java.lang.management.ManagementFactory + +module ActiveSupport + module Testing + module Performance + DEFAULTS.merge!( + if ARGV.include?('--benchmark') + {:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]} + else + { :metrics => [:wall_time], + :formats => [:flat, :graph] } + end).freeze + + protected + def run_gc + ManagementFactory.memory_mx_bean.gc + end + + class Profiler < Performer + def initialize(*args) + super + @supported = @metric.is_a?(Metrics::WallTime) + end + + def run + return unless @supported + + @total = time_with_block do + @data = JRuby::Profiler.profile do + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end + end + 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| + 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 WallTime < Time + def measure + super + end + end + + class CpuTime < Time + def measure + ManagementFactory.thread_mx_bean.get_current_thread_cpu_time / 1000 / 1000 / 1000.0 # seconds + end + end + + class UserTime < Time + def measure + ManagementFactory.thread_mx_bean.get_current_thread_user_time / 1000 / 1000 / 1000.0 # seconds + end + end + + class Memory < DigitalInformationUnit + 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 + ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_runs, current_gc| total_runs += current_gc.collection_count } + end + end + + class GcTime < Time + 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 + end +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..198d235548 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb @@ -0,0 +1,113 @@ +require 'rubinius/agent' + +module ActiveSupport + module Testing + module Performance + DEFAULTS.merge!( + if ARGV.include?('--benchmark') + {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} + else + { :metrics => [:wall_time], + :formats => [:flat, :graph] } + end).freeze + + protected + def run_gc + GC.run(true) + end + + class Performer; end + + class Profiler < Performer + def initialize(*args) + super + @supported = @metric.is_a?(Metrics::WallTime) + end + + def run + return unless @supported + + @profiler = Rubinius::Profiler::Instrumenter.new + + @total = time_with_block do + @profiler.profile(false) do + full_profile_options[:runs].to_i.times { run_test(@metric, :profile) } + end + end + end + + def record + return unless @supported + + if(full_profile_options[:formats].include?(:flat)) + create_path_and_open_file(:flat) do |file| + @profiler.show(file) + end + end + + if(full_profile_options[:formats].include?(:graph)) + create_path_and_open_file(:graph) do |file| + @profiler.show(file) + end + end + end + + protected + def create_path_and_open_file(printer_name) + fname = "#{output_filename}_#{printer_name}.txt" + FileUtils.mkdir_p(File.dirname(fname)) + File.open(fname, 'wb') do |file| + yield(file) + end + end + end + + module Metrics + class Base + attr_reader :loopback + + def profile + yield + end + + protected + def with_gc_stats + @loopback = Rubinius::Agent.loopback + GC.run(true) + yield + end + end + + class WallTime < Time + def measure + super + end + end + + class Memory < DigitalInformationUnit + def measure + loopback.get("system.memory.counter.bytes").last + end + end + + class Objects < Amount + def measure + loopback.get("system.memory.counter.objects").last + end + end + + class GcRuns < Amount + def measure + loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last + end + end + + class GcTime < Time + def measure + (loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last) / 1000.0 + 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..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 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..86e650050b --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/ruby/mri.rb @@ -0,0 +1,59 @@ +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 + GC.start + yield + ensure + GC.disable_stats + end + end + end + + class Memory < DigitalInformationUnit + # Ruby 1.8 + ruby-prof wrapper + if RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory + end + end + 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.0 / 1000.0 + 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 new file mode 100644 index 0000000000..62095a8fe4 --- /dev/null +++ b/activesupport/lib/active_support/testing/performance/ruby/yarv.rb @@ -0,0 +1,57 @@ +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 < DigitalInformationUnit + # Ruby 1.9 + GCdata patch + if GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size + end + end + 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 |