#-- # Copyright (c) 2005, Michael Schuerig, michael@schuerig.de # # == License # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # See http://www.gnu.org/copyleft/lesser.html #++ # Stopwatches for in-place performance measurement. # # To measure the time spent in a section of code, wrap it in a block like this # # Stopwatch.measure('complicated_calculation') do # complicated_calculation(x) # end # # The block delimites the scope of variables, thus when setting the values # of variables whose value is needed outside, these variables must be defined # before the block: # # value = nil # Stopwatch.measure('complicated_calculation') do # value = complicated_calculation(x) # end # if value # ... # # This file has to be +required+ (not +loaded+) so that the Stopwatch class # is loaded only once and its internal state is kept intact. # class Stopwatch @@stopwatches = {} @@snapshots = {} attr_reader :name, :executions, :accumulated_time def initialize(name) @name = name reset end def measure start result = yield self stop result end def self.measure(name, &block) self.for_name(name).measure(&block) end def reset @executions = 0 @accumulated_time = 0.0 end def self.take_snapshot(name) @@snapshots[name] = @@stopwatches.values.sort.inject([]) do |memo, sw| memo << sw.dup end end def self.delete_snapshot(name) @@snapshots.delete(name) end def total_millis accumulated_time * 1000.0 end def millis_per_execution if executions > 0 (total_millis/executions).to_f else 0.0 end end def <=>(other) self.name <=> other.name end def self.each(&block) @@stopwatches.values.sort.each(&block) end def self.each_snapshot(&block) @@snapshots.sort.each(&block) end def self.for_name(name) @@stopwatches[name] ||= Stopwatch.new(name) end def self.executions_for(name) self.for_name(name).executions end def self.accumluated_time_for(name) self.for_name(name).accumluated_time end def self.reset_all self.each do |sw| sw.reset end end def self.reset(name) if sw = self.for_name(name) sw.reset end end def to_h { 'name' => name, 'executions' => executions, 'total_time'=> total_millis } end def self.export(name = 'current', stopwatches = @@stopwatches.values) { 'snapshot' => name, 'stopwatches' => stopwatches.map { |sw| sw.to_h } } end protected def start @executions += 1 @start_time = Time.now end def stop @stop_time = Time.now @accumulated_time += (@stop_time - @start_time) end end