Wie kann ich eine Operation in Ruby in Millisekunden zeitlich festlegen?


79

Ich möchte herausfinden, wie viele Millisekunden eine bestimmte Funktion verwendet. Also sah ich hoch und niedrig aus, konnte aber keinen Weg finden, die Zeit in Ruby mit Millisekundengenauigkeit zu erfassen.

Wie machst Du das? In den meisten Programmiersprachen ist es einfach so

start = now.milliseconds
myfunction()
end = now.milliseconds
time = end - start

Antworten:


92

Sie können Rubys TimeKlasse verwenden. Zum Beispiel:

t1 = Time.now
# processing...
t2 = Time.now
delta = t2 - t1 # in seconds

Jetzt deltaist ein floatObjekt und Sie können ein so feinkörniges Ergebnis erzielen, wie es die Klasse liefert.


2
Dies ist die einfache Lösung, aber besser ist die Antwort mit Process.clock_gettime. Siehe blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Guy C

60

Sie können auch die integrierte Funktion Benchmark.measure verwenden:

require "benchmark"
puts(Benchmark.measure { sleep 0.5 })

Drucke:

0.000000   0.000000   0.000000 (  0.501134)

1
Ich mag die Idee, eine eingebaute Bibliothek zu verwenden, aber was sind die ersten drei Zahlen (die 0.000000)? Update: Sah es bis in die Dokumentation und es ist Benutzer - CPU - Zeit, System - CPU - Zeit, und die Summe der beiden letztgenannten.
Lyonsinbeta

1
Ja. Sie sind beschriftet, wenn Sie verwenden Benchmark.bm. Es ist nützlich zu sehen, wo die Zeit manchmal verbracht wird. Hier können Sie sehen, dass der Block nur im Leerlauf ist (0 insgesamt, 0,5 real)
Simon Perepelitsa

13
Oder es gibt einen, Benchmark.realtimeder einen einzigen verständlichen Schwimmer zurückgibt :)
Sergio Tulentsev

26

Die Verwendung Time.now(die die Wanduhrzeit zurückgibt) als Basislinie weist einige Probleme auf, die zu unerwartetem Verhalten führen können. Dies wird durch die Tatsache verursacht , dass die Zeit Wanduhr unterliegt Änderungen wie eingefügten Schaltsekunden oder Zeitschwenk die lokale Zeit zu einer Referenzzeit einzustellen.

Wenn während der Messung beispielsweise eine Schaltsekunde eingefügt wird, wird diese um eine Sekunde ausgeschaltet. Abhängig von den örtlichen Systembedingungen müssen Sie sich möglicherweise mit Sommerzeit, schnelleren oder langsamer laufenden Uhren oder sogar einem Zeitsprung der Uhr auseinandersetzen, was zu einer negativen Dauer und vielen anderen Problemen führt.

Eine Lösung für dieses Problem besteht darin, eine andere Uhrzeit zu verwenden: eine monotone Uhr. Dieser Uhrentyp hat andere Eigenschaften als die Wanduhr. Es erhöht sich monitonisch, dh es geht nie zurück und nimmt mit konstanter Geschwindigkeit zu. Damit stellt es nicht die Wanduhr dar (dh die Zeit, die Sie von einer Uhr an Ihrer Wand lesen), sondern einen Zeitstempel, den Sie mit einem späteren Zeitstempel vergleichen können, um einen Unterschied zu erhalten.

In Ruby können Sie einen solchen Zeitstempel Process.clock_gettime(Process::CLOCK_MONOTONIC)wie folgt verwenden:

t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# => 63988.576809828

sleep 1.5 # do some work

t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# => 63990.08359163

delta = t2 - t1
# => 1.5067818019961123
delta_in_milliseconds = delta * 1000
# => 1506.7818019961123

Das Process.clock_gettime Methode gibt einen Zeitstempel als Float mit Sekundenbruchteilen zurück. Die tatsächlich zurückgegebene Nummer hat keine definierte Bedeutung (auf die Sie sich verlassen sollten). Sie können jedoch sicher sein, dass der nächste Anruf eine größere Nummer zurückgibt, und durch Vergleichen der Werte können Sie den Echtzeitunterschied ermitteln.

Diese Attribute machen die Methode zu einem Hauptkandidaten für die Messung von Zeitunterschieden, ohne dass Ihr Programm in den ungünstigsten Zeiten fehlschlägt (z. B. um Mitternacht an Silvester, wenn eine weitere Schaltsekunde eingefügt wird).

