[前][次][番号順一覧][スレッド一覧]

ruby-changes:18721

From: naruse <ko1@a...>
Date: Tue, 1 Feb 2011 01:15:15 +0900 (JST)
Subject: [ruby-changes:18721] Ruby:r30747 (trunk): * lib/benchmark.rb: fix benchmarck to work with current ruby.

naruse	2011-02-01 01:11:06 +0900 (Tue, 01 Feb 2011)

  New Revision: 30747

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=30747

  Log:
    * lib/benchmark.rb: fix benchmarck to work with current ruby.
      patched by Benoit Daloze [ruby-core:33846] [ruby-dev:43143]
      merged from https://github.com/eregon/ruby/commits/benchmark
    
    *  lib/benchmark (Report#width): update documentation
    *  lib/benchmark: document the return value of #benchmark and the
       :list attribute in Report
    *  lib/benchmark (Tms#format): rename variables, use String#%
       instead of Kernel.format
    *  lib/benchmark: remove undocumented Benchmark::times (an alias
       of Process::times used twice)
    *  lib/benchmark (#benchmark): use label_width for the caption
    *  lib/benchmark (Tms#initialize): rename variables
    *  lib/benchmark: allow title to not be a String and call #to_s
    *  lib/benchmark (Benchmark#bm): return an Array of the times with
       the labels
    *  lib/benchmark: correct output for Benchmark#bmbm
       (remove the extra space)
    *  lib/benchmark: add a few tests for Benchmark::Tms output
    *  lib/benchmark: improve style (enumerators, ljust, unused vars)
    *  lib/benchmark: add spec about output and return value
    *  lib/benchmark: improve basic style and consistency
       no parenthesis for print and use interpolation instead of printf
    *  lib/benchmark: remove unnecessary conversions and variables
    *  lib/benchmark: correct indentation
    *  lib/benchmark: rename the FMTSTR constant and variable to FORMAT
    *  lib/benchmark: remove useless exception
    
    *  test/benchmark: remove unused variable warnings

  Modified files:
    trunk/ChangeLog
    trunk/lib/benchmark.rb
    trunk/test/benchmark/test_benchmark.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 30746)
+++ ChangeLog	(revision 30747)
@@ -1,3 +1,35 @@
+Tue Feb  1 00:10:30 2011  NARUSE, Yui  <naruse@r...>
+
+	* lib/benchmark.rb: fix benchmarck to work with current ruby.
+	  patched by Benoit Daloze [ruby-core:33846] [ruby-dev:43143]
+	  merged from https://github.com/eregon/ruby/commits/benchmark
+
+	*  lib/benchmark (Report#width): update documentation
+	*  lib/benchmark: document the return value of #benchmark and the
+	   :list attribute in Report
+	*  lib/benchmark (Tms#format): rename variables, use String#%
+	   instead of Kernel.format
+	*  lib/benchmark: remove undocumented Benchmark::times (an alias
+	   of Process::times used twice)
+	*  lib/benchmark (#benchmark): use label_width for the caption
+	*  lib/benchmark (Tms#initialize): rename variables
+	*  lib/benchmark: allow title to not be a String and call #to_s
+	*  lib/benchmark (Benchmark#bm): return an Array of the times with
+	   the labels
+	*  lib/benchmark: correct output for Benchmark#bmbm
+	   (remove the extra space)
+	*  lib/benchmark: add a few tests for Benchmark::Tms output
+	*  lib/benchmark: improve style (enumerators, ljust, unused vars)
+	*  lib/benchmark: add spec about output and return value
+	*  lib/benchmark: improve basic style and consistency
+	   no parenthesis for print and use interpolation instead of printf
+	*  lib/benchmark: remove unnecessary conversions and variables
+	*  lib/benchmark: correct indentation
+	*  lib/benchmark: rename the FMTSTR constant and variable to FORMAT
+	*  lib/benchmark: remove useless exception
+
+	*  test/benchmark: remove unused variable warnings
+
 Mon Jan 31 23:27:23 2011  CHIKANAGA Tomoyuki  <nagachika00@g...>
 
 	* node.c (add_id): remove duplicated rb_id2str() call.
Index: lib/benchmark.rb
===================================================================
--- lib/benchmark.rb	(revision 30746)
+++ lib/benchmark.rb	(revision 30747)
@@ -103,10 +103,10 @@
 #   using the #benchmark method:
 #
 #       require 'benchmark'
-#       include Benchmark         # we need the CAPTION and FMTSTR constants
+#       include Benchmark         # we need the CAPTION and FORMAT constants
 #
 #       n = 50000
-#       Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x|
+#       Benchmark.benchmark(CAPTION, 7, FORMAT, ">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 }
 #         tu = x.report("upto:")  { 1.upto(n) do ; a = "1"; end }
@@ -126,16 +126,13 @@
 
   BENCHMARK_VERSION = "2002-04-25" #:nodoc"
 
-  def Benchmark::times() # :nodoc:
-      Process::times()
-  end
-
-
   # Invokes the block with a <tt>Benchmark::Report</tt> object, which
   # may be used to collect and report on the results of individual
   # benchmark tests. Reserves <i>label_width</i> leading spaces for
   # labels on each line. Prints _caption_ at the top of the
-  # report, and uses _fmt_ to format each line.
+  # report, and uses _format_ to format each line.
+  # Returns an array of Benchmark::Tms objects.
+  #
   # If the block returns an array of
   # <tt>Benchmark::Tms</tt> objects, these will be used to format
   # additional lines of output. If _label_ parameters are
@@ -148,10 +145,10 @@
   # Example:
   #
   #     require 'benchmark'
-  #     include Benchmark          # we need the CAPTION and FMTSTR constants
+  #     include Benchmark          # we need the CAPTION and FORMAT constants
   #
   #     n = 50000
-  #     Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x|
+  #     Benchmark.benchmark(CAPTION, 7, FORMAT, ">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 }
   #       tu = x.report("upto:")  { 1.upto(n) do ; a = "1"; end }
@@ -168,19 +165,19 @@
   #        >avg:    1.333333   0.011111   1.344444 (  0.629761)
   #
 
-  def benchmark(caption = "", label_width = nil, fmtstr = nil, *labels) # :yield: report
+  def benchmark(caption = "", label_width = nil, format = nil, *labels) # :yield: report
     sync = STDOUT.sync
     STDOUT.sync = true
     label_width ||= 0
-    fmtstr ||= FMTSTR
-    raise ArgumentError, "no block" unless iterator?
-    print caption
-    results = yield(Report.new(label_width, fmtstr))
+    format ||= FORMAT
+    print ' '*label_width + caption
+    report = Report.new(label_width, format)
+    results = yield(report)
     Array === results and results.grep(Tms).each {|t|
-      print((labels.shift || t.label || "").ljust(label_width),
-            t.format(fmtstr))
+      print((labels.shift || t.label || "").ljust(label_width), t.format(format))
     }
     STDOUT.sync = sync
+    report.list
   end
 
 
@@ -205,7 +202,7 @@
   #
 
   def bm(label_width = 0, *labels, &blk) # :yield: report
-    benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels, &blk)
+    benchmark(CAPTION, label_width, FORMAT, *labels, &blk)
   end
 
 
@@ -254,35 +251,24 @@
     STDOUT.sync = true
 
     # rehearsal
-    print "Rehearsal "
-    puts '-'*(width+CAPTION.length - "Rehearsal ".length)
-    list = []
-    job.list.each{|label,item|
-      print(label.ljust(width))
-      res = Benchmark::measure(&item)
-      print res.format()
-      list.push res
-    }
-    sum = Tms.new; list.each{|i| sum += i}
-    ets = sum.format("total: %tsec")
-    printf("%s %s\n\n",
-           "-"*(width+CAPTION.length-ets.length-1), ets)
+    puts 'Rehearsal '.ljust(width+CAPTION.length,'-')
+    ets = job.list.inject(Tms.new) { |sum,(label,item)|
+      print label.ljust(width)
+      res = Benchmark.measure(&item)
+      print res.format
+      sum + res
+    }.format("total: %tsec")
+    print " #{ets}\n\n".rjust(width+CAPTION.length+2,'-')
 
     # take
-    print ' '*width, CAPTION
-    list = []
-    ary = []
-    job.list.each{|label,item|
-      GC::start
+    print ' '*width + CAPTION
+    job.list.map { |label,item|
+      GC.start
       print label.ljust(width)
-      res = Benchmark::measure(&item)
-      print res.format()
-      ary.push res
-      list.push [label, res]
+      Benchmark.measure(&item).tap { |res| print res.format }
+    }.tap {
+      STDOUT.sync = sync
     }
-
-    STDOUT.sync = sync
-    ary
   end
 
   #
@@ -290,9 +276,9 @@
   # Benchmark::Tms object.
   #
   def measure(label = "") # :yield:
-    t0, r0 = Benchmark.times, Time.now
+    t0, r0 = Process.times, Time.now
     yield
-    t1, r1 = Benchmark.times, Time.now
+    t1, r1 = Process.times, Time.now
     Benchmark::Tms.new(t1.utime  - t0.utime,
                        t1.stime  - t0.stime,
                        t1.cutime - t0.cutime,
@@ -307,12 +293,11 @@
   def realtime(&blk) # :yield:
     r0 = Time.now
     yield
-    r1 = Time.now
-    r1.to_f - r0.to_f
+    Time.now - r0
   end
 
+  module_function :benchmark, :measure, :realtime, :bm, :bmbm
 
-
   #
   # A Job is a sequence of labelled blocks to be processed by the
   # Benchmark.bmbm method.  It is of little direct interest to the user.
@@ -335,10 +320,10 @@
     #
     def item(label = "", &blk) # :yield:
       raise ArgumentError, "no block" unless block_given?
-      label += ' '
+      label = label.to_s
       w = label.length
       @width = w if @width < w
-      @list.push [label, blk]
+      @list << [label, blk]
       self
     end
 
@@ -347,14 +332,10 @@
     # An array of 2-element arrays, consisting of label and block pairs.
     attr_reader :list
 
-    # Length of the widest label in the #list, plus one.
+    # Length of the widest label in the #list.
     attr_reader :width
   end
 
-  module_function :benchmark, :measure, :realtime, :bm, :bmbm
-
-
-
   #
   # This class is used by the Benchmark.benchmark and Benchmark.bm methods.
   # It is of little direct interest to the user.
@@ -364,26 +345,29 @@
     # Returns an initialized Report instance.
     # Usually, one doesn't call this method directly, as new
     # Report objects are created by the #benchmark and #bm methods.
-    # _width_ and _fmtstr_ are the label offset and
+    # _width_ and _format_ are the label offset and
     # format string used by Tms#format.
     #
-    def initialize(width = 0, fmtstr = nil)
-      @width, @fmtstr = width, fmtstr
+    def initialize(width = 0, format = nil)
+      @width, @format, @list = width, format, []
     end
 
     #
     # Prints the _label_ and measured time for the block,
-    # formatted by _fmt_. See Tms#format for the
+    # formatted by _format_. See Tms#format for the
     # formatting rules.
     #
-    def item(label = "", *fmt, &blk) # :yield:
-      print label.ljust(@width)
-      res = Benchmark::measure(&blk)
-      print res.format(@fmtstr, *fmt)
+    def item(label = "", *format, &blk) # :yield:
+      print label.to_s.ljust(@width)
+      @list << res = Benchmark.measure(label, &blk)
+      print res.format(@format, *format)
       res
     end
 
     alias report item
+
+    # An array of Benchmark::Tms objects representing each item.
+    attr_reader :list
   end
 
 
@@ -394,7 +378,7 @@
   #
   class Tms
     CAPTION = "      user     system      total        real\n"
-    FMTSTR = "%10.6u %10.6y %10.6t %10.6r\n"
+    FORMAT = "%10.6u %10.6y %10.6t %10.6r\n"
 
     # User CPU time
     attr_reader :utime
@@ -419,13 +403,12 @@
 
     #
     # Returns an initialized Tms object which has
-    # _u_ as the user CPU time, _s_ as the system CPU time,
-    # _cu_ as the children's user CPU time, _cs_ as the children's
-    # system CPU time, _real_ as the elapsed real time and _l_
-    # as the label.
+    # _utime_ as the user CPU time, _stime_ as the system CPU time,
+    # _cutime_ as the children's user CPU time, _cstime_ as the children's
+    # system CPU time, _real_ as the elapsed real time and _label_ as the label.
     #
-    def initialize(u = 0.0, s = 0.0, cu = 0.0, cs = 0.0, real = 0.0, l = nil)
-      @utime, @stime, @cutime, @cstime, @real, @label = u, s, cu, cs, real, l
+    def initialize(utime = 0.0, stime = 0.0, cutime = 0.0, cstime = 0.0, real = 0.0, label = nil)
+      @utime, @stime, @cutime, @cstime, @real, @label = utime, stime, cutime, cstime, real, label.to_s
       @total = @utime + @stime + @cutime + @cstime
     end
 
@@ -434,14 +417,14 @@
     # Tms object, plus the time required to execute the code block (_blk_).
     #
     def add(&blk) # :yield:
-      self + Benchmark::measure(&blk)
+      self + Benchmark.measure(&blk)
     end
 
     #
     # An in-place version of #add.
     #
     def add!(&blk)
-      t = Benchmark::measure(&blk)
+      t = Benchmark.measure(&blk)
       @utime  = utime + t.utime
       @stime  = stime + t.stime
       @cutime = cutime + t.cutime
@@ -492,19 +475,19 @@
     # <tt>%r</tt>::     Replaced by the elapsed real time, as reported by Tms#real
     # <tt>%n</tt>::     Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame")
     #
-    # If _fmtstr_ is not given, FMTSTR is used as default value, detailing the
+    # If _format_ is not given, FORMAT is used as default value, detailing the
     # user, system and real elapsed time.
     #
-    def format(arg0 = nil, *args)
-      fmtstr = (arg0 || FMTSTR).dup
-      fmtstr.gsub!(/(%[-+\.\d]*)n/){"#{$1}s" % label}
-      fmtstr.gsub!(/(%[-+\.\d]*)u/){"#{$1}f" % utime}
-      fmtstr.gsub!(/(%[-+\.\d]*)y/){"#{$1}f" % stime}
-      fmtstr.gsub!(/(%[-+\.\d]*)U/){"#{$1}f" % cutime}
-      fmtstr.gsub!(/(%[-+\.\d]*)Y/){"#{$1}f" % cstime}
-      fmtstr.gsub!(/(%[-+\.\d]*)t/){"#{$1}f" % total}
-      fmtstr.gsub!(/(%[-+\.\d]*)r/){"(#{$1}f)" % real}
-      arg0 ? Kernel::format(fmtstr, *args) : fmtstr
+    def format(format = nil, *args)
+      str = (format || FORMAT).dup
+      str.gsub!(/(%[-+\.\d]*)n/) { "#{$1}s" % label }
+      str.gsub!(/(%[-+\.\d]*)u/) { "#{$1}f" % utime }
+      str.gsub!(/(%[-+\.\d]*)y/) { "#{$1}f" % stime }
+      str.gsub!(/(%[-+\.\d]*)U/) { "#{$1}f" % cutime }
+      str.gsub!(/(%[-+\.\d]*)Y/) { "#{$1}f" % cstime }
+      str.gsub!(/(%[-+\.\d]*)t/) { "#{$1}f" % total }
+      str.gsub!(/(%[-+\.\d]*)r/) { "(#{$1}f)" % real }
+      format ? str % args : str
     end
 
     #
@@ -549,7 +532,7 @@
   CAPTION = Benchmark::Tms::CAPTION
 
   # The default format string used to display times.  See also Benchmark::Tms#format.
-  FMTSTR = Benchmark::Tms::FMTSTR
+  FORMAT = Benchmark::Tms::FORMAT
 end
 
 if __FILE__ == $0
@@ -557,15 +540,15 @@
 
   n = ARGV[0].to_i.nonzero? || 50000
   puts %Q([#{n} times iterations of `a = "1"'])
-  benchmark("       " + CAPTION, 7, FMTSTR) do |x|
-    x.report("for:")   {for _ in 1..n; _ = "1"; end} # Benchmark::measure
+  benchmark("       " + CAPTION, 7, FORMAT) do |x|
+    x.report("for:")   {for _ in 1..n; _ = "1"; end} # Benchmark.measure
     x.report("times:") {n.times do   ; _ = "1"; end}
     x.report("upto:")  {1.upto(n) do ; _ = "1"; end}
   end
 
   benchmark do
     [
-      measure{for _ in 1..n; _ = "1"; end},  # Benchmark::measure
+      measure{for _ in 1..n; _ = "1"; end},  # Benchmark.measure
       measure{n.times do   ; _ = "1"; end},
       measure{1.upto(n) do ; _ = "1"; end}
     ]
Index: test/benchmark/test_benchmark.rb
===================================================================
--- test/benchmark/test_benchmark.rb	(revision 30746)
+++ test/benchmark/test_benchmark.rb	(revision 30747)
@@ -1,10 +1,166 @@
-require "test/unit"
-require "benchmark"
+require 'minitest/spec'
+require 'benchmark'
 
-class TestBenchmark < Test::Unit::TestCase
-  def test_add!
-    assert_nothing_raised("[ruby-dev:40906]") do
-      Benchmark::Tms.new.add! {}
+MiniTest::Unit.autorun
+
+describe Benchmark do
+  BENCH_FOR_TIMES_UPTO = lambda do |x|
+    n = 1000
+    tf = x.report("for:")   { for _ in 1..n; '1'; end }
+    tt = x.report("times:") { n.times do   ; '1'; end }
+    tu = x.report("upto:")  { 1.upto(n) do ; '1'; end }
+    [tf+tt+tu, (tf+tt+tu)/3]
+  end
+
+  BENCH_FOR_TIMES_UPTO_NO_LABEL = lambda do |x|
+    n = 1000
+    x.report { for _ in 1..n; '1'; end }
+    x.report { n.times do   ; '1'; end }
+    x.report { 1.upto(n) do ; '1'; end }
+  end
+
+  def labels
+    %w[first second third]
+  end
+
+  def bench(type = :bm, *args, &block)
+    if block
+      Benchmark.send(type, *args, &block)
+    else
+      Benchmark.send(type, *args) do |x|
+        labels.each { |label|
+          x.report(label) {}
+        }
+      end
     end
   end
+
+  def capture_output
+    capture_io { yield }.first.gsub(/\d\.\d{6}/, '--time--')
+  end
+
+  def capture_bench_output(type, *args, &block)
+    capture_output { bench(type, *args, &block) }
+  end
+
+  describe 'Tms' do
+    it 'outputs nicely' do
+      Benchmark::Tms.new.to_s.must_equal            "  0.000000   0.000000   0.000000 (  0.000000)\n"
+      Benchmark::Tms.new(1,2,3,4,5).to_s.must_equal "  1.000000   2.000000  10.000000 (  5.000000)\n"
+      Benchmark::Tms.new(1,2,3,4,5,'label').format('%u %y %U %Y %t %r %n').must_equal \
+        "1.000000 2.000000 3.000000 4.000000 10.000000 (5.000000) label"
+      Benchmark::Tms.new(1).format('%u %.3f', 2).must_equal "1.000000 2.000"
+    end
+
+    it 'wont modify the format String given' do
+      format = "format %u"
+      Benchmark::Tms.new.format(format)
+      format.must_equal "format %u"
+    end
+  end
+
+  describe 'benchmark' do
+    it 'makes extra calcultations with an Array at the end of the benchmark and show the result' do
+      capture_bench_output(:benchmark,
+        Benchmark::CAPTION, 7,
+        Benchmark::FORMAT, ">total:", ">avg:",
+        &BENCH_FOR_TIMES_UPTO).must_equal BENCHMARK_OUTPUT_WITH_TOTAL_AVG
+    end
+  end
+
+  describe 'bm' do
+    it "returns an Array of the times with the labels" do
+      capture_io do
+        results = bench
+        results.must_be_instance_of Array
+        results.size.must_equal labels.size
+        results.zip(labels).each { |tms, label|
+          tms.must_be_instance_of Benchmark::Tms
+          tms.label.must_equal label
+        }
+      end
+    end
+
+    it 'correctly guess the label width even when not given' do
+      skip :not_implemented
+      capture_bench_output(:bm).must_equal BM_OUTPUT
+    end
+
+    it 'correctly output when the label width is given' do
+      capture_bench_output(:bm, 6).must_equal BM_OUTPUT
+    end
+
+    it 'correctly output when no label is given' do
+      capture_bench_output(:bm, &BENCH_FOR_TIMES_UPTO_NO_LABEL).must_equal BM_OUTPUT_NO_LABEL
+    end
+
+    it 'can make extra calcultations with an array at the end of the benchmark' do
+      capture_bench_output(:bm, 7, ">total:", ">avg:",
+        &BENCH_FOR_TIMES_UPTO).must_equal BENCHMARK_OUTPUT_WITH_TOTAL_AVG
+    end
+  end
+
+  describe 'bmbm' do
+    it 'correctly guess the label width even when not given' do
+      capture_bench_output(:bmbm).must_equal BMBM_OUTPUT
+    end
+
+    it 'correctly output when the label width is given (bmbm ignore it, but it is a frequent mistake)' do
+      capture_bench_output(:bmbm, 6).must_equal BMBM_OUTPUT
+    end
+  end
+
+  describe 'Report' do
+    describe '#item' do
+      it 'shows the title, even if not a string' do
+        capture_bench_output(:bm) { |x| x.report(:title) {} }.must_include 'title'
+        capture_bench_output(:bmbm) { |x| x.report(:title) {} }.must_include 'title'
+      end
+    end
+  end
+
+  describe 'Bugs' do
+    it '[ruby-dev:40906] can add in-place the time of execution of the block given' do
+      t = Benchmark::Tms.new
+      t.real.must_equal 0
+      t.add! {}
+      t.real.wont_equal 0
+    end
+  end
 end
+
+BM_OUTPUT = <<BENCH
+            user     system      total        real
+first   --time--   --time--   --time-- (  --time--)
+second  --time--   --time--   --time-- (  --time--)
+third   --time--   --time--   --time-- (  --time--)
+BENCH
+
+BM_OUTPUT_NO_LABEL = <<BENCH
+      user     system      total        real
+  --time--   --time--   --time-- (  --time--)
+  --time--   --time--   --time-- (  --time--)
+  --time--   --time--   --time-- (  --time--)
+BENCH
+
+BMBM_OUTPUT = <<BENCH
+Rehearsal -----------------------------------------
+first   --time--   --time--   --time-- (  --time--)
+second  --time--   --time--   --time-- (  --time--)
+third   --time--   --time--   --time-- (  --time--)
+-------------------------------- total: --time--sec
+
+            user     system      total        real
+first   --time--   --time--   --time-- (  --time--)
+second  --time--   --time--   --time-- (  --time--)
+third   --time--   --time--   --time-- (  --time--)
+BENCH
+
+BENCHMARK_OUTPUT_WITH_TOTAL_AVG = <<BENCH
+             user     system      total        real
+for:     --time--   --time--   --time-- (  --time--)
+times:   --time--   --time--   --time-- (  --time--)
+upto:    --time--   --time--   --time-- (  --time--)
+>total:  --time--   --time--   --time-- (  --time--)
+>avg:    --time--   --time--   --time-- (  --time--)
+BENCH

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]