class Perf::Meter

Measures the runtime execution speed of a block of code, expression or entire methods.

Constants

ACCURACY_EXCELLENT
ACCURACY_FAIR
ACCURACY_GOOD
ACCURACY_POOR
ACCURACY_UNKNOWN

Constant for accuracy. The constant represents the upper bound of its description.

ACCURACY_VERY_POOR
METHOD_TYPE_CLASS
METHOD_TYPE_INSTANCE

Hash keys for instrumented methods

OVERHEAD_CALC_MAX_REPETITIONS

Overhead calculation tuning constants

OVERHEAD_CALC_MIN_TIME
OVERHEAD_CALC_RUNS
PATH_MEASURES

Name of the path roots

PATH_METHODS

Attributes

current_path[RW]
measurements[RW]

Public Class Methods

new(options={}) click to toggle source

Creation of a Perf::Meter allows you to specify if you want to consider the overhead in the calculation or not. The overhead is calculated once and stored away. That way it is always the same in a single run.

# File lib/perf/meter.rb, line 44
def initialize(options={})
  @options = options.clone

  @options[:subtract_overhead] = true if @options[:subtract_overhead].nil? # Never use ||= with booleans

  @measurements             = {}      # A hash of Measure
  @current_path             = nil
  @instrumented_methods     = {METHOD_TYPE_INSTANCE=>[],METHOD_TYPE_CLASS=>[]}
  @subtract_overhead        = @options[:subtract_overhead]
  if @@overhead.nil?
    @@overhead              = measure_overhead
    @measurements           = {}      # A hash of Measure; must repeat here to cleanup what measure_overhead did
  end
end

Public Instance Methods

accuracy(path) click to toggle source

Returns an index of accuracy of the measure calculated in relation to the overhead. The larger the accuracy, the more accurate the measure. accuracy < 0 means that it is not possible to calculate; accuracy <= 1 means that the measure is equal or smaller than the overhead.

This makes the measure very inaccurate.

accuracy = X means that the measure is X times the overhead.

# File lib/perf/meter.rb, line 346
def accuracy(path)
  if @@overhead
    over=@@overhead[:time].total+@@overhead[:time].real
    if over>0
      m=get_measurement(path)
      return ((m.time.total+m.time.real)*@@overhead[:count] / (over*m.count)) if m.count>0
    end
  end
  -1.0
end
adjust_overhead() click to toggle source

The overhead cannot be larger that any of the measures taken. If a measure taken is larger than the overhead, than this function takes care of adjusting the overhead. This is called by the ReportFormat class just before rendering the report, so you should not have to call this by hand unless you are interested in getting the overhead.

# File lib/perf/meter.rb, line 362
def adjust_overhead
  false
end
adjusted_time(m) click to toggle source
# File lib/perf/meter.rb, line 67
def adjusted_time(m)
  return m.time if !@subtract_overhead || !@overhead

  utime,stime,cutime,cstime,real = nil,nil,nil,nil,nil

  adj=m.time-((@overhead[:time]*m.count)/@overhead[:count])

  utime  = 0.0 if adj.utime  < 0.0
  stime  = 0.0 if adj.stime  < 0.0
  cutime = 0.0 if adj.cutime < 0.0
  cstime = 0.0 if adj.cstime < 0.0
  real   = 0.0 if adj.real   < 0.0

  if utime || stime || cutime || cstime || real
    Benchmark::Tms.new(utime  || adj.utime,
                       stime  || adj.stime,
                       cutime || adj.cutime,
                       cstime || adj.cstime,
                       real   || adj.real)
  else
    adj
  end
end
blocks_time() click to toggle source

Returns the total time - expressed with a Benchmark::Tms object - for all the blocks measures

# File lib/perf/meter.rb, line 96
def blocks_time
  @measurements[PATH_MEASURES].time if @measurements[PATH_MEASURES]
end
has_measures?() click to toggle source
# File lib/perf/meter.rb, line 91
def has_measures?
  @measurements.length > 0
