我正在尝试对一组这样的计算进行基准测试 -
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 的。将带有参数的函数传递给 Python 中的另一个函数?有人可以协助吗?谢谢。
在 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 支持一种称为块的构造,它更适合此目的。您可能已经熟悉 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
结果:
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)
您似乎正在尝试将 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
在准备好时调用函数,而不是在尝试将其传递给另一个方法之前立即调用, 就像您的原始代码一样。
这是我的做法。 我首先创建一个包含所有要测试的方法的模块:
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
我使用一个模块,以便可以添加、删除或重命名要基准测试的方法,而无需接触模块外部的任何代码。
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}nn"
how_many.times do |a|
print "."
block.call
end
print "nn"
end_time = Time.now
puts "End Time:t#{end_time}n"
puts "-------------Benchmarking finished----------------nn"
result_time = end_time - start_time
puts "Total time:tt#{result_time.round(3)} secondsnn"
puts "The run times for the iterations from shortest to longest was: #{$sorted_time.sort.join("s, ")}snn"
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
请检查我的新 gem,它可以分析您的 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