Benchmarking Techniques in Ruby

I am trying to do a comparative set of such calculations -

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 am wondering how I can achieve this. I tried this example but spits out

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

      

If I try -

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

      

Spits out ...

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

      

I saw a similar question asked about this, but this was for python. Passing functions with arguments to another function in Python? Can anyone please help? Thank.

+3


source to share


5 answers


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

def func1
  puts "Hello!"
end

func1 # Calls func1 and prints "Hello!"

      

Because of this, when you write benchmark(func1, index1, array1)

, you are actually calling func1

with no arguments and passing the result in benchmark

without passing func1

to the reference function as you would expect. To pass func1

as an object, you can get the wrapper object for the function using a method method

like:

def func1
  puts "Hello!"
end

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

      

In most cases, this is not what 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 Tether each

, which Ruby uses to loop through arrays. This is how it would look like using 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 benchmarking library called Benchmark that uses blocks and probably does exactly what you want it to do already.

Using:

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

      

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)

      

+7


source


This is how I do it. First, I create a module containing all the methods for testing:

module Methods
  def bob(array)
    ...
  end

  def gretta(array
    ...
  end

  def wilma(array)
    ...
  end
end

      

then I include the module and put the methods into an array:

include Methods
@methods = Methods.public_instance_methods(false)
  #=> [:bob, :gretta, :wilma]

      

This allows me to execute send(meth, *args)

for each method meth

in the @methods

.



Here's an example of this approach. You will see that I also have some code to check that all methods return the same result and format the output.

The basic procedure might look something like this:

test_sizes.each do |n|
  puts "\nn = #{n}"
  arr = test_array(n)
  Benchmark.bm(@indent) do |bm|
    @methods.each do |m|
      bm.report m.to_s do
        send(m, arr)
     end
    end
  end
end

      

I use a module so that methods that can be compared, added, removed, or renamed without having to touch any code outside the module.

+1


source


It looks like you are trying to use Ruby methods as functions. This is unusual, but entirely possible.

def benchmark(func, index, array)
    start = Time.now
    func.call(index, array) # <= (C)
    start - Time.now
end

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

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

benchmark(method(:func1), index1, array1) # <= (A)
benchmark(method(:func1), index2, array2) # <= (B)

      

Your code changes look like this:

A, B) Create an object Method

from your previously defined methods. An object Method

is similar to an object Proc

in that it has a method call

that allows you to call it later. In your code, when you just use func1

, and not method(:func1)

, what happens is that you immediately call the method and pass its result to benchmark

, instead of passing that function to benchmark

for later call.

C) Use method call

. Ruby doesn't allow you to arbitrarily call variables as functions using parentheses like some other languages ​​do, but if it's an object type with a method call

like Method

or Proc

, you can use call

to call the function when you're ready, rather than immediately before try to pass it to another method, as in the source code.

+1


source


class SimpleBenchmarker
    def self.go(how_many=1, &block)
        $sorted_time = Array.new
        puts "\n--------------Benchmarking started----------------"
        start_time = Time.now
        puts "Start Time:\t#{start_time}\n\n"
        how_many.times do |a|
            print "."
            block.call
        end
        print "\n\n"
        end_time = Time.now
        puts "End Time:\t#{end_time}\n"
        puts "-------------Benchmarking finished----------------\n\n"
        result_time = end_time - start_time
        puts "Total time:\t\t#{result_time.round(3)} seconds\n\n"
        puts "The run times for the iterations from shortest to longest was: #{$sorted_time.sort.join("s, ")}s\n\n"
    end
end
    print "How many times? "
    t = gets.to_i
SimpleBenchmarker.go t do
    time = rand(0.1..1.0).round(3)
    $sorted_time.push(time)
    sleep time
end

      

0


source


Please check out my new gem that can profile your ruby ​​method (instance or class) - https://github.com/igorkasyanchuk/benchmark_methods .

No more code:

t = Time.now
user.calculate_report
puts Time.now - t

      

Now you can do:

benchmark :calculate_report # in class

      

And just call your method

user.calculate_report

      

0


source







All Articles