Mierz i porównuj czas dla metod Ruby


87

Jak mogę zmierzyć czas potrzebny na metodę i poszczególne instrukcje w tej metodzie w Rubim. Jeśli widzisz poniższą metodę, chcę zmierzyć całkowity czas potrzebny na tę metodę oraz czas potrzebny na dostęp do bazy danych i dostęp do redis. Nie chcę pisać Benchmark.measure przed każdym stwierdzeniem. Czy interpreter języka ruby ​​daje nam jakieś wskazówki, jak to zrobić?

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

jest coś podobnego do new Date()javascript, który ma Ruby, ale nie pamiętam poprawnej składni. powinien dać ci przyzwoity wpis w Google
reagan

1
@Phani Czy możesz wybrać poprawną odpowiedź? Myślę, że po 8 latach są tutaj solidne odpowiedzi. Dzięki.
Joshua Pinter

Odpowiedzi:


113

Możesz użyć Timeobiektu. ( Dokumenty czasu )

Na przykład,

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

diff = finish - start

diff będzie w sekundach, jako liczba zmiennoprzecinkowa.

EDYCJA: endjest zarezerwowana.


13
tylko drobna korekta. endjest zarezerwowana, więc użyj innej nazwy zmiennej.
Jason Kim,

4
Time.nowzależy od zmian w zegarze systemowym, więc najlepiej jest użyć go Process.clock_gettime(Process::CLOCK_MONOTONIC)zamiast tego. Ale dla przybliżonych obliczeń to nie ma znaczenia. blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Patrick Brinich-Langlois

104

Najprostszy sposób:

require 'benchmark'

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

Przykładowe dane wyjściowe:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

Wartości to: czas procesora, czas systemowy, całkowity i rzeczywisty czas, który upłynął.

Źródło: ruby docs .


40
Możesz także to zrobić, Benchmark.realtime { block }jeśli chcesz tylko czasu rzeczywistego
jmccure

35

Użyj Benchmarkraportu

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

Spowoduje to wyświetlenie czegoś podobnego do następującego:

                        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.

Więcej informacji można znaleźć tutaj .


2
Właśnie wróciłem do własnej odpowiedzi i byłem pod wrażeniem (ponownie), jak radzi sobie z tym Benchmark. Uwielbiam Ruby.
Joshua Pinter

2
Powinna to być preferowana odpowiedź: ponieważ od wersji Ruby 2.2 Benchmarkklasa używa zegara monotonicznego, co omówiono w innych odpowiedziach. Zobacz na przykład następujący kod źródłowy i poszukaj "def measure" w linii 286: github.com/ruby/ruby/blob/ruby_2/lib/benchmark.rb
Purplejacket

17

Wiele odpowiedzi sugeruje użycie Time.now. Ale warto mieć świadomość, że Time.nowmoże się to zmienić. Zegary systemowe mogą dryfować i mogą być korygowane przez administratora systemu lub przez NTP. Dlatego Time.now może przeskoczyć do przodu lub do tyłu i podać niedokładne wyniki benchmarkingu.

Lepszym rozwiązaniem jest użycie monotonicznego zegara systemu operacyjnego, który zawsze posuwa się do przodu. Ruby 2.1 i nowsze dają dostęp do tego poprzez:

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

Możesz przeczytać więcej szczegółów tutaj . Możesz również zobaczyć popularny projekt Ruby, Sidekiq, który przeszedł na zegar monotoniczny .


7

Druga myśl, zdefiniowanie funkcji Measure () z argumentem bloku kodu Ruby może pomóc uprościć kod miary czasu:

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

W swojej definicji nazywasz to benchmark. Kiedy go używasz, nazywa się measure. Proszę to naprawić.
Sandro L

4

W duchu odpowiedzi wquista , ale trochę prostszej, możesz to również zrobić jak poniżej:

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

Ta odpowiedź jest (nieco) innym sposobem odpowiedzi na pytanie. To, że możesz to rozgryźć na podstawie odpowiedzi @ wquist, nie oznacza, że ​​jest nieprawidłowa.
thesecretmaster

3

Zajrzyj do ruby-profopakowania, powinno zawierać to, czego potrzebujesz. Stworzy ogromne stosy wywołań z czasami.

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

Może być zbyt ziarnisty, w takim przypadku samo zawijanie większych sekcji Benchmark.measuremoże być dobrym rozwiązaniem.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.