4

This is pretty ugly:

t = Time.now
result = do_something
elapsed = Time.now - t

I tried this:

elapsed = time do
  result = do_something
end

def time
  t = Time.now
  yield
  Time.now - t
end

This is better. But the problem is that result falls out of scope after the block ends.

So, is there a better way of doing timing? Or a good way to use the result?

B Seven
  • 44,484
  • 66
  • 240
  • 385

3 Answers3

4

A really idiomatic way would be to use the standard library. :)

require 'benchmark'

result = nil
elapsed = Benchmark.realtime do 
  result = do_something
end
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
2

You've got the right idea here, but to avoid the scope problem do this:

result = nil
elapsed = time do
  result = do_something
end
tadman
  • 208,517
  • 23
  • 234
  • 262
2

I like the way you've constructed your time method. I have no suggestions for improvement, but I will say a few words about a related problem. Suppose you wished to measure the amount of time spent executing methods. Sometimes you might be able to write something simple such as:

require 'time'

t = Time.now
rv = my_method(*args)
et = t.Time.now - t

Other times that's not convenient. Suppose, for example, you were constructing an array whose elements were the return values of my_method or my_method returned an enumerator so that it could be chained to other methods.

As an example, let's suppose you wanted to sum the values of an array until a zero is encountered. One way to do that is to construct an enumerator stop_at_zero that generates values from its receiver until it encounters a zero, then stops (i.e., raises a StopIteration exception). We could then write:

arr.stop_at_zero.reduce(:+)

If we want to know how much time is spent executing stop_at_zero we could construct it as follows.

class Array
  def stop_at_zero
    extime = Time.now
    Enumerator.new do |y|
      begin
        each do |n|
          sleep(0.5)
          return y if n.zero?
          y << n
        end
      ensure
        $timings << [__method__, Time.now - extime]
      end
    end
  end
end

I used a begin, ensure, end block to make sure $timings << [__method__, Time.now - extime] is executed when the method returns prematurely. sleep(0.5) is of course just for illustrative purposes.

Let's try it.

$timings = []
arr = [1,7,0,3,4]
arr.stop_at_zero.reduce(:+)
  #=> 8 
$timings
  #=> [[:stop_at_zero, 1.505672]] 

$timings will contain a history of execution times of all methods that contain the timing code.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100