модуль 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)
В этом отчёте показаны процессорное время пользователя, процессорное время системы, сумма процессорного времени пользователя и системы, а также фактическое прошедшее время. Единица измерения времени — секунды.
-
Проведение нескольких экспериментов последовательно с помощью метода
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
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.