Issue #5809 has been updated by Benoit Daloze.


Thomas Sawyer wrote:
> All makes very good sense. But I would avoid complexity. Sometimes things need to change, people have to adjust. I'm not sure I ever even knew that #report returned the time, and I imagine you are right, it was rarely used, so I don't think it is too much to ask.

Thanks, I believe dropping the unused feature is the way to go, but I want to know other's opinions.

> OTOH, maybe just let people worry about spacing themselves. 

I think complicated formatters should not be part of the library, but aligning the times make them easier to read and still easy to parse (and the implementation is small). I especially want to avoid the user to have to think about spaces when making a benchmark.

> But I also don't like the "header -> array mapping" thing --that's a pretty fugly API. I'd much rather do something like:
> 
>   Benchmark.bm do |x|
>    tf = x.report("for:   ") { for i in 1..n; a = "1"; end }
>    tt = x.report("times: ") { n.times do  ; a = "1"; end }
>          x.report("total: ", "#{tf + tt}")
>          x.report("avg:   ", "#{(tf+tt)/3}")
>   end

Yeah, it's a rather special API. Thanks for the code, it gave me some ideas to implement this kind of feature.
----------------------------------------
Feature #5809: Benchmark#bm: remove the label_width parameter
https://bugs.ruby-lang.org/issues/5809

Author: Benoit Daloze
Status: Open
Priority: Normal
Assignee: 
Category: lib
Target version: 


Hello,

I would like to keep on improving the benchmark library.
Feature #4197 was mostly a clean-up, this intend to improve the existing methods.

First, I would like to make Benchmark#bm smarter.
I think than having to specify the maximum width of the labels as an argument is not natural, and probably not the best API.
An easy way to calculate the maximum width is to store all blocks and labels and run them after the #bm block is yielded.
That's the way #bmbm does it (although some people don't know #bmbm can calculate the width itself).
It would also avoid the duplication between the Report and Job classes, and make #bm more consistent with #bmbm.

There is however a good reason it is not done this way:
It lets #report return immediately the Benchmark::Tms, which can then be assigned to a variable, and be used to easily display the total, average, etc:

Benchmark.bm(7, ">total:", ">avg:") do |x|
  tf = x.report("for:")   { for i in 1..n; a = "1"; end }
  tt = x.report("times:") { n.times do   ; a = "1"; end }
 [tf+tt, (tf+tt)/3]
end
# =>
             user     system      total        real
for:     0.000000   0.000000   0.000000 (  0.000054)
times:   0.000000   0.000000   0.000000 (  0.000027)
>total:  0.000000   0.000000   0.000000 (  0.000081)
>avg:    0.000000   0.000000   0.000000 (  0.000027)

I am not sure this is worth having to give the width.
I think this feature is very rarely used (I actually never saw a code using it, except the code in the documentation). Please show me if I'm wrong.

There are some workarounds.

First, I made Benchmark#benchmark returns the times of the #reports (an Array of Tms).
One can then easily store the results of #bm and print them (however it will not indent them automatically, but that could be fixed).

tf,tt = Benchmark.bm { ... }
puts ">total: #{(tf+tt)}"
puts ">avg:  #{(tf+tt)/3}"

Second, it is actually possible to support the same feature, even when the #report blocks are executed after.
This is done by creating "delay-able"/lazy objects, which remember the calls, and make the real calls by calling #compute on them.
Benchmark#benchmark could then execute them when the #bm block is yielded, and it would be transparent to the user (except if he prints the result of #report inside the #bm block).

The second is not small to implement, and might not belong to the benchmark library, but could be useful in general. It also adds some significant complexity.

What do you think?



-- 
http://redmine.ruby-lang.org