Измерение и оценка времени для методов Ruby

87

Как я могу измерить время, затрачиваемое методом и отдельными операторами этого метода в Ruby. Если вы видите метод ниже, я хочу измерить общее время, затрачиваемое на этот метод, и время, затраченное на доступ к базе данных и доступ к Redis. Я не хочу писать Benchmark.measure перед каждым утверждением. Есть ли у интерпретатора ruby ​​какие-нибудь ловушки для этого?

def foo
# code to access database
# code to access redis. 
end
Фани
источник
есть что-то похожее на new Date()javascript, которое есть в ruby, но я не могу вспомнить правильный синтаксис. должен дать вам достойный листинг в Google
Рейган
1
@Phani Можете ли вы выбрать правильный ответ? По прошествии 8 лет, я думаю, здесь есть несколько твердых ответов. Спасибо.
Джошуа Пинтер,

Ответы:

113

Вы могли бы использовать Timeобъект. ( Документы времени )

Например,

start = Time.now
# code to time
finish = Time.now

diff = finish - start

diff будет в секундах, как число с плавающей запятой.

РЕДАКТИРОВАТЬ: endзарезервировано.

wquist
источник
13
просто небольшая поправка. endзарезервировано, поэтому используйте другое имя переменной.
Джейсон Ким
4
Time.nowзависит от настроек системных часов, поэтому рекомендуется использовать их Process.clock_gettime(Process::CLOCK_MONOTONIC)вместо них. Но для грубых расчетов это не имеет значения. blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Патрик Бринич-Ланглуа
104

Самый простой способ:

require 'benchmark'

def foo
 time = Benchmark.measure {
  code to test
 }
 puts time.real #or save it to logs
end

Пример вывода:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

Значения: время процессора, системное время, общее и реальное прошедшее время.

Источник: ruby docs .

Лукаш Островски
источник
40
Вы также можете сделать это, Benchmark.realtime { block }если хотите только в реальном времени
jmccure
35

Использовать Benchmarkотчет

require 'benchmark' # Might be necessary.

def foo
  Benchmark.bm( 20 ) do |bm|  # The 20 is the width of the first column in the output.
    bm.report( "Access Database:" ) do 
      # Code to access database.
    end
   
    bm.report( "Access Redis:" ) do
      # Code to access redis.
    end
  end
end

Это выведет что-то вроде следующего:

                        user     system      total        real
Access Database:    0.020000   0.000000   0.020000 (  0.475375)
Access Redis:       0.000000   0.000000   0.000000 (  0.000037)

<------ 20 -------> # This is where the 20 comes in. NOTE: This is not shown in output.

Более подробную информацию можно найти здесь .

Джошуа Пинтер
источник
2
Я только что вернулся к своему собственному ответу и был (снова) впечатлен тем, как Benchmark справляется с этим. Люблю Руби.
Джошуа Пинтер
2
Это должен быть предпочтительный ответ: поскольку, начиная с Ruby 2.2, Benchmarkкласс использует монотонные часы, как обсуждалось в других ответах. См., Например, следующий исходный код и найдите «def measure» в строке 286: github.com/ruby/ruby/blob/ruby_2_2/lib/benchmark.rb
Purplejacket
17

Многие ответы предполагают использование Time.now. Но стоит знать, что Time.nowможет измениться. Системные часы могут дрейфовать и корректироваться системным администратором или через NTP. Таким образом, Time.now может перескочить вперед или назад и дать вам неточные результаты тестирования.

Лучшее решение - использовать монотонные часы операционной системы, которые всегда движутся вперед. Ruby 2.1 и выше предоставляют доступ к этому через:

start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# code to time
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
diff = finish - start # gets time is seconds as a float

Вы можете прочитать больше здесь . Также вы можете увидеть, как популярный проект Ruby, Sidekiq, перешел на монотонные часы .

Гай C
источник
7

Вторая мысль: определение функции measure () с аргументом блока кода Ruby может помочь упростить код измерения времени:

def measure(&block)
  start = Time.now
  block.call
  Time.now - start
end

# t1 and t2 is the executing time for the code blocks.
t1 = measure { sleep(1) }

t2 = measure do
  sleep(2)
end
Houcheng
источник
В вашем определении вы это называете benchmark. Когда вы его используете, это называется measure. Пожалуйста, исправьте это.
Sandro L
4

В духе ответа wquist , но немного проще, вы также можете сделать это, как показано ниже:

start = Time.now
# code to time
Time.now - start
ADude2
источник
Этот ответ - (немного) другой способ ответить на вопрос. То, что вы могли понять это из ответа @wquist, не означает, что он недействителен.
thecretmaster
3

Загляните в ruby-profупаковку, там должно быть то, что вам нужно. Это создаст огромные стеки вызовов с указанием времени.

http://ruby-prof.rubyforge.org/

Он может быть слишком подробным, и в этом случае просто обертывание больших разделов Benchmark.measureможет быть хорошим способом.

Коди Кофлан
источник
0

Другой метод автоматической проверки кода в консоли rails - использование этого драгоценного камня: https://github.com/igorkasyanchuk/execution_time

вы увидите информацию, аналогичную той, что вы получаете из запросов в журналах

Игорь Касьянчук
источник