Die Process::CLOCK_MONOTONIChier verwendete Konstante ist auf allen modernen Linux-, BSD- und macOS-Systemen sowie auf dem Linux-Subsystem für Windows verfügbar. Es ist jedoch noch nicht für "rohe" Windows-Systeme verfügbar. Dort können Sie den GetTickCount64Systemaufruf verwenden, der stattdessen Process.clock_gettimeauch einen Zeitgeberwert in Millisekunden-Granularität unter Windows zurückgibt (> = Windows Vista, Windows Server 2008).

Mit Ruby können Sie diese Funktion folgendermaßen aufrufen:

require 'fiddle'

# Get a reference to the function once
GetTickCount64 = Fiddle::Function.new(
  Fiddle.dlopen('kernel32.dll')['GetTickCount64'],
  [],
  -Fiddle::TYPE_LONG_LONG # unsigned long long
)

timestamp = GetTickCount64.call / 1000.0
# => 63988.576809828

1
Dies ist die einzige Antwort, die akzeptabel sein sollte, geschweige denn akzeptiert werden sollte.
Rob Kinyon

Vielleicht lohnt es sich, einige Details zur Betriebssystemunterstützung hinzuzufügen: SUSv3 bis 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12
Guy C

Die ursprüngliche Frage und Antwort wurden beide bereitgestellt, bevor diese Lösung verfügbar war. Warum sollte dies die einzige "akzeptable" Antwort sein?
CitizenX

13

Sie sollten sich das Benchmark-Modul ansehen, um Benchmarks durchzuführen. Als schnelle und schmutzige Timing-Methode können Sie jedoch Folgendes verwenden:

def time
  now = Time.now.to_f
  yield
  endd = Time.now.to_f
  endd - now
end

Beachten Sie die Verwendung von Time.now.to_f, die im Gegensatz zu to_i nicht auf Sekunden gekürzt wird.


6
Casting-Zeiten für Floats sind hier nicht erforderlich.
Sergio Tulentsev

auch Tippfehler in "endd"
Nightpool

@nightpool Es wird so geschrieben, weil endes ein Schlüsselwort ist.
sepp2k

1
dann mag end_timeoder so, nicht nur falsch geschriebene Wörter, das ist höllisch schlampig
Nightpool


1

Der absolute_timeEdelstein ist ein Ersatz für Benchmark, verwendet jedoch native Anweisungen, um weitaus genauer zu sein.


1
oder Hitimes Gem, das unter Windows eine höhere Granularität haben kann als absolute_time ...
Rogerdpack

1

Wir können auch eine einfache Funktion erstellen, um jeden Codeblock zu protokollieren:

def log_time
  start_at = Time.now

  yield if block_given?

  execution_time = (Time.now - start_at).round(2)
  puts "Execution time: #{execution_time}s"
end

log_time { sleep(2.545) } # Execution time: 2.55s

0

Wenn du benutzt

date = Time.now.to_i

Sie erhalten Zeit in Sekunden, was alles andere als genau ist, insbesondere wenn Sie kleine Codestücke zeitlich festlegen.


0

Die Verwendung der Time.now.to_iRückgabe der zweiten verging ab 1970/01/01. Wenn Sie das wissen, können Sie es tun

date1 = Time.now.to_f
date2 = Time.now.to_f
diff = date2 - date1

Damit haben Sie einen Unterschied in der zweiten Größe. Wenn Sie es in Millisekunden möchten , fügen Sie es einfach dem Code hinzu

diff = diff * 1000

2
.to_igibt eine ganze Zahl an und würde die Millisekunden abschneiden. Dies würde funktionieren, wenn Sie es nur in ändern.to_f
Ryan Taylor

1
Ja, wie Ryan sagt; Entschuldigung für die Ablehnung, aber dies hatte tatsächlich eine Gegenstimme und ich fühlte mich gezwungen, dem entgegenzuwirken. Multiplizieren Sie eine ganzzahlige Zeitdifferenz in Sekunden mit 1000, um Millisekunden zu erhalten? Komm schon, MARC.RS, du hast uns doch nur getrollt! :-)
Andrew Hodgkinson

Nein, ich versuche nur mein Bestes
MARC.RS

0

Ich habe ein Juwel, das Ihre Ruby-Methode (Instanz oder Klasse) profilieren kann - https://github.com/igorkasyanchuk/benchmark_methods .

Kein Code mehr wie dieser:

t = Time.now
user.calculate_report
puts Time.now - t

Jetzt können Sie tun:

benchmark :calculate_report # in class

Und rufen Sie einfach Ihre Methode auf

user.calculate_report
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.