论坛首页 编程语言技术论坛

Ruby Benchmark的实现

浏览 2246 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-04-01   最后修改:2009-04-01
以我们常用的benchmark方法bm()为例,

请看下面这个例子:
    Benchmark.bm(20) do |x|
      x.report("getBWIL"){1000.times{result.getBindedWebIdList(0, '22000005')}}
      x.report("getBWIL"){5000.times{result.getBindedWebIdList(0, '22000005')}}
      x.report("getBWIL"){10000.times{result.getBindedWebIdList(0, '22000005')}}
    end


  def bm(label_width = 0, *labels, &blk) # :yield: report
    benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels, &blk)
  end

可以看到,源码里它是调用了benchmark方法,跳到benchmark

  def benchmark(caption = "", label_width = nil, fmtstr = 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))
    Array === results and results.grep(Tms).each {|t|
      print((labels.shift || t.label || "").ljust(label_width), 
            t.format(fmtstr))
    }
    STDOUT.sync = sync
  end

这里的代码我们只看yield,这就是例子中的那个传递给bm的block的x,是个Report对象。

  class Report # :nodoc:
    #
    # 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 
    # format string used by Tms#format. 
    # 
    def initialize(width = 0, fmtstr = nil)
      @width, @fmtstr = width, fmtstr
    end

    #
    # Prints the _label_ and measured time for the block,
    # formatted by _fmt_. 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)
      res
    end

    alias report item
  end

进入Report后发现,x.report其实就是Report.item,item方法中关键的实现是Benchmark::measure,继续跟进

  def measure(label = "") # :yield:
    t0, r0 = Benchmark.times, Time.now
    yield
    t1, r1 = Benchmark.times, Time.now
    Benchmark::Tms.new(t1.utime  - t0.utime, 
                       t1.stime  - t0.stime, 
                       t1.cutime - t0.cutime, 
                       t1.cstime - t0.cstime, 
                       r1.to_f - r0.to_f,
                       label)
  end


这里看到benchmark中真正负责计算执行时间的算法,很容易理解,但有个关键的类型要知道,Benchmark.times,我们继续走进看看这个东东是什么。

插一句,我们上面看到的measure方法前面并没有加self关键字来定义,为何就变成了模块方法可以Benchmark::measure直接执行呢,原因在benchmark.rb文件第353行:
module_function :benchmark, :measure, :realtime, :bm, :bmbm

module_function将模块内方法变成了模块方法(java的静态方法),可以通过模块名直接引用的。

  def Benchmark::times() # :nodoc:
      Process::times()
  end

Benchmark.times调用了Process::times(),继续走进

  #     Process.times   => aStructTms
  #
  #
  # Returns a <code>Tms</code> structure (see <code>Struct::Tms</code>
  # on page 388) that contains user and system CPU times for this
  # process.
  #
  #    t = Process.times
  #    [ t.utime, t.stime ]   #=> [0.0, 0.02]
  #
  #
  def self.times
    # This is just a stub for a builtin Ruby method.
    # See the top of this file for more info.
  end


我们跳进了另一个文件stub_process.rb,看到这个东东的E文说明,是builtin方法,native code不下ruby的源文件是看不到源码的,看说明我们知道这个方法返回Tms结构体。

tms这个结构体在ruby中没有说明,但可以在Linux的内核文件中找到定义,
请参看<sys/times.h>

struct tms {
     clock_t  tms_utime;  /* user CPU time */
     clock_t  tms_stime;  /* system CPU time */
     clock_t  tms_cutime; /* user CPU time, terminated children */
     clock_t  tms_cstime; /* system CPU time, terminated children */
   };


该结构体是针对当前进程而得到的结果,
tms_utime是用户态时间,我们写的ruby的代码执行时间应该算在这里,
tms_stime是系统态时间,应该是启动或者销毁ruby解析器所花费的时间,还有其它和系统相关的任务所花费的时间。
剩下的两个children的我就不知道了,是不是该进程所起动的子进程的当前用户态和系统态时间?可能。

benchmark的计时算法就是把这四个时间相减,得到block的执行时间,再用Time.now相减,得到real time.

ruby中通过以下方法访问tms结构体:
utime           # user time
stime           # system time
cutime          # user time of children
cstime          # system time of children

   发表时间:2009-08-31   最后修改:2009-08-31

刚才翻了一下benchmark,发现了这个bmbm..

# * The times for some benchmarks depend on the order in which items
#   are run.  These differences are due to the cost of memory


#   allocation and garbage collection. To avoid these discrepancies,
#   the #bmbm method is provided.  For example, to compare ways to
#   sort an array of floats:
#
#       require 'benchmark'
#       
#       array = (1..1000000).map { rand }
#       
#       Benchmark.bmbm do |x|
#         x.report("sort!") { array.dup.sort! }
#         x.report("sort")  { array.dup.sort  }
#       end
# 
#   The result:
# 
#        Rehearsal -----------------------------------------
#        sort!  11.928000   0.010000  11.938000 ( 12.756000)
#        sort   13.048000   0.020000  13.068000 ( 13.857000)
#        ------------------------------- total: 25.006000sec
#        
#                    user     system      total        real
#        sort!  12.959000   0.010000  12.969000 ( 13.793000)
#        sort   12.007000   0.000000  12.007000 ( 12.791000)

0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics