Benchmark 模块

Benchmark 模块提供用于测量和报告执行 Ruby 代码所用时间的方法。

结果

              user     system      total        real
for:      1.010000   0.000000   1.010000 (  1.015688)
times:    1.000000   0.000000   1.000000 (  1.003611)
upto:     1.030000   0.000000   1.030000 (  1.028098)

常量

CAPTION

默认标题字符串(输出时间上方的标题)。

FORMAT

用于显示时间的默认格式字符串。另请参见 Benchmark::Tms#format

VERSION

公共类方法

benchmark(caption = "", label_width = nil, format = nil, *labels) { |report| ... } 单击以切换源

使用 Benchmark::Report 对象调用该块,该对象可用于收集和报告各个基准测试结果。为每行标签保留 label_width 个前导空格。在报告顶部打印 caption,并使用 format 格式化每行。(注意:caption 必须包含一个终止换行符,有关示例,请参见默认 Benchmark::Tms::CAPTION。)

返回 Benchmark::Tms 对象的数组。

如果该块返回 Benchmark::Tms 对象的数组,则这些对象将用于格式化其他输出行。如果给出了 labels 参数,则这些参数将用于标记这些额外行。

注意:其他方法提供了更简单的接口,并且适用于几乎所有基准测试要求。请参阅 Benchmark 中的示例,以及 bmbmbm 方法。

示例

require 'benchmark'
include Benchmark          # we need the CAPTION and FORMAT constants

n = 5000000
Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
  tf = x.report("for:")   { for i in 1..n; a = "1"; end }
  tt = x.report("times:") { n.times do   ; a = "1"; end }
  tu = x.report("upto:")  { 1.upto(n) do ; a = "1"; end }
  [tf+tt+tu, (tf+tt+tu)/3]
end

生成

              user     system      total        real
for:      0.970000   0.000000   0.970000 (  0.970493)
times:    0.990000   0.000000   0.990000 (  0.989542)
upto:     0.970000   0.000000   0.970000 (  0.972854)
>total:   2.930000   0.000000   2.930000 (  2.932889)
>avg:     0.976667   0.000000   0.976667 (  0.977630)
# File lib/benchmark.rb, line 170
def benchmark(caption = "", label_width = nil, format = nil, *labels) # :yield: report
  sync = $stdout.sync
  $stdout.sync = true
  label_width ||= 0
  label_width += 1
  format ||= FORMAT
  print ' '*label_width + caption unless caption.empty?
  report = Report.new(label_width, format)
  results = yield(report)
  Array === results and results.grep(Tms).each {|t|
    print((labels.shift || t.label || "").ljust(label_width), t.format(format))
  }
  report.list
ensure
  $stdout.sync = sync unless sync.nil?
end
bm(label_width = 0, *labels) { |report| ... } 点击切换源

benchmark 方法的简单接口,bm 生成带有标签的顺序报告。label_widthlabels 参数与 benchmark 的含义相同。

require 'benchmark'

n = 5000000
Benchmark.bm(7) do |x|
  x.report("for:")   { for i in 1..n; a = "1"; end }
  x.report("times:") { n.times do   ; a = "1"; end }
  x.report("upto:")  { 1.upto(n) do ; a = "1"; end }
end

生成

              user     system      total        real
for:      0.960000   0.000000   0.960000 (  0.957966)
times:    0.960000   0.000000   0.960000 (  0.960423)
upto:     0.950000   0.000000   0.950000 (  0.954864)
# File lib/benchmark.rb, line 209
def bm(label_width = 0, *labels, &blk) # :yield: report
  benchmark(CAPTION, label_width, FORMAT, *labels, &blk)
end
bmbm(width = 0) { |job| ... } 点击切换源

有时基准测试结果会发生偏差,因为较早执行的代码遇到的垃圾回收开销与较晚执行的代码不同。bmbm 尝试通过两次运行测试来最大程度地减少这种影响,第一次作为彩排以使运行时环境稳定,第二次为实际运行。GC.start 在每个实际计时开始之前执行;其成本不包括在计时中。然而,实际上,bmbm 能做的事情有限,并且无法保证结果与垃圾回收和其他影响隔离。

由于 bmbm 对测试进行两次遍历,因此它可以计算所需的标签宽度。

require 'benchmark'

