Medir y comparar el tiempo para los métodos Ruby

87

¿Cómo puedo medir el tiempo que tarda un método y las declaraciones individuales en ese método en Ruby? Si ve el método a continuación, quiero medir el tiempo total que tomó el método y el tiempo que tomó el acceso a la base de datos y el acceso a redis. No quiero escribir Benchmark.measure antes de cada declaración. ¿El intérprete de ruby ​​nos da algún gancho para hacer esto?

def foo
# code to access database
# code to access redis. 
end
Phani
fuente
hay algo similar a new Date()javascript que tiene ruby, pero no recuerdo la sintaxis correcta. Sin embargo
Reagan
1
@Phani ¿Puedes seleccionar una respuesta correcta por favor? Después de 8 años, creo que aquí hay algunas respuestas sólidas. Gracias.
Joshua Pinter

Respuestas:

113

Podrías usar el Timeobjeto. ( Documentos de tiempo )

Por ejemplo,

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

diff = finish - start

diff estaría en segundos, como un número de punto flotante.

EDITAR: endestá reservado.

wquist
fuente
13
solo una pequeña corrección. endestá reservado, así que use algún otro nombre de variable.
Jason Kim
4
Time.nowse ve afectado por los ajustes en el reloj del sistema, por lo que es una buena práctica utilizar Process.clock_gettime(Process::CLOCK_MONOTONIC)en su lugar. Pero para cálculos aproximados, esto no importa. blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Patrick Brinich-Langlois
103

La forma más sencilla:

require 'benchmark'

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

Salida de muestra:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

Los valores son: tiempo de la CPU, tiempo del sistema, tiempo transcurrido total y real.

Fuente: ruby docs .

Łukasz Ostrowski
fuente
40
También puede hacerlo Benchmark.realtime { block }si solo desea el tiempo real
jmccure
35

Uso Benchmark's Informe

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

Esto generará algo como lo siguiente:

                        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.

Puede encontrar más información aquí .

Joshua Pinter
fuente
2
Regresé a mi propia respuesta y quedé impresionado (nuevamente) con la forma en que Benchmark maneja esto. Amo a Ruby.
Joshua Pinter
2
Esta debería ser la respuesta preferida: porque, a partir de Ruby 2.2, la Benchmarkclase usa un reloj monótono, como se discutió en otras respuestas. Consulte, por ejemplo, el siguiente código fuente y busque "def meter
Purplejacket
17

Muchas de las respuestas sugieren el uso de Time.now. Pero vale la pena ser consciente de que Time.nowpuede cambiar. Los relojes del sistema pueden variar y pueden ser corregidos por el administrador del sistema o mediante NTP. Por lo tanto, Time.now puede dar un salto hacia adelante o hacia atrás y dar a sus evaluaciones comparativas resultados inexactos.

Una mejor solución es utilizar el reloj monótono del sistema operativo, que siempre avanza. Ruby 2.1 y superior dan acceso a esto a través de:

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

Puedes leer más detalles aquí . También puede ver el popular proyecto Ruby, Sidekiq, que cambió al reloj monótono .

Chico C
fuente
7

Un segundo pensamiento, definir la función medida () con el argumento del bloque de código Ruby puede ayudar a simplificar el código de medida de tiempo:

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
fuente
En tu definición lo llamas benchmark. Cuando lo usas se llama measure. Por favor arregle esto.
Sandro L
4

En el espíritu de la respuesta de wquist , pero un poco más simple, también puede hacerlo como se muestra a continuación:

start = Time.now
# code to time
Time.now - start
ADude2
fuente
Esta respuesta es una forma (ligeramente) diferente de responder la pregunta. El hecho de que pueda averiguarlo a partir de la respuesta de @ wquist no significa que no sea válido.
thesecretmaster
3

Mire en el ruby-profpaquete, debería tener lo que necesita. Creará enormes pilas de llamadas con tiempos.

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

Puede ser demasiado granular, en cuyo caso simplemente envolver secciones más grandes Benchmark.measurepodría ser una buena manera de hacerlo.

Cody Caughlan
fuente