end
measure(what,root_path=PATH_MEASURES,&code) click to toggle source

Takes a description and a code block and measures the performance of the block. It returns the value returned by the block

Attributes

  • what - The title that will identify of the block

  • code - Block of code to measure

Examples

Measures the time taken by the code in the block

perf = Perf::Meter.new
perf.measure(:func) do
   some code here
end

Measure the time taken to compute “some_expression”

if perf.measure(:some_expression) { some_expression }
  ...
end

Measure a bunch of things, and divides the measures in sub blocks that get measures separately

perf.measure(:bunch_of_stuff) do
   perf.measure(:thing1)  do
     ...
   end
   perf.measure(:thing2) do
      perf.measure(:thing2_part1) do
        ...
      end
      perf.measure(:thing2_part2) do
        ...
      end
   end
end
# File lib/perf/meter.rb, line 145
def measure(what,root_path=PATH_MEASURES,&code)
  current_path=@current_path
  if @current_path.nil?
    @current_path=root_path
    root=get_measurement(@current_path)
  else
    root=nil
  end
  @current_path+= "\\#{what}"
  res=nil
  begin
    m=get_measurement(@current_path)
    m.count     += 1
    root.count  += 1 if root
    m.measuring +=1
    if m.measuring>1
      res=code.call
    else
      t = Benchmark.measure { res=code.call }
      root.time  += t if root
      m.time     += t
    end
  ensure
    @current_path=current_path
    m.measuring-=1
  end
  res
end
measure_class_method(klass,method_name) click to toggle source

Puts a wrapper around class methods of a specific class to measure their performance Remember to use restore_class_method when you are done, otherwise the class method will stay instrumented.

Use sparingly!

Attributes

  • klass - The Class containing the instance method that you want to measure

  • method_name - The name of the class method that you want to measure

Examples

Instruments the class find so that the execution of the class method “static_method” will be measures every time that is used.

perf = Perf::Meter.new
perf.measure_class_method(SomeClass,:static_method)
..

Removes the instrumentation (important!)

perf.restore_class_method(SomeClass,:static_method)

Removes all instrumentation from class SomeClass

perf.restore_all_class_methods(SomeClass)
# File lib/perf/meter.rb, line 313
def measure_class_method(klass,method_name)
  measure_method_by_type(class << klass; self end,method_name,METHOD_TYPE_CLASS)
end
measure_instance_method(klass,method_name) click to toggle source

Puts a wrapper around instance methods of a specific class to measure their performance Remember to use restore_instance_method when you are done, otherwise the method will stay instrumented.

Use sparingly!

Attributes

  • klass - The Class containing the instance method that you want to measure

  • method_name - The name of the method that you want to measure

Examples

Instruments the class find so that the execution of the method “find” will be measures every time that is used.

perf = Perf::Meter.new
perf.measure_instance_method(User,:find)
..

Removes the instrumentation (important!)

perf.restore_instance_method(User,:find)

Removes all instrumentation from class User

perf.restore_all_instance_methods(User)
# File lib/perf/meter.rb, line 268
def measure_instance_method(klass,method_name)
  measure_method_by_type(klass,method_name,METHOD_TYPE_INSTANCE)
end
measure_result(what,&code) click to toggle source

Takes a description and an expression returning a value and measures the performance of the expression storing the result by returned value. Should be used only for expressions that can return only a small discrete number of unique values, such a flag for example.

It returns the value returned by the block

Attributes

  • what - The title that will identify of the block

  • code - Block of code to measure

Examples

Measures the time take to compute the existence of user xyz and will give you stats for the case in which the result is true and false.

perf = Perf::Meter.new
if perf.measure_result(:long_epression) { User.find(xyz).nil? }
end
# File lib/perf/meter.rb, line 195
def measure_result(what,&code)
  res=measure(what,PATH_MEASURES,&code)
  merge_measures(what,"#{what} = \"#{res.to_s}\"")
  res
end
method_meters(klass,imethods=[],cmethods=[]) { || ... } click to toggle source

