class Benchmark::IPS::Job

Benchmark jobs.

def compare!

Set @compare to true.
def compare!
  @compare = true
end

def compare?

Returns:
  • (Boolean) - Need to compare?
def compare?
  @compare
end

def config opts

Options Hash: (**iterations)
  • :time (Integer) -- Warmup and calculation iterations.
  • :time (Integer) -- Calculation time.
  • :warmup (Integer) -- Warmup time.
def config opts
  @warmup = opts[:warmup] if opts[:warmup]
  @time = opts[:time] if opts[:time]
  @suite = opts[:suite] if opts[:suite]
  @iterations = opts[:iterations] if opts[:iterations]
end

def create_report(label, measured_us, iter, avg_ips, sd_ips, cycles)

Returns:
  • (Report::Entry) - Entry with data.

Parameters:
  • cycles (Integer) -- Number of Cycles.
  • sd_ips (Float) -- Standard deviation iterations per second.
  • avg_ips (Float) -- Average iterations per second.
  • iter (Integer) -- Iterations.
  • measured_us (Integer) -- Measured time in microsecond.
  • label (String) -- Report item label.
def create_report(label, measured_us, iter, avg_ips, sd_ips, cycles)
  @full_report.add_entry label, measured_us, iter, avg_ips, sd_ips, cycles
end

def cycles_per_100ms time_msec, iters

Returns:
  • (Integer) - Cycles per 100ms.

Parameters:
  • iters (Integer) -- Iterations.
  • time_msec (Float) -- Each iteration's time in ms.
def cycles_per_100ms time_msec, iters
  cycles = ((MICROSECONDS_PER_100MS / time_msec) * iters).to_i
  cycles = 1 if cycles <= 0
  cycles
end

def generate_json

Generate json from +@full_report+.
def generate_json
  @full_report.generate_json @json_path if json?
end

def held_results?

def held_results?
  File.exist?(@held_path)
end

def hold!(held_path)

Set @hold to true.
def hold!(held_path)
  @held_path = held_path
end

def hold?

Returns:
  • (Boolean) - Need to hold results between multiple Ruby invocations?
def hold?
  !!@held_path
end

def initialize opts={}

Options Hash: (**opts)
  • (false) (Boolean) -- :quiet Suppress the printing of information.
  • (nil) (Benchmark::Suite) -- :suite Specify Benchmark::Suite.
def initialize opts={}
  @suite = opts[:suite] || nil
  @stdout = opts[:quiet] ? nil : StdoutReport.new
  @list = []
  @compare = false
  @json_path = false
  @held_path = nil
  @held_results = nil
  @timing = {}
  @full_report = Report.new
  # Default warmup and calculation time in seconds.
  @warmup = 2
  @time = 5
  @iterations = 1
end

def item(label="", str=nil, &blk) # :yield:

Raises:
  • (ArgumentError) - Raises if str and blk are both absent.
  • (ArgumentError) - Raises if str and blk are both present.

Parameters:
  • blk (Proc) -- Code to be benchmarked.
  • str (String) -- Code to be benchmarked.
  • label (String) -- Label of benchmarked code.
def item(label="", str=nil, &blk) # :yield:
  if blk and str
    raise ArgumentError, "specify a block and a str, but not both"
  end
  action = str || blk
  raise ArgumentError, "no block or string" unless action
  @list.push Entry.new(label, action)
  self
end

def iterations_per_sec cycles, time_us

Returns:
  • (Float) - Iteration per second.

Parameters:
  • time_us (Integer) -- Time in microsecond.
  • cycles (Integer) -- Cycles.
def iterations_per_sec cycles, time_us
  MICROSECONDS_PER_SECOND * (cycles.to_f / time_us.to_f)
end

def json!(path="data.json")

Set @json_path to given path, defaults to "data.json".
def json!(path="data.json")
  @json_path = path
end

def json?

Returns:
  • (Boolean) - Need to generate json?
def json?
  !!@json_path
end

def load_held_results

def load_held_results
  require "json"
  @held_results = Hash[File.open(@held_path).map { |line|
    result = JSON.parse(line)
    [result['item'], result]
  }]
end

def run

def run
  @stdout.start_warming if @stdout
  @iterations.times do
    run_warmup
  end
  
  @stdout.start_running if @stdout
  
  held = nil
  
  @iterations.times do |n|
    held = run_benchmark
  end
  
  if held
    puts
    puts 'Pausing here -- run Ruby again to measure the next benchmark...'
  end
end

def run_benchmark

Run calculation.
def run_benchmark
  @list.each do |item|
    if hold? && @held_results && @held_results.key?(item.label)
     result = @held_results[item.label]
      create_report(item.label, result['measured_us'], result['iter'],
        result['avg_ips'], result['sd_ips'], result['cycles'])
      next
    end
    
    @suite.running item.label, @time if @suite
    @stdout.running item.label, @time if @stdout
    Timing.clean_env
    iter = 0
    measurements_us = []
    # Running this number of cycles should take around 100ms.
    cycles = @timing[item]
    target = Time.now + @time
    
    while Time.now < target
      before = Time.now
      item.call_times cycles
      after = Time.now
      # If for some reason the timing said this took no time (O_o)
      # then ignore the iteration entirely and start another.
      iter_us = time_us before, after
      next if iter_us <= 0.0
      iter += cycles
      measurements_us << iter_us
    end
    final_time = Time.now
    measured_us = measurements_us.inject(0) { |a,i| a + i }
    all_ips = measurements_us.map { |time_us|
      iterations_per_sec cycles, time_us
    }
    avg_ips = Timing.mean(all_ips)
    sd_ips =  Timing.stddev(all_ips, avg_ips).round
    rep = create_report(item.label, measured_us, iter, avg_ips, sd_ips, cycles)
    if (final_time - target).abs >= (@time.to_f * MAX_TIME_SKEW)
      rep.show_total_time!
    end
    @stdout.add_report rep, caller(1).first if @stdout
    @suite.add_report rep, caller(1).first if @suite
    
    if hold? && item != @list.last
      File.open @held_path, "a" do |f|
        require "json"
        f.write JSON.generate({
          :item => item.label,
          :measured_us => measured_us,
          :iter => iter,
          :avg_ips => avg_ips,
          :sd_ips => sd_ips,
          :cycles => cycles
        })
        f.write "\n"
      end
      
      return true
    end
  end
  
  if hold? && @full_report.entries.size == @list.size
    File.delete @held_path if File.exist?(@held_path)
  end
  
  false
end

def run_comparison

Run comparison of entries in +@full_report+.
def run_comparison
  @full_report.run_comparison if compare?
end

def run_warmup

Run warmup.
def run_warmup
  @list.each do |item|
    next if hold? && @held_results && @held_results.key?(item.label)
    
    @suite.warming item.label, @warmup if @suite
    @stdout.warming item.label, @warmup if @stdout
    Timing.clean_env
    before = Time.now
    target = Time.now + @warmup
    warmup_iter = 0
    while Time.now < target
      item.call_times(1)
      warmup_iter += 1
    end
    after = Time.now
    warmup_time_us = time_us before, after
    @timing[item] = cycles_per_100ms warmup_time_us, warmup_iter
    @stdout.warmup_stats warmup_time_us, @timing[item] if @stdout
    @suite.warmup_stats warmup_time_us, @timing[item] if @suite
    
    break if hold?
  end
end

def time_us before, after

Returns:
  • (Float) - Time difference of before and after.

Parameters:
  • after (Time) -- time.
  • before (Time) -- time.
def time_us before, after
  (after.to_f - before.to_f) * MICROSECONDS_PER_SECOND
end