array = (1..1000000).map { rand }

Benchmark.bmbm do |x|
  x.report("sort!") { array.dup.sort! }
  x.report("sort")  { array.dup.sort  }
end

生成

Rehearsal -----------------------------------------
sort!   1.440000   0.010000   1.450000 (  1.446833)
sort    1.440000   0.000000   1.440000 (  1.448257)
-------------------------------- total: 2.890000sec

            user     system      total        real
sort!   1.460000   0.000000   1.460000 (  1.458065)
sort    1.450000   0.000000   1.450000 (  1.455963)

bmbm 生成一个 Benchmark::Job 对象,并返回一个 Benchmark::Tms 对象数组。

# File lib/benchmark.rb, line 251
def bmbm(width = 0) # :yield: job
  job = Job.new(width)
  yield(job)
  width = job.width + 1
  sync = $stdout.sync
  $stdout.sync = true

  # rehearsal
  puts 'Rehearsal '.ljust(width+CAPTION.length,'-')
  ets = job.list.inject(Tms.new) { |sum,(label,item)|
    print label.ljust(width)
    res = Benchmark.measure(&item)
    print res.format
    sum + res
  }.format("total: %tsec")
  print " #{ets}\n\n".rjust(width+CAPTION.length+2,'-')

  # take
  print ' '*width + CAPTION
  job.list.map { |label,item|
    GC.start
    print label.ljust(width)
    Benchmark.measure(label, &item).tap { |res| print res }
  }
ensure
  $stdout.sync = sync unless sync.nil?
end
measure(label = "") { || ... } 点击切换源

返回执行给定块所用时间,作为 Benchmark::Tms 对象。采用 label 选项。

require 'benchmark'

n = 1000000

time = Benchmark.measure do
  n.times { a = "1" }
end
puts time

生成

0.220000   0.000000   0.220000 (  0.227313)
# File lib/benchmark.rb, line 296
def measure(label = "") # :yield:
  t0, r0 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
  yield
  t1, r1 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
  Benchmark::Tms.new(t1.utime  - t0.utime,
                     t1.stime  - t0.stime,
                     t1.cutime - t0.cutime,
                     t1.cstime - t0.cstime,
                     r1 - r0,
                     label)
end
realtime() { || ... } 点击切换源

返回执行给定块所用的实际经过时间。

# File lib/benchmark.rb, line 311
def realtime # :yield:
  r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  yield
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
end

私有实例方法

benchmark(caption = "", label_width = nil, format = nil, *labels) { |report| ... } 单击以切换源

使用 Benchmark::Report 对象调用该块,该对象可用于收集和报告各个基准测试结果。为每行标签保留 label_width 个前导空格。在报告顶部打印 caption,并使用 format 格式化每行。(注意:caption 必须包含一个终止换行符,有关示例,请参见默认 Benchmark::Tms::CAPTION。)

返回 Benchmark::Tms 对象的数组。

如果该块返回 Benchmark::Tms 对象的数组,则这些对象将用于格式化其他输出行。如果给出了 labels 参数,则这些参数将用于标记这些额外行。

注意:其他方法提供了更简单的接口,并且适用于几乎所有基准测试要求。请参阅 Benchmark 中的示例,以及 bmbmbm 方法。

示例

require 'benchmark'
include Benchmark          # we need the CAPTION and FORMAT constants

n = 5000000
Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
  tf = x.report("for:")   { for i in 1..n; a = "1"; end }
  tt = x.report("times:") { n.times do   ; a = "1"; end }
  tu = x.report("upto:")  { 1.upto(n) do ; a = "1"; end }
  [tf+tt+tu, (tf+tt+tu)/3]
end

生成

              user     system      total        real
for:      0.970000   0.000000   0.970000 (  0.970493)
times:    0.990000   0.000000   0.990000 (  0.989542)
upto:     0.970000   0.000000   0.970000 (  0.972854)
>total:   2.930000   0.000000   2.930000 (  2.932889)
>avg:     0.976667   0.000000   0.976667 (  0.977630)
# File lib/benchmark.rb, line 170
def benchmark(caption = "", label_width = nil, format = nil, *labels) # :yield: report
  sync = $stdout.sync
  $stdout.sync = true
  label_width ||= 0
  label_width += 1
  format ||= FORMAT
  print ' '*label_width + caption unless caption.empty?
  report = Report.new(label_width, format)
  results = yield(report)
  Array === results and results.grep(Tms).each {|t|
    print((labels.shift || t.label || "").ljust(label_width), t.format(format))
  }
  report.list
