module Benchmark::Perf::Execution

Measure length of time the work could take on average

@api public

Public Class Methods

check_greater(expected, min) click to toggle source

Check if expected value is greater than minimum

@param [Numeric] expected @param [Numeric] min

@raise [ArgumentError]

@api private

# File lib/benchmark/perf/execution.rb, line 121
def check_greater(expected, min)
  unless expected > min
    raise ArgumentError,
          "Repeat value: #{expected} needs to be greater than #{min}"
  end
end
run(repeat: 1, io: nil, warmup: 1, subprocess: true, &work) click to toggle source

Perform work x times

@param [Integer] repeat

how many times to repeat the code measuremenets

@example

ExecutionTime.run(repeat: 10) { ... }

@return [Array[Float, Float]]

average and standard deviation

@api public

# File lib/benchmark/perf/execution.rb, line 93
def run(repeat: 1, io: nil, warmup: 1, subprocess: true, &work)
  check_greater(repeat, 0)

  result = CPUResult.new

  run_warmup(warmup: warmup, io: io, subprocess: subprocess, &work)

  repeat.times do
    GC.start
    result << run_in_subprocess(io: io, subprocess: subprocess) do
      Clock.measure(&work)
    end
  end

  io.puts if io

  result
end
run_in_subprocess(subprocess: true, io: nil) { || ... } click to toggle source

Isolate run in subprocess

@example

iteration.run_in_subproces { ... }

@return [Float]

the elapsed time of the measurement

@api private

# File lib/benchmark/perf/execution.rb, line 29
def run_in_subprocess(subprocess: true, io: nil)
  return yield unless subprocess && Process.respond_to?(:fork)
  return yield unless run_in_subprocess?

  reader, writer = IO.pipe
  reader.binmode
  writer.binmode

  pid = Process.fork do
    GC.start
    GC.disable if ENV['BENCH_DISABLE_GC']

    begin
      reader.close
      time = yield
      io.print "%9.6f" % data if io
      Marshal.dump(time, writer)
    rescue => error
      Marshal.dump(error, writer)
    ensure
      GC.enable if ENV['BENCH_DISABLE_GC']
      writer.close
      exit # allow finalizers to run
    end
  end

  writer.close unless writer.closed?
  Process.waitpid(pid)
  data = Marshal.load(reader)
  reader.close
  raise data if data.is_a?(Exception)
  data
end
run_in_subprocess?() click to toggle source

Check if measurements need to run in subprocess

@api private

# File lib/benchmark/perf/execution.rb, line 15
def run_in_subprocess?
  ENV["RUN_IN_SUBPROCESS"] != 'false' && Process.respond_to?(:fork)
end
run_warmup(warmup: 1, io: nil, subprocess: true, &work) click to toggle source

Run warmup measurement

@param [Numeric] warmup

the warmup time

@api private

# File lib/benchmark/perf/execution.rb, line 70
def run_warmup(warmup: 1, io: nil, subprocess: true, &work)
  GC.start

  warmup.times do
    run_in_subprocess(io: io, subprocess: subprocess) do
      Clock.measure(&work)
    end
  end
end

Private Instance Methods

check_greater(expected, min) click to toggle source

Check if expected value is greater than minimum

@param [Numeric] expected @param [Numeric] min

@raise [ArgumentError]

@api private

# File lib/benchmark/perf/execution.rb, line 121
def check_greater(expected, min)
  unless expected > min
    raise ArgumentError,
          "Repeat value: #{expected} needs to be greater than #{min}"
  end
end
run(repeat: 1, io: nil, warmup: 1, subprocess: true, &work) click to toggle source

Perform work x times

@param [Integer] repeat

how many times to repeat the code measuremenets

@example

ExecutionTime.run(repeat: 10) { ... }

@return [Array[Float, Float]]

average and standard deviation

@api public

# File lib/benchmark/perf/execution.rb, line 93
def run(repeat: 1, io: nil, warmup: 1, subprocess: true, &work)
  check_greater(repeat, 0)

  result = CPUResult.new

  run_warmup(warmup: warmup, io: io, subprocess: subprocess, &work)

  repeat.times do
    GC.start
    result << run_in_subprocess(io: io, subprocess: subprocess) do
      Clock.measure(&work)
    end
  end

  io.puts if io

  result
end
run_in_subprocess(subprocess: true, io: nil) { || ... } click to toggle source

Isolate run in subprocess

@example

iteration.run_in_subproces { ... }

@return [Float]

the elapsed time of the measurement

@api private

# File lib/benchmark/perf/execution.rb, line 29
def run_in_subprocess(subprocess: true, io: nil)
  return yield unless subprocess && Process.respond_to?(:fork)
  return yield unless run_in_subprocess?

  reader, writer = IO.pipe
  reader.binmode
  writer.binmode

  pid = Process.fork do
    GC.start
    GC.disable if ENV['BENCH_DISABLE_GC']

    begin
      reader.close
      time = yield
      io.print "%9.6f" % data if io
      Marshal.dump(time, writer)
    rescue => error
      Marshal.dump(error, writer)
    ensure
      GC.enable if ENV['BENCH_DISABLE_GC']
      writer.close
      exit # allow finalizers to run
    end
  end

  writer.close unless writer.closed?
  Process.waitpid(pid)
  data = Marshal.load(reader)
  reader.close
  raise data if data.is_a?(Exception)
  data
end
run_in_subprocess?() click to toggle source

Check if measurements need to run in subprocess

@api private

# File lib/benchmark/perf/execution.rb, line 15
def run_in_subprocess?
  ENV["RUN_IN_SUBPROCESS"] != 'false' && Process.respond_to?(:fork)
end
run_warmup(warmup: 1, io: nil, subprocess: true, &work) click to toggle source

Run warmup measurement

@param [Numeric] warmup

the warmup time

@api private

# File lib/benchmark/perf/execution.rb, line 70
def run_warmup(warmup: 1, io: nil, subprocess: true, &work)
  GC.start

  warmup.times do
    run_in_subprocess(io: io, subprocess: subprocess) do
      Clock.measure(&work)
    end
  end
end