source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/benchmark.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 17.7 KB
Line 
1=begin
2#
3# benchmark.rb - a performance benchmarking library
4#
5# $Id: benchmark.rb 11708 2007-02-12 23:01:19Z shyouhei $
6#
7# Created by Gotoken ([email protected]).
8#
9# Documentation by Gotoken (original RD), Lyle Johnson (RDoc conversion), and
10# Gavin Sinclair (editing).
11#
12=end
13
14# == Overview
15#
16# The Benchmark module provides methods for benchmarking Ruby code, giving
17# detailed reports on the time taken for each task.
18#
19
20# The Benchmark module provides methods to measure and report the time
21# used to execute Ruby code.
22#
23# * Measure the time to construct the string given by the expression
24# <tt>"a"*1_000_000</tt>:
25#
26# require 'benchmark'
27#
28# puts Benchmark.measure { "a"*1_000_000 }
29#
30# On my machine (FreeBSD 3.2 on P5, 100MHz) this generates:
31#
32# 1.166667 0.050000 1.216667 ( 0.571355)
33#
34# This report shows the user CPU time, system CPU time, the sum of
35# the user and system CPU times, and the elapsed real time. The unit
36# of time is seconds.
37#
38# * Do some experiments sequentially using the #bm method:
39#
40# require 'benchmark'
41#
42# n = 50000
43# Benchmark.bm do |x|
44# x.report { for i in 1..n; a = "1"; end }
45# x.report { n.times do ; a = "1"; end }
46# x.report { 1.upto(n) do ; a = "1"; end }
47# end
48#
49# The result:
50#
51# user system total real
52# 1.033333 0.016667 1.016667 ( 0.492106)
53# 1.483333 0.000000 1.483333 ( 0.694605)
54# 1.516667 0.000000 1.516667 ( 0.711077)
55#
56# * Continuing the previous example, put a label in each report:
57#
58# require 'benchmark'
59#
60# n = 50000
61# Benchmark.bm(7) do |x|
62# x.report("for:") { for i in 1..n; a = "1"; end }
63# x.report("times:") { n.times do ; a = "1"; end }
64# x.report("upto:") { 1.upto(n) do ; a = "1"; end }
65# end
66#
67# The result:
68#
69# user system total real
70# for: 1.050000 0.000000 1.050000 ( 0.503462)
71# times: 1.533333 0.016667 1.550000 ( 0.735473)
72# upto: 1.500000 0.016667 1.516667 ( 0.711239)
73#
74#
75# * The times for some benchmarks depend on the order in which items
76# are run. These differences are due to the cost of memory
77# allocation and garbage collection. To avoid these discrepancies,
78# the #bmbm method is provided. For example, to compare ways to
79# sort an array of floats:
80#
81# require 'benchmark'
82#
83# array = (1..1000000).map { rand }
84#
85# Benchmark.bmbm do |x|
86# x.report("sort!") { array.dup.sort! }
87# x.report("sort") { array.dup.sort }
88# end
89#
90# The result:
91#
92# Rehearsal -----------------------------------------
93# sort! 11.928000 0.010000 11.938000 ( 12.756000)
94# sort 13.048000 0.020000 13.068000 ( 13.857000)
95# ------------------------------- total: 25.006000sec
96#
97# user system total real
98# sort! 12.959000 0.010000 12.969000 ( 13.793000)
99# sort 12.007000 0.000000 12.007000 ( 12.791000)
100#
101#
102# * Report statistics of sequential experiments with unique labels,
103# using the #benchmark method:
104#
105# require 'benchmark'
106#
107# n = 50000
108# Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x|
109# tf = x.report("for:") { for i in 1..n; a = "1"; end }
110# tt = x.report("times:") { n.times do ; a = "1"; end }
111# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
112# [tf+tt+tu, (tf+tt+tu)/3]
113# end
114#
115# The result:
116#
117# user system total real
118# for: 1.016667 0.016667 1.033333 ( 0.485749)
119# times: 1.450000 0.016667 1.466667 ( 0.681367)
120# upto: 1.533333 0.000000 1.533333 ( 0.722166)
121# >total: 4.000000 0.033333 4.033333 ( 1.889282)
122# >avg: 1.333333 0.011111 1.344444 ( 0.629761)
123
124module Benchmark
125
126 BENCHMARK_VERSION = "2002-04-25" #:nodoc"
127
128 def Benchmark::times() # :nodoc:
129 Process::times()
130 end
131
132
133 # Invokes the block with a <tt>Benchmark::Report</tt> object, which
134 # may be used to collect and report on the results of individual
135 # benchmark tests. Reserves <i>label_width</i> leading spaces for
136 # labels on each line. Prints _caption_ at the top of the
137 # report, and uses _fmt_ to format each line.
138 # If the block returns an array of
139 # <tt>Benchmark::Tms</tt> objects, these will be used to format
140 # additional lines of output. If _label_ parameters are
141 # given, these are used to label these extra lines.
142 #
143 # _Note_: Other methods provide a simpler interface to this one, and are
144 # suitable for nearly all benchmarking requirements. See the examples in
145 # Benchmark, and the #bm and #bmbm methods.
146 #
147 # Example:
148 #
149 # require 'benchmark'
150 # include Benchmark # we need the CAPTION and FMTSTR constants
151 #
152 # n = 50000
153 # Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x|
154 # tf = x.report("for:") { for i in 1..n; a = "1"; end }
155 # tt = x.report("times:") { n.times do ; a = "1"; end }
156 # tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
157 # [tf+tt+tu, (tf+tt+tu)/3]
158 # end
159 #
160 # <i>Generates:</i>
161 #
162 # user system total real
163 # for: 1.016667 0.016667 1.033333 ( 0.485749)
164 # times: 1.450000 0.016667 1.466667 ( 0.681367)
165 # upto: 1.533333 0.000000 1.533333 ( 0.722166)
166 # >total: 4.000000 0.033333 4.033333 ( 1.889282)
167 # >avg: 1.333333 0.011111 1.344444 ( 0.629761)
168 #
169
170 def benchmark(caption = "", label_width = nil, fmtstr = nil, *labels) # :yield: report
171 sync = STDOUT.sync
172 STDOUT.sync = true
173 label_width ||= 0
174 fmtstr ||= FMTSTR
175 raise ArgumentError, "no block" unless iterator?
176 print caption
177 results = yield(Report.new(label_width, fmtstr))
178 Array === results and results.grep(Tms).each {|t|
179 print((labels.shift || t.label || "").ljust(label_width),
180 t.format(fmtstr))
181 }
182 STDOUT.sync = sync
183 end
184
185
186 # A simple interface to the #benchmark method, #bm is generates sequential reports
187 # with labels. The parameters have the same meaning as for #benchmark.
188 #
189 # require 'benchmark'
190 #
191 # n = 50000
192 # Benchmark.bm(7) do |x|
193 # x.report("for:") { for i in 1..n; a = "1"; end }
194 # x.report("times:") { n.times do ; a = "1"; end }
195 # x.report("upto:") { 1.upto(n) do ; a = "1"; end }
196 # end
197 #
198 # <i>Generates:</i>
199 #
200 # user system total real
201 # for: 1.050000 0.000000 1.050000 ( 0.503462)
202 # times: 1.533333 0.016667 1.550000 ( 0.735473)
203 # upto: 1.500000 0.016667 1.516667 ( 0.711239)
204 #
205
206 def bm(label_width = 0, *labels, &blk) # :yield: report
207 benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels, &blk)
208 end
209
210
211 # Sometimes benchmark results are skewed because code executed
212 # earlier encounters different garbage collection overheads than
213 # that run later. #bmbm attempts to minimize this effect by running
214 # the tests twice, the first time as a rehearsal in order to get the
215 # runtime environment stable, the second time for
216 # real. <tt>GC.start</tt> is executed before the start of each of
217 # the real timings; the cost of this is not included in the
218 # timings. In reality, though, there's only so much that #bmbm can
219 # do, and the results are not guaranteed to be isolated from garbage
220 # collection and other effects.
221 #
222 # Because #bmbm takes two passes through the tests, it can
223 # calculate the required label width.
224 #
225 # require 'benchmark'
226 #
227 # array = (1..1000000).map { rand }
228 #
229 # Benchmark.bmbm do |x|
230 # x.report("sort!") { array.dup.sort! }
231 # x.report("sort") { array.dup.sort }
232 # end
233 #
234 # <i>Generates:</i>
235 #
236 # Rehearsal -----------------------------------------
237 # sort! 11.928000 0.010000 11.938000 ( 12.756000)
238 # sort 13.048000 0.020000 13.068000 ( 13.857000)
239 # ------------------------------- total: 25.006000sec
240 #
241 # user system total real
242 # sort! 12.959000 0.010000 12.969000 ( 13.793000)
243 # sort 12.007000 0.000000 12.007000 ( 12.791000)
244 #
245 # #bmbm yields a Benchmark::Job object and returns an array of
246 # Benchmark::Tms objects.
247 #
248 def bmbm(width = 0, &blk) # :yield: job
249 job = Job.new(width)
250 yield(job)
251 width = job.width
252 sync = STDOUT.sync
253 STDOUT.sync = true
254
255 # rehearsal
256 print "Rehearsal "
257 puts '-'*(width+CAPTION.length - "Rehearsal ".length)
258 list = []
259 job.list.each{|label,item|
260 print(label.ljust(width))
261 res = Benchmark::measure(&item)
262 print res.format()
263 list.push res
264 }
265 sum = Tms.new; list.each{|i| sum += i}
266 ets = sum.format("total: %tsec")
267 printf("%s %s\n\n",
268 "-"*(width+CAPTION.length-ets.length-1), ets)
269
270 # take
271 print ' '*width, CAPTION
272 list = []
273 ary = []
274 job.list.each{|label,item|
275 GC::start
276 print label.ljust(width)
277 res = Benchmark::measure(&item)
278 print res.format()
279 ary.push res
280 list.push [label, res]
281 }
282
283 STDOUT.sync = sync
284 ary
285 end
286
287 #
288 # Returns the time used to execute the given block as a
289 # Benchmark::Tms object.
290 #
291 def measure(label = "") # :yield:
292 t0, r0 = Benchmark.times, Time.now
293 yield
294 t1, r1 = Benchmark.times, Time.now
295 Benchmark::Tms.new(t1.utime - t0.utime,
296 t1.stime - t0.stime,
297 t1.cutime - t0.cutime,
298 t1.cstime - t0.cstime,
299 r1.to_f - r0.to_f,
300 label)
301 end
302
303 #
304 # Returns the elapsed real time used to execute the given block.
305 #
306 def realtime(&blk) # :yield:
307 Benchmark::measure(&blk).real
308 end
309
310
311
312 #
313 # A Job is a sequence of labelled blocks to be processed by the
314 # Benchmark.bmbm method. It is of little direct interest to the user.
315 #
316 class Job # :nodoc:
317 #
318 # Returns an initialized Job instance.
319 # Usually, one doesn't call this method directly, as new
320 # Job objects are created by the #bmbm method.
321 # _width_ is a initial value for the label offset used in formatting;
322 # the #bmbm method passes its _width_ argument to this constructor.
323 #
324 def initialize(width)
325 @width = width
326 @list = []
327 end
328
329 #
330 # Registers the given label and block pair in the job list.
331 #
332 def item(label = "", &blk) # :yield:
333 raise ArgmentError, "no block" unless block_given?
334 label.concat ' '
335 w = label.length
336 @width = w if @width < w
337 @list.push [label, blk]
338 self
339 end
340
341 alias report item
342
343 # An array of 2-element arrays, consisting of label and block pairs.
344 attr_reader :list
345
346 # Length of the widest label in the #list, plus one.
347 attr_reader :width
348 end
349
350 module_function :benchmark, :measure, :realtime, :bm, :bmbm
351
352
353
354 #
355 # This class is used by the Benchmark.benchmark and Benchmark.bm methods.
356 # It is of little direct interest to the user.
357 #
358 class Report # :nodoc:
359 #
360 # Returns an initialized Report instance.
361 # Usually, one doesn't call this method directly, as new
362 # Report objects are created by the #benchmark and #bm methods.
363 # _width_ and _fmtstr_ are the label offset and
364 # format string used by Tms#format.
365 #
366 def initialize(width = 0, fmtstr = nil)
367 @width, @fmtstr = width, fmtstr
368 end
369
370 #
371 # Prints the _label_ and measured time for the block,
372 # formatted by _fmt_. See Tms#format for the
373 # formatting rules.
374 #
375 def item(label = "", *fmt, &blk) # :yield:
376 print label.ljust(@width)
377 res = Benchmark::measure(&blk)
378 print res.format(@fmtstr, *fmt)
379 res
380 end
381
382 alias report item
383 end
384
385
386
387 #
388 # A data object, representing the times associated with a benchmark
389 # measurement.
390 #
391 class Tms
392 CAPTION = " user system total real\n"
393 FMTSTR = "%10.6u %10.6y %10.6t %10.6r\n"
394
395 # User CPU time
396 attr_reader :utime
397
398 # System CPU time
399 attr_reader :stime
400
401 # User CPU time of children
402 attr_reader :cutime
403
404 # System CPU time of children
405 attr_reader :cstime
406
407 # Elapsed real time
408 attr_reader :real
409
410 # Total time, that is _utime_ + _stime_ + _cutime_ + _cstime_
411 attr_reader :total
412
413 # Label
414 attr_reader :label
415
416 #
417 # Returns an initialized Tms object which has
418 # _u_ as the user CPU time, _s_ as the system CPU time,
419 # _cu_ as the children's user CPU time, _cs_ as the children's
420 # system CPU time, _real_ as the elapsed real time and _l_
421 # as the label.
422 #
423 def initialize(u = 0.0, s = 0.0, cu = 0.0, cs = 0.0, real = 0.0, l = nil)
424 @utime, @stime, @cutime, @cstime, @real, @label = u, s, cu, cs, real, l
425 @total = @utime + @stime + @cutime + @cstime
426 end
427
428 #
429 # Returns a new Tms object whose times are the sum of the times for this
430 # Tms object, plus the time required to execute the code block (_blk_).
431 #
432 def add(&blk) # :yield:
433 self + Benchmark::measure(&blk)
434 end
435
436 #
437 # An in-place version of #add.
438 #
439 def add!
440 t = Benchmark::measure(&blk)
441 @utime = utime + t.utime
442 @stime = stime + t.stime
443 @cutime = cutime + t.cutime
444 @cstime = cstime + t.cstime
445 @real = real + t.real
446 self
447 end
448
449 #
450 # Returns a new Tms object obtained by memberwise summation
451 # of the individual times for this Tms object with those of the other
452 # Tms object.
453 # This method and #/() are useful for taking statistics.
454 #
455 def +(other); memberwise(:+, other) end
456
457 #
458 # Returns a new Tms object obtained by memberwise subtraction
459 # of the individual times for the other Tms object from those of this
460 # Tms object.
461 #
462 def -(other); memberwise(:-, other) end
463
464 #
465 # Returns a new Tms object obtained by memberwise multiplication
466 # of the individual times for this Tms object by _x_.
467 #
468 def *(x); memberwise(:*, x) end
469
470 #
471 # Returns a new Tms object obtained by memberwise division
472 # of the individual times for this Tms object by _x_.
473 # This method and #+() are useful for taking statistics.
474 #
475 def /(x); memberwise(:/, x) end
476
477 #
478 # Returns the contents of this Tms object as
479 # a formatted string, according to a format string
480 # like that passed to Kernel.format. In addition, #format
481 # accepts the following extensions:
482 #
483 # <tt>%u</tt>:: Replaced by the user CPU time, as reported by Tms#utime.
484 # <tt>%y</tt>:: Replaced by the system CPU time, as reported by #stime (Mnemonic: y of "s*y*stem")
485 # <tt>%U</tt>:: Replaced by the children's user CPU time, as reported by Tms#cutime
486 # <tt>%Y</tt>:: Replaced by the children's system CPU time, as reported by Tms#cstime
487 # <tt>%t</tt>:: Replaced by the total CPU time, as reported by Tms#total
488 # <tt>%r</tt>:: Replaced by the elapsed real time, as reported by Tms#real
489 # <tt>%n</tt>:: Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame")
490 #
491 # If _fmtstr_ is not given, FMTSTR is used as default value, detailing the
492 # user, system and real elapsed time.
493 #
494 def format(arg0 = nil, *args)
495 fmtstr = (arg0 || FMTSTR).dup
496 fmtstr.gsub!(/(%[-+\.\d]*)n/){"#{$1}s" % label}
497 fmtstr.gsub!(/(%[-+\.\d]*)u/){"#{$1}f" % utime}
498 fmtstr.gsub!(/(%[-+\.\d]*)y/){"#{$1}f" % stime}
499 fmtstr.gsub!(/(%[-+\.\d]*)U/){"#{$1}f" % cutime}
500 fmtstr.gsub!(/(%[-+\.\d]*)Y/){"#{$1}f" % cstime}
501 fmtstr.gsub!(/(%[-+\.\d]*)t/){"#{$1}f" % total}
502 fmtstr.gsub!(/(%[-+\.\d]*)r/){"(#{$1}f)" % real}
503 arg0 ? Kernel::format(fmtstr, *args) : fmtstr
504 end
505
506 #
507 # Same as #format.
508 #
509 def to_s
510 format
511 end
512
513 #
514 # Returns a new 6-element array, consisting of the
515 # label, user CPU time, system CPU time, children's
516 # user CPU time, children's system CPU time and elapsed
517 # real time.
518 #
519 def to_a
520 [@label, @utime, @stime, @cutime, @cstime, @real]
521 end
522
523 protected
524 def memberwise(op, x)
525 case x
526 when Benchmark::Tms
527 Benchmark::Tms.new(utime.__send__(op, x.utime),
528 stime.__send__(op, x.stime),
529 cutime.__send__(op, x.cutime),
530 cstime.__send__(op, x.cstime),
531 real.__send__(op, x.real)
532 )
533 else
534 Benchmark::Tms.new(utime.__send__(op, x),
535 stime.__send__(op, x),
536 cutime.__send__(op, x),
537 cstime.__send__(op, x),
538 real.__send__(op, x)
539 )
540 end
541 end
542 end
543
544 # The default caption string (heading above the output times).
545 CAPTION = Benchmark::Tms::CAPTION
546
547 # The default format string used to display times. See also Benchmark::Tms#format.
548 FMTSTR = Benchmark::Tms::FMTSTR
549end
550
551if __FILE__ == $0
552 include Benchmark
553
554 n = ARGV[0].to_i.nonzero? || 50000
555 puts %Q([#{n} times iterations of `a = "1"'])
556 benchmark(" " + CAPTION, 7, FMTSTR) do |x|
557 x.report("for:") {for i in 1..n; a = "1"; end} # Benchmark::measure
558 x.report("times:") {n.times do ; a = "1"; end}
559 x.report("upto:") {1.upto(n) do ; a = "1"; end}
560 end
561
562 benchmark do
563 [
564 measure{for i in 1..n; a = "1"; end}, # Benchmark::measure
565 measure{n.times do ; a = "1"; end},
566 measure{1.upto(n) do ; a = "1"; end}
567 ]
568 end
569end
Note: See TracBrowser for help on using the repository browser.