模組 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