aboutsummaryrefslogblamecommitdiffstats
path: root/activesupport/lib/active_support/testing/performance/mri.rb
blob: 859588c7356103903e4b4980dc581d37f32449b6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                                            
      
               
                  

                  
              





























































                                                                                                                 



                            

























                                                                                 

















































                                                                                 

           
                              













                                                                               

           
                             













                                                                       

           
                           













                                                                            




           
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