Puts a wrapper around a set of methods of a specific class to measure their performance. The set is provided with an array of instance methods, and one of class methods.

The method defines the wrapper, yields to the block, and then restores the instrumented class. This ensures that the instrumented class is restored, and that the instrumentation occurs only in the context of the block

Attributes

  • klass - The Class containing the instance method that you want to measure

  • imethods - An array of instance methods that you want to measure

  • cmethods - An array of class methods that you want to measure

Examples

perf = Perf::Meter.new
m.method_meters(PerfTestExample,[:test,:test_np],[:static_method]) do
  a=PerfTestExample.new
  a.test(1,2,3)
  a.test_np
  PerfTestExample.static_method
end

After this m contains measures for the executions of the instance methods test, test_np and the class methods static_method

# File lib/perf/meter.rb, line 228
def method_meters(klass,imethods=[],cmethods=[])
  res=nil
  begin
    imethods.each {|m| measure_instance_method(klass,m) }
    cmethods.each {|m| measure_class_method(klass,m) }
    res=yield
  ensure
    imethods.each {|m| restore_instance_method(klass,m) }
    cmethods.each {|m| restore_class_method(klass,m) }
  end
  res
end
methods_time() click to toggle source

Returns the total time - expressed with a Benchmark::Tms object - for all the methods measures

# File lib/perf/meter.rb, line 101
def methods_time
  @measurements[PATH_METHODS].time if @measurements[PATH_METHODS]
end
overhead() click to toggle source
# File lib/perf/meter.rb, line 59
def overhead
  if @subtract_overhead
    @@overhead.dup
  else
    {:time=>Benchmark::Tms.new,:count=>0}
  end
end
restore_all_class_methods(klass) click to toggle source

Removes the instrumentation of all class methods in a given class. See measure_class_method for more information.

# File lib/perf/meter.rb, line 327
def restore_all_class_methods(klass)
  restore_all_methods_by_type(class << klass; self end,METHOD_TYPE_CLASS)
end
restore_all_instance_methods(klass) click to toggle source

Removes all instrumentation of instance methods in a given class. See measure_instance_method for more information.

# File lib/perf/meter.rb, line 282
def restore_all_instance_methods(klass)
  restore_all_methods_by_type(klass,METHOD_TYPE_INSTANCE)
end
restore_all_methods(klass) click to toggle source

Removes all instrumentation of class methods and instance methods in a given class. See measure_class_method for more information.

# File lib/perf/meter.rb, line 334
def restore_all_methods(klass)
  restore_all_instance_methods(klass)
  restore_all_class_methods(klass)
end
restore_class_method(klass,method_name) click to toggle source

Removes the instrumentation of a class method in a given class. See measure_class_method for more information.

# File lib/perf/meter.rb, line 320
def restore_class_method(klass,method_name)
  restore_method_by_type(class << klass; self end,method_name,METHOD_TYPE_CLASS)
end
restore_instance_method(klass,method_name) click to toggle source

Removes the instrumentation of a instance method in a given class. See measure_instance_method for more information.

# File lib/perf/meter.rb, line 275
def restore_instance_method(klass,method_name)
  restore_method_by_type(klass,method_name,METHOD_TYPE_INSTANCE)
end

Protected Instance Methods

camelize(from) click to toggle source
# File lib/perf/meter.rb, line 481
def camelize(from)
  from.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
end
clear_measurement(path) click to toggle source
# File lib/perf/meter.rb, line 408
def clear_measurement(path)
  @measurements.delete(path)
end
get_current_path() click to toggle source
# File lib/perf/meter.rb, line 392
def get_current_path
  @current_stack.join("\\") + (!@current_stack.empty? ? "\\" : "")
end
get_measurement(path) click to toggle source
# File lib/perf/meter.rb, line 412
def get_measurement(path)
  @measurements[path] ||= Perf::Measure.new(path)
