diff options
author | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2002-03-14 06:23:46 +0000 |
---|---|---|
committer | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2002-03-14 06:23:46 +0000 |
commit | 997b69cb6ac9eb97ea644276f1af886a49c1de03 (patch) | |
tree | 1dffc60dc5f5afbec79dc8d0f314c3355f92dab2 /lib/benchmark.rb | |
parent | 4f0870f74b530cdb2de4306d55e966a924a9149c (diff) |
* re.c (rb_reg_match): should clear $~ if operand is nil.
* re.c (rb_reg_match2): ditto.
* configure: merge Jonathan Baker's large file support patch
[ruby-talk:35316], with read_all patch in [ruby-talk:35470].
* eval.c (rb_f_abort): optional message argument that be printed
on termination. [new]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@2200 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/benchmark.rb')
-rw-r--r-- | lib/benchmark.rb | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/lib/benchmark.rb b/lib/benchmark.rb new file mode 100644 index 0000000000..e6c63a7c14 --- /dev/null +++ b/lib/benchmark.rb @@ -0,0 +1,651 @@ +# +# benchmark.rb +# +=begin + 2001-11-26: Time.times renamed Process.times for ruby17 (gotoken#notwork.org) + 2001-01-12: made bmbm module func. bmbm return Tms array. + 2001-01-10: added bmbm, Job and INSTALL.rb (gotoken#notwork.org) + 2000-04-00: report() prints tag before eval block (gotoken#notwork.org) + 2000-02-22: report(): measure -> Benchmark::measure (nakahiro#sarion.co.jp) + 2000-01-02: bug fix, documentation (gotoken#notwork.org) + 2000-01-01: measure can take a tag as opt. (nobu.nakada#nifty.ne.jp) + 2000-01-01: first release (gotoken#notwork.org) +=end + +=begin += benchmark.rb + +== NAME +((*benchmark.rb*)) - a benchmark utility + +== SYNOPSIS + ---------- + require "benchmark" + include Benchmark + ---------- + +== DESCRIPTION + +benchmark.rb provides some utilities to measure and report the +times used and passed to execute. + +== SIMPLE EXAMPLE + +=== EXAMPLE 0 +To ((<measure>)) the times to make (({"a"*1_000_000})): + + ---------- + puts measure{ "a"*1_000_000 } + ---------- + +On my machine (FreeBSD 3.2 on P5100MHz) this reported as follows: + + ---------- + 1.166667 0.050000 1.216667 ( 0.571355) + ---------- + +The above shows user time, system time, user+system, and really passed +time. The unit of time is second. + +=== EXAMPLE 1 +To do some experiments sequentially, ((<bm>)) is useful: + + ---------- + n = 50000 + 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 + ---------- + +The result: + ---------- + user system total real + 1.033333 0.016667 1.016667 ( 0.492106) + 1.483333 0.000000 1.483333 ( 0.694605) + 1.516667 0.000000 1.516667 ( 0.711077) + ---------- + +=== EXAMPLE 2 +To put a label in each ((<report>)): + + ---------- + n = 50000 + bm(7) do |x| + x.report("for:") {for i in 1..n; a = "1"; end} + x.report("times:") {n.times do ; a = "1"; end} + x.report("upto:") {1.upto(n) do ; a = "1"; end} + end + ---------- + +The option (({7})) specifies the offset of each report accoding to the +longest label. + +This reports as follows: + + ---------- + user system total real + for: 1.050000 0.000000 1.050000 ( 0.503462) + times: 1.533333 0.016667 1.550000 ( 0.735473) + upto: 1.500000 0.016667 1.516667 ( 0.711239) + ---------- + +=== EXAMPLE 3 + +By the way, benchmarks might seem to depend on the order of items. It +is caused by the cost of memory allocation and the garbage collection. +To prevent this boresome, Benchmark::((<bmbm>)) is provided, e.g., to +compare ways for sort array of strings: + + ---------- + require "rbconfig" + include Config + def file + open("%s/lib/ruby/%s.%s/tk.rb" % + [CONFIG['prefix'],CONFIG['MAJOR'],CONFIG['MINOR']]).read + end + + n = 10 + bmbm do |x| + x.report("destructive!"){ + t = (file*n).to_a; t.each{|line| line.upcase!}; t.sort! + } + x.report("method chain"){ + t = (file*n).to_a.collect{|line| line.upcase}.sort + } + end + ---------- + +This reports: + + ---------- + Rehearsal ------------------------------------------------ + destructive! 2.664062 0.070312 2.734375 ( 2.783401) + method chain 5.257812 0.156250 5.414062 ( 5.736088) + --------------------------------------- total: 8.148438sec + + user system total real + destructive! 2.359375 0.007812 2.367188 ( 2.381015) + method chain 3.046875 0.023438 3.070312 ( 3.085816) + ---------- + +=== EXAMPLE 4 +To report statistics of sequential experiments with unique label, +((<benchmark>)) is available: + + ---------- + n = 50000 + benchmark(" "*7 + CAPTION, 7, FMTSTR, ">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} + [tf+tt+tu, (tf+tt+tu)/3] + end + ---------- + +The result: + + ---------- + user system total real + for: 1.016667 0.016667 1.033333 ( 0.485749) + times: 1.450000 0.016667 1.466667 ( 0.681367) + upto: 1.533333 0.000000 1.533333 ( 0.722166) + >total: 4.000000 0.033333 4.033333 ( 1.889282) + >avg: 1.333333 0.011111 1.344444 ( 0.629761) + ---------- + +== Benchmark module + +=== CONSTANT +:CAPTION + CAPTION is a caption string which is used in Benchmark::((<benchmark>)) and + Benchmark::Report#((<report>)). +:FMTSTR + FMTSTR is a format string which is used in Benchmark::((<benchmark>)) and + Benchmark::Report#((<report>)). See also Benchmark::Tms#((<format>)). +:BENCHMARK_VERSION + BENCHMARK_VERSION is version string which statnds for the last modification + date (YYYY-MM-DD). + +=== INNER CLASS +* ((<Benchmark::Job>)) +* ((<Benchmark::Report>)) +* ((<Benchmark::Tms>)) + +=== MODULE FUNCTION +==== benchmark + ---------- + benchmark([caption [, label_width [, fmtstr]]]) do |x| ... end + benchmark([caption [, label_width [, fmtstr]]]) do array_of_Tms end + benchmark([caption [, label_width [, fmtstr [, labels...]]]]) do + ... + array_of_Tms + end + ---------- + +(({benchmark})) reports the times. In the first form the block variable x is +treated as a ((<Benchmark::Report>)) object, which has ((<report>)) method. +In the second form, each member of array_of_Tms is reported in the +specified form if the member is a ((<Benchmark::Tms>)) object. The +last form provides combined above two forms (See ((<EXAMPLE 3>))). + +The following lists the meaning of each option. + +:caption + A string ((|caption|)) is printed once before execution of the given block. + +:label_width + An integer ((|label_width|)) is used as an offset in each report. + +:fmtstr + An string ((|fmtstr|)) is used to format each measurement. + See ((<format>)) + +:labels + The rest parameters labels is used as prefix of the format to the + value of block, that is array_of_Tms. + +==== bm + ---------- + bm([label_width [, labels ...]) do ... end + ---------- + +(({bm})) is a simpler interface of ((<benchmark>)). +(({bm})) acts as same as follows: + + benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels) do + ... + end + +==== bmbm + ---------- + bmbm([label_width]) do |x| + x.item("label1") { .... } + .... + end + ---------- + +(({bmbm})) is yet another ((<benchmark>)). This utility function is +provited to prevent a kind of job order dependency, which is caused +by memory allocation and object creation. The usage is similar to +((<bm>)) but has less options and does extra three things: + + (1) ((*Rehearsal*)): runs all items in the job ((<list>)) to allocate + enough memory. + (2) ((*GC*)): before each ((<measure>))ment, invokes (({GC.start})) to + prevent the influence of previous job. + (3) If given ((|label_width|)) is less than the maximal width of labels + given as ((|item|))'s argument, the latter is used. + Because (({bmbm})) is a 2-pass procedure, this is possible. + +(({bmbm})) returns an array which consists of Tms correspoding to each +(({item})). +==== measure + ---------- + measure([label]) do ... end + ---------- + +measure returns the times used and passed to execute the given block as a +Benchmark::Tms object. + +==== realtime + ---------- + realtime do ... end + ---------- + +realtime returns the times passed to execute the given block. + +== Benchmark::Report + +=== CLASS METHOD + +==== Benchmark::Report::new(width) + ---------- + Benchmark::Report::new([width [, fmtstr]]) + ---------- + +Usually, one doesn't have to use this method directly, +(({Benchmark::Report::new})) is called by ((<benchmark>)) or ((<bm>)). +((|width|)) and ((|fmtstr|)) are the offset of ((|label|)) and +format string responsively; Both of them are used in ((<format>)). + +=== METHOD + +==== report + + ---------- + report(fmt, *args) + ---------- + +This method reports label and time formated by ((|fmt|)). See +((<format>)) of Benchmark::Tms for formatting rule. + +== Benchmark::Tms + +=== CLASS METHOD + +== Benchmark::Job + +=== CLASS METHOD + +==== Benchmark::Job::new + ---------- + Benchmark::Job::new(width) + ---------- + +Usually, one doesn't have to use this method directly, +(({Benchmark::Job::new})) is called by ((<bmbm>)). +((|width|)) is a initial value for the offset ((|label|)) for formatting. +((<bmbm>)) passes its argument ((|width|)) to this constructor. + +=== METHOD + +==== item + ---------- + item(((|lable|))){ .... } + ---------- + +(({item})) registers a pair of (((|label|))) and given block as job ((<list>)). +==== width + +Maximum length of labels in ((<list>)) plus one. + +==== list + +array of array which consists of label and jop proc. + +==== report + +alias to ((<item>)). + +==== Benchmark::Tms::new + ---------- + Benchmark::Tms::new([u [, s [, cu [, cs [, re [, l]]]]]]) + ---------- + +returns new Benchmark::Tms object which has +((|u|)) as ((<utime>)), +((|s|)) as ((<stime>)), +((|cu|)) as ((<cutime>)) +((|cs|)) as ((<cstime>)), +((|re|)) as ((<real>)) and +((|l|)) as ((<label>)). + +The default value is assumed as 0.0 for ((|u|)), ((|s|)), ((|cu|)), +((|cs|)) and ((|re|)). The default of ((|l|)) is null string ((({""}))). + +==== operator + + ---------- + tms1 + tms2 + ---------- + +returns a new Benchmark::Tms object as memberwise summation. +This method and ((<(('operator /'))>)) is useful to take statistics. + +==== operator / + ---------- + tms / num + ---------- + +returns a new Benchmark::Tms object as memberwise division by ((|num|)). +This method and ((<operator +>)) is useful to take statistics. + +==== add + ---------- + add do ... end + ---------- + +returns a new Benchmark::Tms object which is the result of additional +execution which is given by block. + +==== add! + ---------- + add! do ... end + ---------- + +do additional execution which is given by block. + +==== format + ---------- + format([fmtstr [, *args]]) + ---------- + +(({format})) returns formatted string of (({self})) according to a +((|fmtstr|)) like (({Kernel::format})). In addition, (({format})) accepts +some extentions as follows: + :%u + ((<utime>)). + :%y + ((<stime>)). (Mnemonic: y of ``s((*y*))stem'') + :%U + ((<cutime>)). + :%Y + ((<cstime>)). + :%t + ((<total>)). + :%r + ((<real>)). + :%n + ((<label>)). (Mnemonic: n of ``((*n*))ame'') + +If fmtstr is not given ((<FMTSTR>)) is used as default value. + +==== utime + +returns user time. + +==== stime + +returns system time. + +==== cutime + +returns user time of children. + +==== cstime + +returns system time of children. + +==== total + +returns total time, that is +((<utime>)) + ((<stime>)) + ((<cutime>)) + ((<cstime>)). + +==== real + +returns really passed time. + +==== label + +returns label. + +==== to_a + +returns a new array as follows + + [label, utime, stime, cutime, cstime, real] + +==== to_s + +same as (({format()})). See also ((<format>)). + +== HISTORY +<<< benchmark.rb + +== AUTHOR + +Gotoken (gotoken@notwork.org). +=end + +module Benchmark + BENCHMARK_VERSION = "2001-11-26" + + def Benchmark::times() + Process::times() + end + + def benchmark(caption = "", label_width = nil, fmtstr = nil, *labels) + 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 + + def bm(label_width = 0, *labels, &blk) + benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels, &blk) + end + + def bmbm(width = 0, &blk) + job = Job.new(width) + yield(job) + width = job.width + sync = STDOUT.sync + 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(@fmtstr) + 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) + + # take + print ' '*width, CAPTION + list = [] + ary = [] + job.list.each{|label,item| + GC::start + print label.ljust(width) + res = Benchmark::measure(&item) + print res.format(@fmtstr) + ary.push res + list.push [label, res] + } + + STDOUT.sync = sync + ary + end + + def measure(label = "") + 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 + + def realtime(&blk) + Benchmark::measure(&blk).real + end + + class Job + def initialize(width) + @width = width + @list = [] + end + + def item(label = "", &blk) + raise ArgmentError, "no block" unless block_given? + label.concat ' ' + w = label.length + @width = w if @width < w + @list.push [label, blk] + self + end + + alias report item + attr_reader :list, :width + end + + module_function :benchmark, :measure, :realtime, :bm, :bmbm + + class Report + def initialize(width = 0, fmtstr = nil) + @width, @fmtstr = width, fmtstr + end + + def item(label = "", *fmt, &blk) + print label.ljust(@width) + res = Benchmark::measure(&blk) + print res.format(@fmtstr, *fmt) + res + end + + alias report item + end + + class Tms + CAPTION = " user system total real\n" + FMTSTR = "%10.6u %10.6y %10.6t %10.6r\n" + + attr_reader :utime, :stime, :cutime, :cstime, :real, :total, :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 + @total = @utime + @stime + @cutime + @cstime + end + + def add(&blk) + self + Benchmark::measure(&blk) + end + + def add! + t = Benchmark::measure(&blk) + @utime = utime + t.utime + @stime = stime + t.stime + @cutime = cutime + t.cutime + @cstime = cstime + t.cstime + @real = real + t.real + self + end + + def +(x); memberwise(:+, x) end + def -(x); memberwise(:-, x) end + def *(x); memberwise(:*, x) end + def /(x); memberwise(:/, x) end + + 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 + end + + def to_s + format + end + + def to_a + [@label, @utime, @stime, @cutime, @cstime, @real] + end + + protected + def memberwise(op, x) + case x + when Benchmark::Tms + Benchmark::Tms.new(utime.__send__(op, x.utime), + stime.__send__(op, x.stime), + cutime.__send__(op, x.cutime), + cstime.__send__(op, x.cstime), + real.__send__(op, x.real) + ) + else + Benchmark::Tms.new(utime.__send__(op, x), + stime.__send__(op, x), + cutime.__send__(op, x), + cstime.__send__(op, x), + real.__send__(op, x) + ) + end + end + end + + CAPTION = Benchmark::Tms::CAPTION + FMTSTR = Benchmark::Tms::FMTSTR +end + +if __FILE__ == $0 + include Benchmark + + 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 i in 1..n; a = "1"; end} # Benchmark::measure + x.report("times:") {n.times do ; a = "1"; end} + x.report("upto:") {1.upto(n) do ; a = "1"; end} + end + + benchmark do + [ + measure{for i in 1..n; a = "1"; end}, # Benchmark::measure + measure{n.times do ; a = "1"; end}, + measure{1.upto(n) do ; a = "1"; end} + ] + end +end |