模块 Benchmark
Benchmark
模块提供了测量和报告执行 Ruby 代码所用时间的方法。
-
测量构建表达式
"a"*1_000_000_000
给出的字符串所需的时间require 'benchmark' puts Benchmark.measure { "a"*1_000_000_000 }
在我的机器上(OSX 10.8.3,i5 1.7 GHz)生成以下内容
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
公共类方法
源代码
# 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 report = Report.new(label_width, format) results = yield(report) print " " * report.width + caption unless caption.empty? report.list.each { |i| print i.label.to_s.ljust(report.width) print i.format(report.format, *format) } 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::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 215 def bm(label_width = 0, *labels, &blk) # :yield: report benchmark(CAPTION, label_width, FORMAT, *labels, &blk) 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 257 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
有时基准测试结果会发生偏差,因为较早执行的代码会遇到与稍后运行的代码不同的垃圾回收开销。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 302 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
返回执行给定块所用的时间,表示为一个 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 321 def realtime # :yield: r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) yield Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0 end
返回执行给定块所用的经过的实际时间。时间单位为秒。
Benchmark.realtime { "a" * 1_000_000_000 } #=> 0.5098029999935534
私有实例方法
源代码
# 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 report = Report.new(label_width, format) results = yield(report) print " " * report.width + caption unless caption.empty? report.list.each { |i| print i.label.to_s.ljust(report.width) print i.format(report.format, *format) } 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::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 215 def bm(label_width = 0, *labels, &blk) # :yield: report benchmark(CAPTION, label_width, FORMAT, *labels, &blk) 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 257 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
有时基准测试结果会发生偏差,因为较早执行的代码会遇到与稍后运行的代码不同的垃圾回收开销。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 302 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
返回执行给定块所用的时间,表示为一个 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 321 def realtime # :yield: r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) yield Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0 end
返回执行给定块所用的经过的实际时间。时间单位为秒。
Benchmark.realtime { "a" * 1_000_000_000 } #=> 0.5098029999935534