Tempo de medição e benchmark para métodos Ruby

87

Como posso medir o tempo gasto por um método e as declarações individuais nesse método em Ruby. Se você vir o método abaixo, quero medir o tempo total gasto pelo método e o tempo gasto para acesso ao banco de dados e acesso ao redis. Não quero escrever Benchmark.measure antes de cada declaração. O intérprete ruby ​​nos dá algum gancho para fazer isso?

def foo
# code to access database
# code to access redis. 
end
Phani
fonte
há algo semelhante ao new Date()do javascript que o ruby ​​tem, mas não consigo me lembrar da sintaxe correta. deve dar-lhe uma lista decente do google
reagan
1
@Phani Você pode selecionar uma resposta correta, por favor? Após 8 anos, acho que existem algumas respostas sólidas aqui. Obrigado.
Joshua Pinter

Respostas:

113

Você pode usar o Timeobjeto. ( Time Docs )

Por exemplo,

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

diff = finish - start

diff seria em segundos, como um número de ponto flutuante.

EDITAR: endestá reservado.

wquist
fonte
13
apenas uma pequena correção. endé reservado, então use algum outro nome de variável.
Jason Kim
4
Time.nowé afetado por ajustes no relógio do sistema, portanto, é uma prática recomendada usá-lo Process.clock_gettime(Process::CLOCK_MONOTONIC). Mas para cálculos aproximados, isso não importa. blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
Patrick Brinich-Langlois
104

A maneira mais simples:

require 'benchmark'

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

Saída de amostra:

2.2.3 :001 > foo
  5.230000   0.020000   5.250000 (  5.274806)

Os valores são: tempo da cpu, tempo do sistema, tempo total e real decorrido.

Fonte: ruby docs .

Łukasz Ostrowski
fonte
40
Você também pode fazer Benchmark.realtime { block }se quiser apenas o tempo real
jmccure
35

Use Benchmark's Relatório

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

Isso resultará em algo como o seguinte:

                        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.

Mais informações podem ser encontradas aqui .

Joshua Pinter
fonte
2
Acabei de voltar à minha própria resposta e fiquei impressionado (de novo) com a forma como a Benchmark lida com isso. Amo Ruby.
Joshua Pinter
2
Esta deve ser a resposta preferida: Porque, a partir do Ruby 2.2, a Benchmarkclasse usa um relógio monotônico, conforme discutido em outras respostas. Veja, por exemplo, o seguinte código-fonte e procure por "def measure" na linha 286: github.com/ruby/ruby/blob/ruby_2_2/lib/benchmark.rb
Purplejacket
17

Muitas das respostas sugerem o uso de Time.now. Mas vale ficar atento que Time.nowpode mudar. Os relógios do sistema podem variar e podem ser corrigidos pelo administrador do sistema ou via NTP. Portanto, é possível que o Time.now avance ou retroceda e forneça resultados imprecisos ao seu benchmarking.

A melhor solução é usar o relógio monotônico do sistema operacional, que está sempre avançando. Ruby 2.1 e superior dão acesso a isso 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

Você pode ler mais detalhes aqui . Além disso, você pode ver o popular projeto Ruby, Sidekiq, que mudou para o relógio monotônico .

Guy C
fonte
7

Pensando bem, definir a função measure () com o argumento do bloco de código Ruby pode ajudar a simplificar o código de medida de 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
Houcheng
fonte
Em sua definição, você o chama benchmark. Quando você o usa, ele é chamado measure. Por favor, corrija isso.
Sandro L
4

No espírito da resposta do wquist , mas um pouco mais simples, você também pode fazer como a seguir:

start = Time.now
# code to time
Time.now - start
ADude2
fonte
Essa resposta é uma maneira (ligeiramente) diferente de responder à pergunta. Só porque você pode descobrir com a resposta de @wquist, não significa que não seja válido.
secretário secreto de
3

Olhe na ruby-profembalagem, deve ter o que você precisa. Isso criará enormes pilhas de chamadas com intervalos.

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

Pode ser muito granular; nesse caso, apenas envolver seções maiores Benchmark.measurepode ser uma boa opção.

Cody Caughlan
fonte
0

Outro método para comparar o código automaticamente no console Rails é usando esta gema: https://github.com/igorkasyanchuk/execution_time

você verá informações semelhantes à medida que obtém de solicitações em registros

Igor Kasyanchuk
fonte