Zeit für Ruby-Methoden messen und messen


87

Wie kann ich die Zeit messen, die eine Methode benötigt, und die einzelnen Anweisungen in dieser Methode in Ruby. Wenn Sie die folgende Methode sehen, möchte ich die Gesamtzeit messen, die die Methode benötigt, und die Zeit, die für den Datenbankzugriff und den Redis-Zugriff benötigt wird. Ich möchte Benchmark.measure nicht vor jeder Aussage schreiben. Gibt uns der Ruby-Interpreter irgendwelche Haken dafür?

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

Es gibt etwas Ähnliches wie das new Date()von Javascript, das Ruby hat, aber ich kann mich nicht an die richtige Syntax erinnern. sollte Ihnen eine anständige Google-Auflistung geben
Reagan

1
@Phani Kannst du bitte eine richtige Antwort auswählen? Nach 8 Jahren denke ich, dass es hier einige solide Antworten gibt. Vielen Dank.
Joshua Pinter

Antworten:


112

Sie könnten das TimeObjekt verwenden. ( Zeitdokumente )

Beispielsweise,

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

diff = finish - start

diff wäre in Sekunden als Gleitkommazahl.

BEARBEITEN: endist reserviert.


13
nur eine kleine Korrektur. endist reserviert, verwenden Sie also einen anderen Variablennamen.
Jason Kim

4
Time.nowwird durch Anpassungen der Systemuhr beeinflusst, daher ist es eine bewährte Methode, diese Process.clock_gettime(Process::CLOCK_MONOTONIC)stattdessen zu verwenden . Für grobe Berechnungen spielt dies jedoch keine Rolle. blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Patrick Brinich-Langlois

102

Der einfachste Weg:

require 'benchmark'

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

Beispielausgabe:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

Die Werte sind: CPU-Zeit, Systemzeit, Gesamt- und tatsächlich verstrichene Zeit.

Quelle: Ruby Docs .


39
Sie können auch tun, Benchmark.realtime { block }wenn Sie nur die Echtzeit wollen
jmccure

35

Verwenden Sie Benchmarkden Bericht

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

Dies gibt ungefähr Folgendes aus:

                        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.

Weitere Informationen finden Sie hier .


2
Ich bin gerade auf meine eigene Antwort zurückgekommen und war (wieder) beeindruckt davon, wie Benchmark damit umgeht. Liebe Rubin.
Joshua Pinter

2
Dies sollte die bevorzugte Antwort sein: Da die BenchmarkKlasse ab Ruby 2.2 eine monotone Uhr verwendet, wie in anderen Antworten erläutert. Sehen Sie sich zum Beispiel den folgenden Quellcode an und suchen Sie in Zeile 286 nach "def Measure": github.com/ruby/ruby/blob/ruby_2_2/lib/benchmark.rb
Purplejacket

16

Viele der Antworten deuten auf die Verwendung von hin Time.now. Es lohnt sich jedoch zu wissen, dass Time.nowsich dies ändern kann. Systemuhren können driften und werden möglicherweise vom Systemadministrator oder über NTP korrigiert. Time.now kann daher vorwärts oder rückwärts springen und Ihrem Benchmarking ungenaue Ergebnisse liefern.

Eine bessere Lösung ist die Verwendung der monotonen Uhr des Betriebssystems, die sich immer vorwärts bewegt. Ruby 2.1 und höher bieten Zugriff darauf über:

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

Weitere Details können Sie hier lesen . Sie können auch sehen, dass das beliebte Ruby-Projekt Sidekiq auf monotone Uhr umgestellt hat .


7

Ein zweiter Gedanke: Definieren Sie die Funktion Measure () mit dem Ruby-Code-Blockargument, um den Zeitmesscode zu vereinfachen:

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

In Ihrer Definition nennen Sie es benchmark. Wenn Sie es verwenden, wird es aufgerufen measure. Bitte beheben Sie dies.
Sandro L

4

Im Geiste der Antwort von wquist , aber etwas einfacher, können Sie dies auch wie folgt tun:

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

Diese Antwort ist eine (etwas) andere Art, die Frage zu beantworten. Nur weil Sie es aus der Antwort von @ wquist herausfinden könnten, heißt das nicht, dass es nicht gültig ist.
Geheimmeister

3

Schauen Sie in das ruby-profPaket, es sollte haben, was Sie brauchen. Es werden riesige Anrufstapel mit Timings erstellt.

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

Es könnte zu körnig sein. In diesem Fall ist es Benchmark.measuremöglicherweise ein guter Weg, größere Abschnitte einzuwickeln.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.