user3749140 user3749140 - 11 days ago 5
Ruby Question

Benchmarking methods in Ruby

I am trying to benchmark a set of computations like so -

def benchmark(func, index, array)
start = Time.now
func(index, array)
start - Time.now #returns time taken to perform func
end

def func1(index, array)
#perform computations based on index and array
end

def func2(index, array)
#more computations....
end

benchmark(func1, index1, array1)
benchmark(func1, index2, array2)


Now I'm wondering how can I achieve this. I tried this example, but is spits out

`func1': wrong number of arguments (0 for 2) (ArgumentError)


If I try -

benchmark(func1(index1, array1), index1, array1)


It spits out...

undefined method `func' for main:Object (NoMethodError)


I saw a similar question asked about it, but it was for python. Passing functions with arguments to another function in Python?
Can someone assist? Thanks.

Answer

In Ruby, methods can be called without including empty parentheses after the method name, like so:

def func1
  puts "Hello!"
end

func1 # Calls func1 and prints "Hello!"

Because of this, when you write benchmark(func1, index1, array1), you're actually calling func1 with no arguments and passing the result to benchmark, not passing func1 to the benchmark function as you expected. In order to pass func1 as an object, you may obtain a wrapper object for the function using the method method, like this:

def func1
  puts "Hello!"
end

m = method(:func1) # Returns a Method object for func1
m.call(param1, param2)

Most of the time though, that's not something you really want to do. Ruby supports a construct called blocks which is much better suited for this purpose. You may already be familiar with blocks from the each iterator Ruby uses for looping through arrays. Here's what it would look like to use blocks for your use case:

def benchmark
  start = Time.now
  yield
  Time.now - start # Returns time taken to perform func
end

# Or alternately:
# def benchmark(&block)
#   start = Time.now
#   block.call
#   Time.now - start # Returns time taken to perform func
# end

def func1(index, array)
    # Perform computations based on index and array
end 

def func2(index, array)
    # More computations....
end

benchmark { func1(index1, array1) }
benchmark { func1(index1, array2) }

In fact, Ruby has a standard library for benchmarking called Benchmark which uses blocks and probably already does exactly what you want.

Usage:

require 'benchmark'

n = 5000000
Benchmark.bm do |x|
  x.report { for i in 1..n; a = "1"; end }
  x.report { n.times do   ; a = "1"; end }
  x.report { 1.upto(n) do ; a = "1"; end }
end

The result:

    user     system      total        real
1.010000   0.000000   1.010000 (  1.014479)
1.000000   0.000000   1.000000 (  0.998261)
0.980000   0.000000   0.980000 (  0.981335)
Comments