Misurare e valutare il tempo per i metodi Ruby


87

Come posso misurare il tempo impiegato da un metodo e le singole istruzioni in quel metodo in Ruby. Se vedi il metodo seguente, voglio misurare il tempo totale impiegato dal metodo e il tempo impiegato per l'accesso al database e l'accesso redis. Non voglio scrivere Benchmark.measure prima di ogni affermazione. L'interprete di Ruby ci fornisce degli hook per farlo?

def foo
# code to access database
# code to access redis. 
end

c'è qualcosa di simile a new Date()javascript che ha ruby, ma non ricordo la sintassi corretta. dovrebbe darti un elenco decente di Google però
Reagan

1
@Phani Puoi selezionare una risposta corretta per favore? Dopo 8 anni, penso che ci siano alcune risposte concrete qui. Grazie.
Joshua Pinter,

Risposte:


113

Potresti usare l' Timeoggetto. ( Time Docs )

Per esempio,

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

diff = finish - start

diff sarebbe in secondi, come numero in virgola mobile.

EDIT: endè riservato.


13
solo una piccola correzione. endè riservato, quindi usa un altro nome di variabile.
Jason Kim

4
Time.nowè influenzato dalle regolazioni dell'orologio di sistema, quindi è consigliabile utilizzarlo Process.clock_gettime(Process::CLOCK_MONOTONIC). Ma per calcoli approssimativi questo non importa. blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Patrick Brinich-Langlois

103

Il modo più semplice:

require 'benchmark'

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

Output di esempio:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

I valori sono: tempo cpu, tempo di sistema, tempo totale e tempo trascorso reale.

Fonte: ruby docs .


40
Puoi anche farlo Benchmark.realtime { block }se vuoi solo il tempo reale
jmccure

35

Usa Benchmarkil rapporto di

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

Questo produrrà qualcosa di simile al seguente:

                        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.

Ulteriori informazioni possono essere trovate qui .


2
Sono appena tornato alla mia risposta e sono rimasto impressionato (di nuovo) da come Benchmark gestisce questo. Love Ruby.
Joshua Pinter

2
Questa dovrebbe essere la risposta preferita: perché, a partire da Ruby 2.2, la Benchmarkclasse usa un orologio monotono, come discusso in altre risposte. Vedi ad esempio il seguente codice sorgente e cerca "def measure" alla riga 286: github.com/ruby/ruby/blob/ruby_2_2/lib/benchmark.rb
Purplejacket

17

Molte delle risposte suggeriscono l'uso di Time.now. Ma vale la pena essere consapevoli che Time.nowpuò cambiare. Gli orologi di sistema possono variare e potrebbero essere corretti dall'amministratore del sistema o tramite NTP. È quindi possibile per Time.now saltare avanti o indietro e fornire risultati imprecisi del benchmarking.

Una soluzione migliore è utilizzare l'orologio monotono del sistema operativo, che è sempre in movimento. Ruby 2.1 e versioni successive danno accesso a questo tramite:

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

Puoi leggere maggiori dettagli qui . Inoltre puoi vedere il popolare progetto Ruby, Sidekiq, che è passato all'orologio monotonico .


7

Un secondo pensiero, definire la funzione measure () con l'argomento del blocco di codice Ruby può aiutare a semplificare il codice di misura del tempo:

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

Nella tua definizione lo chiami benchmark. Quando lo usi si chiama measure. Per favore aggiustalo.
Sandro L

4

Nello spirito della risposta di wquist , ma un po 'più semplice, potresti anche farlo come di seguito:

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

Questa risposta è un modo (leggermente) diverso di rispondere alla domanda. Solo perché potresti capirlo dalla risposta di @ wquist non significa che non sia valido.
segretario

3

Guarda nel ruby-profpacchetto, dovrebbe avere ciò di cui hai bisogno. Creerà enormi stack di chiamate con i tempi.

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

Potrebbe essere troppo granulare, nel qual caso semplicemente avvolgere sezioni più grandi Benchmark.measurepotrebbe essere un buon modo per procedere.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.