end
measure_method_by_type(klass,method_name,type) click to toggle source
# File lib/perf/meter.rb, line 416
def measure_method_by_type(klass,method_name,type)
  unless @instrumented_methods[type].find{|x| x[:klass]==klass && x[:method]==method_name}
    old_method_symbol="rubyperf_org_#{method_name}".to_sym
    @instrumented_methods[type] << { :klass=>klass, :method=>method_name, :org=>old_method_symbol }
    klass.send(:alias_method, old_method_symbol,method_name)
    perf=self
    klass.send(:define_method,method_name) do |*args|
      perf.measure("#{klass}.#{method_name}",PATH_METHODS) do
        self.send(old_method_symbol, *args)
      end
    end
  end
end
measure_overhead() click to toggle source

This method measures the overhead of calling “measure” on an instace of Perf::Meter. It will run OVERHEAD_CALC_RUNS measures of an empty block until the total time taken exceeds OVERHEAD_CALC_MIN_TIME.

# File lib/perf/meter.rb, line 372
def measure_overhead
  t=Benchmark::Tms.new
  cnt=0
  rep=0
  runs=OVERHEAD_CALC_RUNS
  while t.total<OVERHEAD_CALC_MIN_TIME && rep<OVERHEAD_CALC_MAX_REPETITIONS
    t+=Benchmark.measure do
      runs.times {measure(:a) {}}
    end
    rep  += 1        # Count the repetitions
    cnt  += runs     # Count the total runs
    runs *= 2        # Increases the number of runs to quickly adapt to the speed of the machine
  end
  {:time=>t,:count=>cnt}
end
merge_measures(what_from,what_to) click to toggle source
# File lib/perf/meter.rb, line 396
def merge_measures(what_from,what_to)
  path_from        = "#{@current_path || PATH_MEASURES}\\#{what_from}"
  path_to          = "#{@current_path || PATH_MEASURES}\\#{what_to}"

  m_from = get_measurement(path_from)
  m_to   = get_measurement(path_to)

  m_to.merge(m_from)

  clear_measurement(path_from)
end
method_missing(method_sym, *arguments, &block) click to toggle source

You can generate a report using one of the built-in report formats with a simple syntax shortcut

m=Perf::Meter.new
m.report_FORMAT

Where FORMAT is the ending part of one of the ReportFormatFORMAT classes built in.

Examples

m=Perf::Meter.new m.measure(:something) {something} puts m.report_html puts m.report_simple

Calls superclass method
# File lib/perf/meter.rb, line 473
def method_missing(method_sym, *arguments, &block)
  if method_sym.to_s =~ /^report_(.*)$/
    klass=Object.const_get("Perf").const_get("ReportFormat#{camelize($1)}")
    return klass.new.format(self,arguments[0]) if klass
  end
  super
end
restore_all_methods_by_type(klass,type) click to toggle source

Removes all instrumentation of instance methods in a given class. See measure_instance_method for more information.

# File lib/perf/meter.rb, line 445
def restore_all_methods_by_type(klass,type)
  remove=[]
  @instrumented_methods[type].select {|x| x[:klass]==klass}.each do |im|
    klass.send(:remove_method,im[:method])
    klass.send(:alias_method, im[:method], im[:org])
    klass.send(:remove_method,im[:org])
    remove<<im
  end
  remove.each do |im|
    @instrumented_methods[type].delete(im)
  end
end
restore_method_by_type(klass,method_name,type) click to toggle source

Removes the instrumentation of a instance method in a given class. See measure_instance_method for more information.

# File lib/perf/meter.rb, line 433
def restore_method_by_type(klass,method_name,type)
  if (im=@instrumented_methods[type].find{|x| x[:klass]==klass && x[:method]==method_name})
    klass.send(:remove_method,im[:method])
    klass.send(:alias_method, im[:method], im[:org])
    klass.send(:remove_method,im[:org])
    @instrumented_methods[type].delete(im)
  end
end
set_measurement(path,m) click to toggle source
# File lib/perf/meter.rb, line 388
def set_measurement(path,m)
  @measurements[path]=m  if m.is_a? Perf::Measure
end