1717 */
1818
1919#include "tclInt.h"
20+ #include "tclCompile.h"
2021#include "tclRegexp.h"
2122#include "tclStringTrim.h"
2223
@@ -4050,43 +4051,192 @@ Tcl_TimeRateObjCmd(
40504051 int objc , /* Number of arguments. */
40514052 Tcl_Obj * const objv []) /* Argument objects. */
40524053{
4054+ static
4055+ double measureOverhead = 0 ; /* global measure-overhead */
4056+ double overhead = -1 ; /* given measure-overhead */
40534057 register Tcl_Obj * objPtr ;
4054- register int result ;
4058+ register int result , i ;
4059+ Tcl_Obj * calibrate = NULL , * direct = NULL ;
40554060 Tcl_WideInt count = 0 ; /* Holds repetition count */
4056- Tcl_WideInt maxms ; /* Maximal running time (in milliseconds) */
4061+ Tcl_WideInt maxms = -0x7FFFFFFFFFFFFFFFL ;
4062+ /* Maximal running time (in milliseconds) */
40574063 Tcl_WideInt threshold = 1 ; /* Current threshold for check time (faster
4058- * repeat count without time check) */
4064+ * repeat count without time check) */
40594065 Tcl_WideInt maxIterTm = 1 ; /* Max time of some iteration as max threshold
4060- * additionally avoid divide to zero (never < 1) */
4066+ * additionally avoid divide to zero (never < 1) */
40614067 register Tcl_WideInt start , middle , stop ;
40624068#ifndef TCL_WIDE_CLICKS
40634069 Tcl_Time now ;
40644070#endif
40654071
4066- if (objc == 2 ) {
4067- maxms = 1000 ;
4068- } else if (objc == 3 ) {
4069- result = TclGetWideIntFromObj (interp , objv [2 ], & maxms );
4072+ static const char * const options [] = {
4073+ "-direct" , "-overhead" , "-calibrate" , "--" , NULL
4074+ };
4075+ enum options {
4076+ TMRT_EV_DIRECT , TMRT_OVERHEAD , TMRT_CALIBRATE , TMRT_LAST
4077+ };
4078+
4079+ NRE_callback * rootPtr ;
4080+ ByteCode * codePtr = NULL ;
4081+
4082+ for (i = 1 ; i < objc - 1 ; i ++ ) {
4083+ int index ;
4084+ if (Tcl_GetIndexFromObj (NULL , objv [i ], options , "option" , TCL_EXACT ,
4085+ & index ) != TCL_OK ) {
4086+ break ;
4087+ }
4088+ if (index == TMRT_LAST ) {
4089+ i ++ ;
4090+ break ;
4091+ }
4092+ switch ((enum options ) index ) {
4093+ case TMRT_EV_DIRECT :
4094+ direct = objv [i ];
4095+ break ;
4096+ case TMRT_OVERHEAD :
4097+ if (++ i >= objc - 1 ) {
4098+ goto usage ;
4099+ }
4100+ if (Tcl_GetDoubleFromObj (interp , objv [i ], & overhead ) != TCL_OK ) {
4101+ return TCL_ERROR ;
4102+ }
4103+ break ;
4104+ case TMRT_CALIBRATE :
4105+ calibrate = objv [i ];
4106+ break ;
4107+ }
4108+ }
4109+
4110+ if (i >= objc || i < objc - 2 ) {
4111+ usage :
4112+ Tcl_WrongNumArgs (interp , 1 , objv , "?-direct? ?-calibrate? ?-overhead double? command ?time?" );
4113+ return TCL_ERROR ;
4114+ }
4115+ objPtr = objv [i ++ ];
4116+ if (i < objc ) {
4117+ result = TclGetWideIntFromObj (interp , objv [i ], & maxms );
40704118 if (result != TCL_OK ) {
40714119 return result ;
40724120 }
4073- } else {
4074- Tcl_WrongNumArgs (interp , 1 , objv , "command ?time?" );
4075- return TCL_ERROR ;
40764121 }
40774122
4078- objPtr = objv [1 ];
4123+ /* if calibrate */
4124+ if (calibrate ) {
4125+
4126+ /* if no time specified for the calibration */
4127+ if (maxms == -0x7FFFFFFFFFFFFFFFL ) {
4128+ Tcl_Obj * clobjv [6 ];
4129+ Tcl_WideInt maxCalTime = 5000 ;
4130+ double lastMeasureOverhead = measureOverhead ;
4131+
4132+ clobjv [0 ] = objv [0 ];
4133+ i = 1 ;
4134+ if (direct ) {
4135+ clobjv [i ++ ] = direct ;
4136+ }
4137+ clobjv [i ++ ] = objPtr ;
4138+
4139+ /* reset last measurement overhead */
4140+ measureOverhead = (double )0 ;
4141+
4142+ /* self-call with 100 milliseconds to warm-up,
4143+ * before entering the calibration cycle */
4144+ TclNewLongObj (clobjv [i ], 100 );
4145+ Tcl_IncrRefCount (clobjv [i ]);
4146+ result = Tcl_TimeRateObjCmd (dummy , interp , i + 1 , clobjv );
4147+ Tcl_DecrRefCount (clobjv [i ]);
4148+ if (result != TCL_OK ) {
4149+ return result ;
4150+ }
4151+
4152+ i -- ;
4153+ clobjv [i ++ ] = calibrate ;
4154+ clobjv [i ++ ] = objPtr ;
4155+
4156+ /* set last measurement overhead to max */
4157+ measureOverhead = (double )0x7FFFFFFFFFFFFFFFL ;
4158+
4159+ /* calibration cycle until it'll be preciser */
4160+ maxms = -1000 ;
4161+ do {
4162+ lastMeasureOverhead = measureOverhead ;
4163+ TclNewLongObj (clobjv [i ], (int )maxms );
4164+ Tcl_IncrRefCount (clobjv [i ]);
4165+ result = Tcl_TimeRateObjCmd (dummy , interp , i + 1 , clobjv );
4166+ Tcl_DecrRefCount (clobjv [i ]);
4167+ if (result != TCL_OK ) {
4168+ return result ;
4169+ }
4170+ maxCalTime += maxms ;
4171+ /* increase maxms for preciser calibration */
4172+ maxms -= (- maxms / 4 );
4173+ /* as long as new value more as 0.05% better */
4174+ } while ( (measureOverhead >= lastMeasureOverhead
4175+ || measureOverhead / lastMeasureOverhead <= 0.9995 )
4176+ && maxCalTime > 0
4177+ );
4178+
4179+ return result ;
4180+ }
4181+ if (maxms == 0 ) {
4182+ /* reset last measurement overhead */
4183+ measureOverhead = 0 ;
4184+ Tcl_SetObjResult (interp , Tcl_NewLongObj (0 ));
4185+ return TCL_OK ;
4186+ }
4187+
4188+ /* if time is negative - make current overhead more precise */
4189+ if (maxms > 0 ) {
4190+ /* set last measurement overhead to max */
4191+ measureOverhead = (double )0x7FFFFFFFFFFFFFFFL ;
4192+ } else {
4193+ maxms = - maxms ;
4194+ }
4195+
4196+ }
4197+
4198+ if (maxms == -0x7FFFFFFFFFFFFFFFL ) {
4199+ maxms = 1000 ;
4200+ }
4201+ if (overhead == -1 ) {
4202+ overhead = measureOverhead ;
4203+ }
4204+
4205+ /* be sure that resetting of result will not smudge the further measurement */
4206+ Tcl_ResetResult (interp );
4207+
4208+ /* compile object */
4209+ if (!direct ) {
4210+ if (TclInterpReady (interp ) != TCL_OK ) {
4211+ return TCL_ERROR ;
4212+ }
4213+ codePtr = TclCompileObj (interp , objPtr , NULL , 0 );
4214+ }
4215+
4216+ /* get start and stop time */
40794217#ifndef TCL_WIDE_CLICKS
40804218 Tcl_GetTime (& now );
40814219 start = now .sec ; start *= 1000000 ; start += now .usec ;
40824220#else
40834221 start = TclpGetWideClicks ();
40844222#endif
4223+
4224+ /* start measurement */
40854225 stop = start + maxms * 1000 ;
40864226 middle = start ;
40874227 while (1 ) {
4228+ /* eval single iteration */
40884229 count ++ ;
4089- result = TclEvalObjEx (interp , objPtr , 0 , NULL , 0 );
4230+
4231+ if (!direct ) {
4232+ /* precompiled */
4233+ rootPtr = TOP_CB (interp );
4234+ result = TclNRExecuteByteCode (interp , codePtr );
4235+ result = TclNRRunCallbacks (interp , result , rootPtr );
4236+ } else {
4237+ /* eval */
4238+ result = TclEvalObjEx (interp , objPtr , 0 , NULL , 0 );
4239+ }
40904240 if (result != TCL_OK ) {
40914241 return result ;
40924242 }
@@ -4110,18 +4260,40 @@ Tcl_TimeRateObjCmd(
41104260 maxIterTm = threshold ;
41114261 }
41124262 /* as relation between remaining time and time since last check */
4113- threshold = ((stop - middle ) / maxIterTm ) / 2 ;
4114- if (threshold > 5000 ) { /* fix for too large threshold */
4115- threshold = 5000 ;
4263+ threshold = ((stop - middle ) / maxIterTm ) / 4 ;
4264+ if (threshold > 100000 ) { /* fix for too large threshold */
4265+ threshold = 100000 ;
41164266 }
41174267 }
41184268
4119- if ( 1 ) {
4120- Tcl_Obj * objs [ 6 ] ;
4269+ {
4270+ Tcl_Obj * objarr [ 8 ], * * objs = objarr ;
41214271 Tcl_WideInt val ;
41224272 const char * fmt ;
41234273
41244274 middle -= start ; /* execution time in microsecs */
4275+
4276+ /* if not calibrate */
4277+ if (!calibrate ) {
4278+ /* minimize influence of measurement overhead */
4279+ if (overhead > 0 ) {
4280+ Tcl_WideInt curOverhead = overhead * count ;
4281+ if (middle > curOverhead ) {
4282+ middle -= curOverhead ;
4283+ } else {
4284+ middle = 1 ;
4285+ }
4286+ }
4287+ } else {
4288+ /* calibration - obtaining new measurement overhead */
4289+ if (measureOverhead > (double )middle / count ) {
4290+ measureOverhead = (double )middle / count ;
4291+ }
4292+ objs [0 ] = Tcl_NewDoubleObj (measureOverhead );
4293+ TclNewLiteralStringObj (objs [1 ], "\xC2\xB5s/#-overhead," ); /* mics */
4294+ objs += 2 ;
4295+ }
4296+
41254297 val = middle / count ; /* microsecs per iteration */
41264298 if (val >= 1000000 ) {
41274299 objs [0 ] = Tcl_NewWideIntObj (val );
@@ -4130,20 +4302,20 @@ Tcl_TimeRateObjCmd(
41304302 if (val < 100 ) { fmt = "%.4f" ; } else
41314303 if (val < 1000 ) { fmt = "%.3f" ; } else
41324304 if (val < 10000 ) { fmt = "%.2f" ; } else
4133- { fmt = "%.1f" ; };
4305+ { fmt = "%.1f" ; };
41344306 objs [0 ] = Tcl_ObjPrintf (fmt , ((double )middle )/count );
41354307 }
41364308
41374309 objs [2 ] = Tcl_NewWideIntObj (count ); /* iterations */
41384310
41394311 /* calculate speed as rate (count) per sec */
41404312 if (!middle ) middle ++ ; /* +1 ms, just to avoid divide by zero */
4141- if (count < (0X7FFFFFFFFFFFFFFFL / 1000000 )) {
4313+ if (count < (0x7FFFFFFFFFFFFFFFL / 1000000 )) {
41424314 val = (count * 1000000 ) / middle ;
41434315 if (val < 100000 ) {
41444316 if (val < 100 ) { fmt = "%.3f" ; } else
41454317 if (val < 1000 ) { fmt = "%.2f" ; } else
4146- { fmt = "%.1f" ; };
4318+ { fmt = "%.1f" ; };
41474319 objs [4 ] = Tcl_ObjPrintf (fmt , ((double )(count * 1000000 )) / middle );
41484320 } else {
41494321 objs [4 ] = Tcl_NewWideIntObj (val );
@@ -4160,9 +4332,10 @@ Tcl_TimeRateObjCmd(
41604332 TclNewLiteralStringObj (objs [1 ], "\xC2\xB5s/#," ); /* mics/# */
41614333 TclNewLiteralStringObj (objs [3 ], "#," );
41624334 TclNewLiteralStringObj (objs [5 ], "#/sec" );
4163- Tcl_SetObjResult (interp , Tcl_NewListObj (6 , objs ));
4335+ Tcl_SetObjResult (interp , Tcl_NewListObj ((! calibrate ) ? 6 : 8 , objarr ));
41644336 }
4165- return TCL_OK ;
4337+
4338+ return result ;
41664339}
41674340
41684341/*
0 commit comments