模組 Benchmark
Benchmark
模組提供方法來測量和報告執行 Ruby 程式碼所使用的時間。
-
測量建立表達式
"a"*1_000_000_000
所給字串的時間require 'benchmark' puts Benchmark.measure { "a"*1_000_000_000 }
在我的機器上 (i5 1.7 GHz 上的 OSX 10.8.3) 會產生
0.350000 0.400000 0.750000 ( 0.835234)
此報告顯示使用者 CPU 時間、系統 CPU 時間、使用者和系統 CPU 時間的總和,以及經過的實際時間。時間單位為秒。
-
使用
bm
方法進行一些實驗require 'benchmark' n = 5000000 Benchmark.bm do |x| x.report { for i in 1..n; a = "1"; end } x.report { n.times do ; a = "1"; end } x.report { 1.upto(n) do ; a = "1"; end } end
結果
user system total real 1.010000 0.000000 1.010000 ( 1.014479) 1.000000 0.000000 1.000000 ( 0.998261) 0.980000 0.000000 0.980000 ( 0.981335)
-
繼續上一個範例,在每個報告中放入標籤
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: 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)
-
一些基準測試的時間取決於執行項目的順序。這些差異是因記憶體配置和垃圾回收的成本而產生。為了避免這些差異,提供了
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.490000 0.010000 1.500000 ( 1.490520) sort 1.460000 0.000000 1.460000 ( 1.463025) -------------------------------- total: 2.960000sec user system total real sort! 1.460000 0.000000 1.460000 ( 1.460465) sort 1.450000 0.010000 1.460000 ( 1.448327)
-
使用
benchmark
方法,報告具有唯一標籤的順序實驗的統計資料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.950000 0.000000 0.950000 ( 0.952039) times: 0.980000 0.000000 0.980000 ( 0.984938) upto: 0.950000 0.000000 0.950000 ( 0.946787) >total: 2.880000 0.000000 2.880000 ( 2.883764) >avg: 0.960000 0.000000 0.960000 ( 0.961255)
常數
- CAPTION
預設標題字串 (輸出時間上方的標題)。
- FORMAT
用於顯示時間的預設格式字串。另請參閱
Benchmark::Tms#format
。- VERSION
公開類別方法
使用 Benchmark::Report 物件呼叫區塊,可以收集和報告個別基準測試的結果。保留 label_width
個前導空白,作為每一行標籤。在報告的最上方列印 caption
,並使用 format
來格式化每一行。(注意:caption
必須包含換行字元,請參閱預設 Benchmark::Tms::CAPTION 作為範例。)
傳回 Benchmark::Tms
物件的陣列。
如果區塊傳回 Benchmark::Tms
物件的陣列,這些物件將用於格式化其他輸出行。如果給定 labels
參數,這些參數將用於標記這些額外行。
註解:其他方法提供更簡單的介面,且適用於幾乎所有基準測試需求。請參閱 Benchmark
中的範例,以及 bm
和 bmbm
方法。
範例
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
一個 benchmark
方法的簡單介面,bm
產生帶標籤的順序報告。label_width
和 labels
參數與 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
嘗試透過執行測試兩次來將此影響降至最低,第一次作為彩排以穩定執行時間環境,第二次則為實際執行。在每個實際計時的開始之前執行 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
傳回用於執行給定區塊的時間,作為 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
傳回用於執行給定區塊的經過實際時間。
# 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::Report 物件呼叫區塊,可以收集和報告個別基準測試的結果。保留 label_width
個前導空白,作為每一行標籤。在報告的最上方列印 caption
,並使用 format
來格式化每一行。(注意:caption
必須包含換行字元,請參閱預設 Benchmark::Tms::CAPTION 作為範例。)
傳回 Benchmark::Tms
物件的陣列。
如果區塊傳回 Benchmark::Tms
物件的陣列,這些物件將用於格式化其他輸出行。如果給定 labels
參數,這些參數將用於標記這些額外行。
註解:其他方法提供更簡單的介面,且適用於幾乎所有基準測試需求。請參閱 Benchmark
中的範例,以及 bm
和 bmbm
方法。
範例
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
一個 benchmark
方法的簡單介面,bm
產生帶標籤的順序報告。label_width
和 labels
參數與 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
嘗試透過執行測試兩次來將此影響降至最低,第一次作為彩排以穩定執行時間環境,第二次則為實際執行。在每個實際計時的開始之前執行 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
傳回用於執行給定區塊的時間,作為 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
傳回用於執行給定區塊的經過實際時間。
# 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