Ruby中的基准测试方法

3

我正在尝试进行一组计算的基准测试,如下所示 -

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)

现在我想知道如何实现这个。我尝试了这个例子,但是它输出了


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

如果我试图 -
benchmark(func1(index1, array1), index1, array1)

它会输出...
undefined method `func' for main:Object (NoMethodError)

我看到有一个类似的问题是关于Python的,有人问如何将带有参数的函数传递给另一个函数。 传递带有参数的函数到另一个函数中? 需要帮忙吗?谢谢。

5个回答

8
在Ruby中,方法可以在方法名后不包含空括号的情况下被调用,如下所示:
def func1
  puts "Hello!"
end

func1 # Calls func1 and prints "Hello!"

因此,当您编写benchmark(func1, index1, array1)时,实际上是调用func1而不带任何参数并将结果传递给benchmark,而不是像您所期望的那样将func1传递给基准测试函数。为了将func1作为对象传递,您可以使用method方法获取该函数的包装对象,就像这样:
def func1
  puts "Hello!"
end

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

大多数情况下,这并不是你真正想做的事情。Ruby支持一种称为块的结构,非常适合此目的。您可能已经熟悉了从each迭代器中使用的块,用于循环遍历数组。以下是在您的用例中使用块的示例:
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) }

事实上,Ruby有一个用于基准测试的标准库Benchmark,它使用块并且可能已经完全符合您的需求。
用法:
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)

1

这是我的做法。首先,我创建一个包含所有要测试的方法的模块:

module Methods
  def bob(array)
    ...
  end

  def gretta(array
    ...
  end

  def wilma(array)
    ...
  end
end

然后我包含该模块并将方法放入数组中:
include Methods
@methods = Methods.public_instance_methods(false)
  #=> [:bob, :gretta, :wilma]

那让我可以为@methods中的每个方法meth执行send(meth, *args)
这里有一个示例(链接),你会看到我还有代码来检查所有方法是否返回相同的结果并格式化输出。
主要程序可能如下所示:
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

我使用一个模块,以便可以添加、删除或重命名要进行基准测试的方法,而无需触及模块外部的任何代码。

1

看起来你正在尝试将Ruby方法用作函数。虽然不常见,但完全可以实现。

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)

您的代码更改如下:
A、B)将之前定义的方法创建为一个“Method”对象。一个“Method”对象类似于一个“Proc”对象,因为它有一个“call”方法,允许您稍后调用它。在您的代码中,当您只使用“func1”而不是“method(:func1)”时,发生的情况是您立即调用该方法并将其结果传递给“benchmark”,而不是将函数本身传递到“benchmark”以便稍后调用。
C)使用“call”方法。Ruby不允许您像其他一些语言那样任意使用括号将变量作为函数调用,但如果它是具有“call”方法的对象类型,例如“Method”或“Proc”,则可以在准备好后使用“call”来调用函数,而不是在尝试将其立即传递给另一个方法之前立即调用它,就像您原始的代码中一样。

0
请查看我的新宝石,它可以对您的Ruby方法(实例或类)进行分析 - https://github.com/igorkasyanchuk/benchmark_methods
不再需要像这样的代码:
t = Time.now
user.calculate_report
puts Time.now - t

现在你可以做:

benchmark :calculate_report # in class

然后只需调用您的方法

user.calculate_report

0
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接