Mesurer et comparer le temps pour les méthodes Ruby


87

Comment puis-je mesurer le temps pris par une méthode et les déclarations individuelles de cette méthode dans Ruby. Si vous voyez la méthode ci-dessous, je veux mesurer le temps total pris par la méthode et le temps nécessaire pour l'accès à la base de données et l'accès redis. Je ne veux pas écrire Benchmark.measure avant chaque déclaration. L'interpréteur ruby ​​nous donne-t-il des crochets pour faire cela?

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

il y a quelque chose de similaire au new Date()javascript de ruby, mais je ne me souviens pas de la syntaxe correcte. devrait vous donner une liste Google décente
reagan

1
@Phani Pouvez-vous sélectionner une bonne réponse s'il vous plaît? Après 8 ans, je pense qu'il y a des réponses solides ici. Merci.
Joshua Pinter

Réponses:


113

Vous pouvez utiliser l' Timeobjet. ( Time Docs )

Par exemple,

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

diff = finish - start

diff serait en secondes, sous forme de nombre à virgule flottante.

EDIT: endest réservé.


13
juste une petite correction. endest réservé, utilisez donc un autre nom de variable.
Jason Kim

4
Time.nowest affectée par les ajustements de l'horloge système, c'est donc une meilleure pratique à utiliser à la Process.clock_gettime(Process::CLOCK_MONOTONIC)place. Mais pour les calculs approximatifs, cela n'a pas d'importance. blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Patrick Brinich-Langlois

104

Le moyen le plus simple:

require 'benchmark'

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

Exemple de sortie:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

Les valeurs sont: temps cpu, temps système, temps total et réel écoulé.

Source: ruby docs .


40
Vous pouvez également le faire Benchmark.realtime { block }si vous voulez juste le temps réel
jmccure

35

Utiliser Benchmarkle rapport

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

Cela affichera quelque chose comme ce qui suit:

                        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.

Plus d'informations peuvent être trouvées ici .


2
Je suis juste revenu à ma propre réponse et j'ai (encore) été impressionné par la façon dont Benchmark gère cela. J'adore Ruby.
Joshua Pinter

2
Cela devrait être la réponse préférée: parce que, depuis Ruby 2.2, la Benchmarkclasse utilise une horloge monotone, comme indiqué dans d'autres réponses. Voir par exemple le code source suivant, et recherchez "def measure" sur la ligne 286: github.com/ruby/ruby/blob/ruby_2_2/lib/benchmark.rb
Purplejacket

17

Beaucoup de réponses suggèrent l'utilisation de Time.now. Mais il vaut la peine d'être conscient que cela Time.nowpeut changer. Les horloges système peuvent dériver et être corrigées par l'administrateur système ou via NTP. Il est donc possible pour Time.now de sauter en avant ou en arrière et de donner à votre benchmarking des résultats inexacts.

Une meilleure solution consiste à utiliser l'horloge monotone du système d'exploitation, qui avance toujours. Ruby 2.1 et supérieur donnent accès à cela via:

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

Vous pouvez lire plus de détails ici . Vous pouvez également voir le projet Ruby populaire, Sidekiq, qui est passé à l' horloge monotone .


7

Une seconde réflexion, définir la fonction measure () avec l'argument de bloc de code Ruby peut aider à simplifier le code de mesure du temps:

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

Dans votre définition, vous l'appelez benchmark. Lorsque vous l'utilisez, il est appelé measure. Veuillez corriger cela.
Sandro L

4

Dans l'esprit de la réponse de wquist , mais un peu plus simple, vous pouvez également le faire comme ci-dessous:

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

Cette réponse est une manière (légèrement) différente de répondre à la question. Ce n'est pas parce que vous pouvez le comprendre à partir de la réponse de @ wquist que cela n'est pas valide.
thesecretmaster

3

Regardez dans l' ruby-profemballage, il devrait avoir ce dont vous avez besoin. Cela créera d'énormes piles d'appels avec des horaires.

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

Cela peut être trop granulaire, auquel cas le simple fait d'envelopper des sections plus grandes Benchmark.measurepourrait être une bonne solution.


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.