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
Public Class Methods
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
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
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
# 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
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
# File lib/perf/meter.rb, line 91 def has_measures? @measurements.length > 0 end
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
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
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
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
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
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
# File lib/perf/meter.rb, line 59 def overhead if @subtract_overhead @@overhead.dup else {:time=>Benchmark::Tms.new,:count=>0} end end
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
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
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
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
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
# File lib/perf/meter.rb, line 481 def camelize(from) from.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } end
# File lib/perf/meter.rb, line 408 def clear_measurement(path) @measurements.delete(path) end
# File lib/perf/meter.rb, line 392 def get_current_path @current_stack.join("\\") + (!@current_stack.empty? ? "\\" : "") end
# File lib/perf/meter.rb, line 412 def get_measurement(path) @measurements[path] ||= Perf::Measure.new(path) end
# 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
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
# 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
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
# 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
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
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
# File lib/perf/meter.rb, line 388 def set_measurement(path,m) @measurements[path]=m if m.is_a? Perf::Measure end