diff options
-rw-r--r-- | ChangeLog | 16 | ||||
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | bignum.c | 112 | ||||
-rw-r--r-- | configure.in | 9 | ||||
-rw-r--r-- | defines.h | 3 | ||||
-rw-r--r-- | eval.c | 17 | ||||
-rw-r--r-- | file.c | 4 | ||||
-rw-r--r-- | intern.h | 6 | ||||
-rw-r--r-- | io.c | 48 | ||||
-rw-r--r-- | lib/README | 1 | ||||
-rw-r--r-- | lib/benchmark.rb | 651 | ||||
-rw-r--r-- | numeric.c | 57 | ||||
-rw-r--r-- | re.c | 11 | ||||
-rw-r--r-- | ruby.h | 43 |
14 files changed, 955 insertions, 24 deletions
@@ -1,3 +1,9 @@ +Thu Mar 14 00:29:12 2002 Yukihiro Matsumoto <matz@ruby-lang.org> + + * re.c (rb_reg_match): should clear $~ if operand is nil. + + * re.c (rb_reg_match2): ditto. + Thu Mar 14 12:32:59 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp> * ext/stringio/stringio.c: fixed frozen string bug. ungetc no @@ -25,6 +31,16 @@ Wed Mar 13 18:36:55 2002 Akinori MUSHA <knu@iDaemons.org> * lib/getopts.rb: single_options can be nil[*], and is not not optional. ([*]Pointed out by gotoken) +Wed Mar 13 17:23:46 2002 Yukihiro Matsumoto <matz@ruby-lang.org> + + * configure: merge Jonathan Baker's large file support patch + [ruby-talk:35316], with read_all patch in [ruby-talk:35470]. + +Wed Mar 13 04:06:48 2002 Yukihiro Matsumoto <matz@ruby-lang.org> + + * eval.c (rb_f_abort): optional message argument that be printed + on termination. + Tue Mar 12 17:12:06 2002 Tanaka Akira <akr@m17n.org> * lib/resolv.rb: don't complete domains for absolute FQNs. @@ -103,6 +103,7 @@ lib/English.rb lib/Env.rb lib/README lib/base64.rb +lib/benchmark.rb lib/cgi.rb lib/cgi/session.rb lib/cgi-lib.rb @@ -38,6 +38,9 @@ typedef long BDIGIT_DBL_SIGNED; #define BITSPERDIG (sizeof(BDIGIT)*CHAR_BIT) #define BIGRAD ((BDIGIT_DBL)1 << BITSPERDIG) #define DIGSPERLONG ((unsigned int)(sizeof(long)/sizeof(BDIGIT))) +#if HAVE_LONG_LONG +# define DIGSPERLL ((unsigned int)(sizeof(long long)/sizeof(BDIGIT))) +#endif #define BIGUP(x) ((BDIGIT_DBL)(x) << BITSPERDIG) #define BIGDN(x) RSHIFT(x,BITSPERDIG) #define BIGLO(x) ((BDIGIT)((x) & (BIGRAD-1))) @@ -507,6 +510,67 @@ rb_str_to_inum(str, base, badcheck) return rb_cstr_to_inum(s, base, badcheck); } +#if HAVE_LONG_LONG + +VALUE +rb_ull2big(n) + unsigned long long n; +{ + BDIGIT_DBL num = n; + long i = 0; + BDIGIT *digits; + VALUE big; + + i = 0; + big = bignew(DIGSPERLL, 1); + digits = BDIGITS(big); + while (i < DIGSPERLL) { + digits[i++] = BIGLO(num); + num = BIGDN(num); + } + + i = DIGSPERLL; + while (i-- && !digits[i]) ; + RBIGNUM(big)->len = i+1; + return big; +} + +VALUE +rb_ll2big(n) + long long n; +{ + long neg = 0; + VALUE big; + + if (n < 0) { + n = -n; + neg = 1; + } + big = rb_ull2big(n); + if (neg) { + RBIGNUM(big)->sign = 0; + } + return big; +} + +VALUE +rb_ull2inum(n) + unsigned long long n; +{ + if (POSFIXABLE(n)) return INT2FIX(n); + return rb_ull2big(n); +} + +VALUE +rb_ll2inum(n) + long long n; +{ + if (FIXABLE(n)) return INT2FIX(n); + return rb_ll2big(n); +} + +#endif /* HAVE_LONG_LONG */ + VALUE rb_cstr2inum(str, base) const char *str; @@ -650,6 +714,54 @@ rb_big2long(x) return num; } +#if HAVE_LONG_LONG + +static unsigned long long +big2ull(x, type) + VALUE x; + char *type; +{ + long len = RBIGNUM(x)->len; + BDIGIT_DBL num; + BDIGIT *ds; + + if (len > sizeof(long long)/sizeof(BDIGIT)) + rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type); + ds = BDIGITS(x); + num = 0; + while (len--) { + num = BIGUP(num); + num += ds[len]; + } + return num; +} + +unsigned long long +rb_big2ull(x) + VALUE x; +{ + unsigned long long num = big2ull(x, "unsigned long long"); + + if (!RBIGNUM(x)->sign) return -num; + return num; +} + +long long +rb_big2ll(x) + VALUE x; +{ + unsigned long long num = big2ull(x, "long long"); + + if ((long long)num < 0 && (RBIGNUM(x)->sign + || (long long)num != LLONG_MIN)) { + rb_raise(rb_eRangeError, "bignum too big to convert into `long long'"); + } + if (!RBIGNUM(x)->sign) return -(long long)num; + return num; +} + +#endif /* HAVE_LONG_LONG */ + static VALUE dbl2big(d) double d; diff --git a/configure.in b/configure.in index 31a007a941..3785b65f46 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,8 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(ruby.h) +AC_PREREQ(2.50) + rb_version=`grep RUBY_VERSION $srcdir/version.h` MAJOR=`expr "$rb_version" : '#define RUBY_VERSION "\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*"'` MINOR=`expr "$rb_version" : '#define RUBY_VERSION "[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*"'` @@ -124,11 +126,16 @@ AC_MINIX AC_EXEEXT AC_OBJEXT +dnl check for large file stuff +AC_SYS_LARGEFILE + +AC_CHECK_TYPES([long long, off_t]) AC_CHECK_SIZEOF(int, 4) AC_CHECK_SIZEOF(short, 2) AC_CHECK_SIZEOF(long, 4) AC_CHECK_SIZEOF(long long, 0) AC_CHECK_SIZEOF(__int64, 0) +AC_CHECK_SIZEOF(off_t, 0) AC_CHECK_SIZEOF(void*, 4) AC_CHECK_SIZEOF(float, 4) AC_CHECK_SIZEOF(double, 8) @@ -292,6 +299,8 @@ AC_TYPE_GETGROUPS AC_TYPE_SIGNAL AC_FUNC_ALLOCA AC_FUNC_MEMCMP +AC_FUNC_FSEEKO +AC_CHECK_FUNCS(ftello) AC_REPLACE_FUNCS(dup2 memmove mkdir strcasecmp strncasecmp strerror strftime\ strchr strstr strtoul crypt flock vsnprintf\ isinf isnan finite hypot) @@ -56,10 +56,9 @@ void *xrealloc _((void*,long)); void xfree _((void*)); #if SIZEOF_LONG_LONG > 0 -# define HAVE_LONG_LONG # define LONG_LONG long long #elif SIZEOF___INT64 > 0 -# define HAVE_LONG_LONG +# define HAVE_LONG_LONG 1 # define LONG_LONG __int64 # undef SIZEOF_LONG_LONG # define SIZEOF_LONG_LONG SIZEOF___INT64 @@ -3491,10 +3491,21 @@ rb_abort() } static VALUE -rb_f_abort() +rb_f_abort(argc, argv) + int argc; + VALUE *argv; { rb_secure(4); - rb_abort(); + if (argc == 0) { + rb_abort(); + } + else { + VALUE mesg; + + rb_scan_args(argc, argv, "01", &mesg); + rb_io_puts(argc, argv, rb_stderr); + exit(1); + } return Qnil; /* not reached */ } @@ -6056,7 +6067,7 @@ Init_eval() rb_define_global_function("caller", rb_f_caller, -1); rb_define_global_function("exit", rb_f_exit, -1); - rb_define_global_function("abort", rb_f_abort, 0); + rb_define_global_function("abort", rb_f_abort, -1); rb_define_global_function("at_exit", rb_f_at_exit, 0); @@ -242,7 +242,7 @@ static VALUE rb_stat_size(self) VALUE self; { - return LONG2NUM(get_stat(self)->st_size); + return OFFT2NUM(get_stat(self)->st_size); } static VALUE @@ -730,7 +730,7 @@ test_s(obj, fname) if (rb_stat(fname, &st) < 0) return Qnil; if (st.st_size == 0) return Qnil; - return rb_int2inum(st.st_size); + return OFFT2NUM(st.st_size); } static VALUE @@ -70,6 +70,12 @@ long rb_big2long _((VALUE)); #define rb_big2int(x) rb_big2long(x) unsigned long rb_big2ulong _((VALUE)); #define rb_big2uint(x) rb_big2ulong(x) +#if HAVE_LONG_LONG +VALUE rb_ll2inum _((long long)); +VALUE rb_ull2inum _((unsigned long long)); +long long rb_big2ll _((VALUE)); +unsigned long long rb_big2ull _((VALUE)); +#endif /* HAVE_LONG_LONG */ void rb_quad_pack _((char*,VALUE)); VALUE rb_quad_unpack _((const char*,int)); VALUE rb_dbl2big _((double)); @@ -45,6 +45,16 @@ #include <sys/fcntl.h> #endif +#if !HAVE_OFF_T && !defined(off_t) +# define off_t long +#endif +#if !HAVE_FSEEKO && !defined(fseeko) +# define fseeko fseek +#endif +#if !HAVE_FTELLO && !defined(ftello) +# define ftello ftell +#endif + #ifdef HAVE_SYS_TIME_H # include <sys/time.h> #else @@ -316,13 +326,20 @@ rb_io_tell(io) VALUE io; { OpenFile *fptr; - long pos; + off_t pos; GetOpenFile(io, fptr); - pos = ftell(fptr->f); + pos = ftello(fptr->f); if (ferror(fptr->f)) rb_sys_fail(fptr->path); +#if SIZEOF_OFF_T > SIZEOF_LONG +# if !HAVE_LONG_LONG +# error off_t is bigger than long, but you have no long long... +# endif + return rb_ll2inum(pos); +#else return rb_int2inum(pos); +#endif } #ifndef SEEK_CUR @@ -340,7 +357,7 @@ rb_io_seek(io, offset, whence) long pos; GetOpenFile(io, fptr); - pos = fseek(fptr->f, NUM2LONG(offset), whence); + pos = fseeko(fptr->f, NUM2OFFT(offset), whence); if (pos != 0) rb_sys_fail(fptr->path); clearerr(fptr->f); @@ -371,7 +388,7 @@ rb_io_set_pos(io, offset) long pos; GetOpenFile(io, fptr); - pos = fseek(fptr->f, NUM2LONG(offset), SEEK_SET); + pos = fseeko(fptr->f, NUM2OFFT(offset), SEEK_SET); if (pos != 0) rb_sys_fail(fptr->path); clearerr(fptr->f); @@ -380,12 +397,12 @@ rb_io_set_pos(io, offset) static VALUE rb_io_rewind(io) - VALUE io; + VALUE io; { OpenFile *fptr; GetOpenFile(io, fptr); - if (fseek(fptr->f, 0L, 0) != 0) rb_sys_fail(fptr->path); + if (fseeko(fptr->f, 0L, 0) != 0) rb_sys_fail(fptr->path); clearerr(fptr->f); if (io == current_file) { gets_lineno -= fptr->lineno; @@ -588,7 +605,7 @@ read_all(port) OpenFile *fptr; VALUE str = Qnil; struct stat st; - long siz = BUFSIZ; + off_t siz = BUFSIZ; long bytes = 0; int n; @@ -607,16 +624,19 @@ read_all(port) return rb_str_new(0, 0); } else { - long pos = ftell(fptr->f); + off_t pos = ftello(fptr->f); if (st.st_size > pos && pos >= 0) { siz = st.st_size - pos + 1; + if (siz > LONG_MAX) { + rb_raise(rb_eIOError, "file too big for single read"); + } } } } - str = rb_tainted_str_new(0, siz); + str = rb_tainted_str_new(0, (long)siz); READ_CHECK(fptr->f); for (;;) { - n = io_fread(RSTRING(str)->ptr+bytes, siz-bytes, fptr->f); + n = io_fread(RSTRING(str)->ptr+bytes, (long)siz-bytes, fptr->f); if (n == 0 && bytes == 0) { if (feof(fptr->f)) return Qnil; rb_sys_fail(fptr->path); @@ -1986,7 +2006,7 @@ io_reopen(io, nfile) OpenFile *fptr, *orig; char *mode; int fd; - long pos; + off_t pos; nfile = rb_io_get_io(nfile); if (rb_safe_level() >= 4 && (!OBJ_TAINTED(io) || !OBJ_TAINTED(nfile))) { @@ -1997,7 +2017,7 @@ io_reopen(io, nfile) if (fptr == orig) return io; if (orig->mode & FMODE_READABLE) { - pos = ftell(orig->f); + pos = ftello(orig->f); } if (orig->f2) { io_fflush(orig->f2, orig->path); @@ -2032,8 +2052,8 @@ io_reopen(io, nfile) fptr->f = rb_fdopen(fd, mode); } if ((orig->mode & FMODE_READABLE) && pos >= 0) { - fseek(fptr->f, pos, SEEK_SET); - fseek(orig->f, pos, SEEK_SET); + fseeko(fptr->f, pos, SEEK_SET); + fseeko(orig->f, pos, SEEK_SET); } if (fptr->f2) { diff --git a/lib/README b/lib/README index c45e019b73..d05964fa67 100644 --- a/lib/README +++ b/lib/README @@ -2,6 +2,7 @@ English.rb lets Perl'ish global variables have English names Env.rb loads importenv.rb README this file base64.rb encodes/decodes base64 (obsolete) +benchmark.rb a benchmark utility cgi-lib.rb simple CGI support library (old style) cgi.rb CGI support library cgi/session CGI session class 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 @@ -869,6 +869,63 @@ rb_num2fix(val) return INT2FIX(v); } +#if HAVE_LONG_LONG + +long long +rb_num2ll(val) + VALUE val; +{ + if (NIL_P(val)) { + rb_raise(rb_eTypeError, "no implicit conversion from nil"); + } + + if (FIXNUM_P(val)) return (long long)FIX2LONG(val); + + switch (TYPE(val)) { + case T_FLOAT: + if (RFLOAT(val)->value <= (double)LLONG_MAX + && RFLOAT(val)->value >= (double)LLONG_MIN) { + return (long long)(RFLOAT(val)->value); + } + else { + char buf[24]; + char *s; + + sprintf(buf, "%-.10g", RFLOAT(val)->value); + if (s = strchr(buf, ' ')) *s = '\0'; + rb_raise(rb_eRangeError, "float %s out of range of long long", buf); + } + + case T_BIGNUM: + return rb_big2ll(val); + + case T_STRING: + rb_raise(rb_eTypeError, "no implicit conversion from string"); + return Qnil; /* not reached */ + + case T_TRUE: + case T_FALSE: + rb_raise(rb_eTypeError, "no implicit conversion from boolean"); + return Qnil; /* not reached */ + + default: + val = rb_to_int(val); + return NUM2LL(val); + } +} + +unsigned long long +rb_num2ull(val) + VALUE val; +{ + if (TYPE(val) == T_BIGNUM) { + return rb_big2ull(val); + } + return (unsigned long long)rb_num2ll(val); +} + +#endif /* HAVE_LONG_LONG */ + static VALUE int_to_i(num) VALUE num; @@ -637,7 +637,7 @@ rb_reg_search(re, str, pos, reverse) if (result == -2) { rb_reg_raise(RREGEXP(re)->str, RREGEXP(re)->len, - "Stack overflow in regexp matcher", re); + "Stack overflow in regexp matcher", re); } if (result < 0) { @@ -1001,7 +1001,10 @@ rb_reg_match(re, str) { int start; - if (NIL_P(str)) return Qnil; + if (NIL_P(str)) { + rb_backref_set(Qnil); + return Qnil; + } StringValue(str); start = rb_reg_search(re, str, 0, 0); if (start < 0) { @@ -1017,8 +1020,10 @@ rb_reg_match2(re) int start; VALUE line = rb_lastline_get(); - if (TYPE(line) != T_STRING) + if (TYPE(line) != T_STRING) { + rb_backref_set(Qnil); return Qnil; + } start = rb_reg_search(re, line, 0, 0); if (start < 0) { @@ -85,6 +85,24 @@ typedef unsigned long ID; # endif #endif +#if HAVE_LONG_LONG +# ifndef LLONG_MAX +# ifdef LONG_LONG_MAX +# define LLONG_MAX LONG_LONG_MAX +# else + /* assuming 64bit(2's complement) long long */ +# define LLONG_MAX 9223372036854775807LL +# endif +# endif +# ifndef LLONG_MIN +# ifdef LONG_LONG_MIN +# define LLONG_MIN LONG_LONG_MIN +# else +# define LLONG_MIN (-LLONG_MAX-1) +# endif +# endif +#endif + #define FIXNUM_MAX (LONG_MAX>>1) #define FIXNUM_MIN RSHIFT((long)LONG_MIN,1) @@ -101,6 +119,19 @@ VALUE rb_uint2inum _((unsigned long)); #define ULONG2NUM(v) UINT2NUM(v) #define rb_uint_new(v) rb_uint2inum(v) +#if HAVE_LONG_LONG +VALUE rb_ll2inum _((long long)); +#define LL2NUM(v) rb_ll2inum(v) +VALUE rb_ull2inum _((unsigned long long)); +#define ULL2NUM(v) rb_ull2inum(v) +#endif + +#if SIZEOF_OFF_T > SIZEOF_LONG && HAVE_LONG_LONG +# define OFFT2NUM(v) LL2NUM(v) +#else +# define OFFT2NUM(v) INT2NUM(v) +#endif + #define FIX2LONG(x) RSHIFT((long)x,1) #define FIX2ULONG(x) (((unsigned long)(x))>>1) #define FIXNUM_P(f) (((long)(f))&FIXNUM_FLAG) @@ -201,6 +232,18 @@ int rb_fix2int _((VALUE)); #define FIX2UINT(x) FIX2ULONG(x) #endif +#if HAVE_LONG_LONG +long long rb_num2ll _((VALUE)); +unsigned long long rb_num2ull _((VALUE)); +# define NUM2LL(x) (FIXNUM_P(x)?FIX2LONG(x):rb_num2ll((VALUE)x)) +#endif + +#if HAVE_LONG_LONG && SIZEOF_OFF_T > SIZEOF_LONG +# define NUM2OFFT(x) ((off_t)NUM2LL(x)) +#else +# define NUM2OFFT(x) NUM2LONG(x) +#endif + double rb_num2dbl _((VALUE)); #define NUM2DBL(x) rb_num2dbl((VALUE)(x)) |