Benchmarking methods in Ruby
我正在尝试对一组这样的计算进行基准测试-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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) |
现在我想知道我怎样才能做到这一点。我试过这个例子,但被吐出来了
1 | `func1': wrong number of arguments (0 for 2) (ArgumentError) |
如果我尝试-
1 | benchmark(func1(index1, array1), index1, array1) |
它吐出…
1 | undefined method `func' for main:Object (NoMethodError) |
我看到一个关于它的类似问题,但它是针对Python的。将带有参数的函数传递给Python中的另一个函数?有人能帮忙吗?谢谢。
在Ruby中,可以在方法名后面不包含空括号的情况下调用方法,如下所示:
1 2 3 4 5 | def func1 puts"Hello!" end func1 # Calls func1 and prints"Hello!" |
因此,在编写
1 2 3 4 5 6 | def func1 puts"Hello!" end m = method(:func1) # Returns a Method object for func1 m.call(param1, param2) |
但大多数时候,这不是你真正想做的事情。Ruby支持一个名为blocks的结构,它更适合于此目的。您可能已经熟悉了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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,它使用块,可能已经完全满足了您的需要。
用途:
1
2
3
4
5
6
7
8 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 }
endThe result:
1
2
3
4 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方法用作函数。这很少见,但完全有可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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)使用以前定义的方法创建一个
c)采用
我就是这样做的。我首先创建一个包含所有要测试的方法的模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 | module Methods def bob(array) ... end def gretta(array ... end def wilma(array) ... end end |
然后我将该模块包括在内,并将这些方法放入一个数组中:
1 2 3 | include Methods @methods = Methods.public_instance_methods(false) #=> [:bob, :gretta, :wilma] |
这样我就可以对
下面是这个方法的一个例子。您将看到我还有代码来检查所有方法是否返回相同的结果并格式化输出。
主要程序可能如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 | test_sizes.each do |n| puts" n = #{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 |
我使用一个模块,这样就可以添加、删除或重命名要进行基准测试的方法,而无需接触模块外部的任何代码。
请检查我的新gem,它可以分析您的Ruby方法(实例或类)-https://github.com/igorkasyanchuk/benchmark_方法。
不再有这样的代码:
1 2 3 | t = Time.now user.calculate_report puts Time.now - t |
现在你可以做到:
1 | benchmark :calculate_report # in class |
然后调用你的方法
1 | user.calculate_report |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | class SimpleBenchmarker def self.go(how_many=1, &block) $sorted_time = Array.new puts" --------------Benchmarking started----------------" start_time = Time.now puts"Start Time:\t#{start_time} " how_many.times do |a| print"." block.call end print" " end_time = Time.now puts"End Time:\t#{end_time} " puts"-------------Benchmarking finished---------------- " result_time = end_time - start_time puts"Total time:\t\t#{result_time.round(3)} seconds " puts"The run times for the iterations from shortest to longest was: #{$sorted_time.sort.join("s,")}s " 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 |