ensure
  $stdout.sync = sync unless sync.nil?
end
bm(label_width = 0, *labels) { |report| ... } 点击切换源

benchmark 方法的简单接口,bm 生成带有标签的顺序报告。label_widthlabels 参数与 benchmark 的含义相同。

require 'benchmark'

n = 5000000
Benchmark.bm(7) do |x|
  x.report("for:")   { for i in 1..n; a = "1"; end }
  x.report("times:") { n.times do   ; a = "1"; end }
  x.report("upto:")  { 1.upto(n) do ; a = "1"; end }
end

生成

              user     system      total        real
for:      0.960000   0.000000   0.960000 (  0.957966)
times:    0.960000   0.000000   0.960000 (  0.960423)
upto:     0.950000   0.000000   0.950000 (  0.954864)
# File lib/benchmark.rb, line 209
def bm(label_width = 0, *labels, &blk) # :yield: report
  benchmark(CAPTION, label_width, FORMAT, *labels, &blk)
end
bmbm(width = 0) { |job| ... } 点击切换源

有时基准测试结果会发生偏差,因为较早执行的代码遇到的垃圾回收开销与较晚执行的代码不同。bmbm 尝试通过两次运行测试来最大程度地减少这种影响,第一次作为彩排以使运行时环境稳定,第二次为实际运行。GC.start 在每个实际计时开始之前执行;其成本不包括在计时中。然而,实际上,bmbm 能做的事情有限,并且无法保证结果与垃圾回收和其他影响隔离。

由于 bmbm 对测试进行两次遍历,因此它可以计算所需的标签宽度。

require 'benchmark'

array = (1..1000000).map { rand }

Benchmark.bmbm do |x|
  x.report("sort!") { array.dup.sort! }
  x.report("sort")  { array.dup.sort  }
end

生成

Rehearsal -----------------------------------------
sort!   1.440000   0.010000   1.450000 (  1.446833)
sort    1.440000   0.000000   1.440000 (  1.448257)
-------------------------------- total: 2.890000sec

            user     system      total        real
sort!   1.460000   0.000000   1.460000 (  1.458065)
sort    1.450000   0.000000   1.450000 (  1.455963)

bmbm 生成一个 Benchmark::Job 对象,并返回一个 Benchmark::Tms 对象数组。

# File lib/benchmark.rb, line 251
def bmbm(width = 0) # :yield: job
  job = Job.new(width)
  yield(job)
  width = job.width + 1
  sync = $stdout.sync
  $stdout.sync = true

  # rehearsal
  puts 'Rehearsal '.ljust(width+CAPTION.length,'-')
  ets = job.list.inject(Tms.new) { |sum,(label,item)|
    print label.ljust(width)
    res = Benchmark.measure(&item)
    print res.format
    sum + res
  }.format("total: %tsec")
  print " #{ets}\n\n".rjust(width+CAPTION.length+2,'-')

  # take
  print ' '*width + CAPTION
  job.list.map { |label,item|
    GC.start
    print label.ljust(width)
    Benchmark.measure(label, &item).tap { |res| print res }
  }
ensure
  $stdout.sync = sync unless sync.nil?
end
measure(label = "") { || ... } 点击切换源

返回执行给定块所用时间,作为 Benchmark::Tms 对象。采用 label 选项。

require 'benchmark'

n = 1000000

time = Benchmark.measure do
  n.times { a = "1" }
end
puts time

生成

0.220000   0.000000   0.220000 (  0.227313)
# File lib/benchmark.rb, line 296
def measure(label = "") # :yield:
  t0, r0 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
  yield
  t1, r1 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
  Benchmark::Tms.new(t1.utime  - t0.utime,
                     t1.stime  - t0.stime,
                     t1.cutime - t0.cutime,
                     t1.cstime - t0.cstime,
                     r1 - r0,
                     label)
end
realtime() { || ... } 点击切换源

返回执行给定块所用的实际经过时间。

# File lib/benchmark.rb, line 311
def realtime # :yield:
  r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  yield
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
end