Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tclSE-back-port: timerate and more #3

Open
wants to merge 12 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions generic/tclBasic.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ static const CmdInfo builtInCmds[] = {
{"source", Tcl_SourceObjCmd, NULL, TclNRSourceObjCmd, 0},
{"tell", Tcl_TellObjCmd, NULL, NULL, CMD_IS_SAFE},
{"time", Tcl_TimeObjCmd, NULL, NULL, CMD_IS_SAFE},
{"timerate", Tcl_TimeRateObjCmd, NULL, NULL, CMD_IS_SAFE},
{"unload", Tcl_UnloadObjCmd, NULL, NULL, 0},
{"update", Tcl_UpdateObjCmd, NULL, NULL, CMD_IS_SAFE},
{"vwait", Tcl_VwaitObjCmd, NULL, NULL, CMD_IS_SAFE},
Expand Down
333 changes: 332 additions & 1 deletion generic/tclCmdMZ.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

#include "tclInt.h"
#include "tclCompile.h"
#include "tclRegexp.h"
#include "tclStringTrim.h"

Expand Down Expand Up @@ -3984,7 +3985,7 @@ Tcl_TimeObjCmd(
start = TclpGetWideClicks();
#endif
while (i-- > 0) {
result = Tcl_EvalObjEx(interp, objPtr, 0);
result = TclEvalObjEx(interp, objPtr, 0, NULL, 0);
if (result != TCL_OK) {
return result;
}
Expand Down Expand Up @@ -4021,6 +4022,336 @@ Tcl_TimeObjCmd(
return TCL_OK;
}

/*
*----------------------------------------------------------------------
*
* Tcl_TimeRateObjCmd --
*
* This object-based procedure is invoked to process the "timerate" Tcl
* command.
* This is similar to command "time", except the execution limited by
* given time (in milliseconds) instead of repetition count.
*
* Example:
* timerate {after 5} 1000 ; # equivalent for `time {after 5} [expr 1000/5]`
*
* Results:
* A standard Tcl object result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/

int
Tcl_TimeRateObjCmd(
ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
static
double measureOverhead = 0; /* global measure-overhead */
double overhead = -1; /* given measure-overhead */
register Tcl_Obj *objPtr;
register int result, i;
Tcl_Obj *calibrate = NULL, *direct = NULL;
Tcl_WideInt count = 0; /* Holds repetition count */
Tcl_WideInt maxms = -0x7FFFFFFFFFFFFFFFL;
/* Maximal running time (in milliseconds) */
Tcl_WideInt threshold = 1; /* Current threshold for check time (faster
* repeat count without time check) */
Tcl_WideInt maxIterTm = 1; /* Max time of some iteration as max threshold
* additionally avoid divide to zero (never < 1) */
register Tcl_WideInt start, middle, stop;
#ifndef TCL_WIDE_CLICKS
Tcl_Time now;
#endif

static const char *const options[] = {
"-direct", "-overhead", "-calibrate", "--", NULL
};
enum options {
TMRT_EV_DIRECT, TMRT_OVERHEAD, TMRT_CALIBRATE, TMRT_LAST
};

NRE_callback *rootPtr;
ByteCode *codePtr = NULL;

for (i = 1; i < objc - 1; i++) {
int index;
if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", TCL_EXACT,
&index) != TCL_OK) {
break;
}
if (index == TMRT_LAST) {
i++;
break;
}
switch ((enum options) index) {
case TMRT_EV_DIRECT:
direct = objv[i];
break;
case TMRT_OVERHEAD:
if (++i >= objc - 1) {
goto usage;
}
if (Tcl_GetDoubleFromObj(interp, objv[i], &overhead) != TCL_OK) {
return TCL_ERROR;
}
break;
case TMRT_CALIBRATE:
calibrate = objv[i];
break;
}
}

if (i >= objc || i < objc-2) {
usage:
Tcl_WrongNumArgs(interp, 1, objv, "?-direct? ?-calibrate? ?-overhead double? command ?time?");
return TCL_ERROR;
}
objPtr = objv[i++];
if (i < objc) {
result = TclGetWideIntFromObj(interp, objv[i], &maxms);
if (result != TCL_OK) {
return result;
}
}

/* if calibrate */
if (calibrate) {

/* if no time specified for the calibration */
if (maxms == -0x7FFFFFFFFFFFFFFFL) {
Tcl_Obj *clobjv[6];
Tcl_WideInt maxCalTime = 5000;
double lastMeasureOverhead = measureOverhead;

clobjv[0] = objv[0];
i = 1;
if (direct) {
clobjv[i++] = direct;
}
clobjv[i++] = objPtr;

/* reset last measurement overhead */
measureOverhead = (double)0;

/* self-call with 100 milliseconds to warm-up,
* before entering the calibration cycle */
TclNewLongObj(clobjv[i], 100);
Tcl_IncrRefCount(clobjv[i]);
result = Tcl_TimeRateObjCmd(dummy, interp, i+1, clobjv);
Tcl_DecrRefCount(clobjv[i]);
if (result != TCL_OK) {
return result;
}

i--;
clobjv[i++] = calibrate;
clobjv[i++] = objPtr;

/* set last measurement overhead to max */
measureOverhead = (double)0x7FFFFFFFFFFFFFFFL;

/* calibration cycle until it'll be preciser */
maxms = -1000;
do {
lastMeasureOverhead = measureOverhead;
TclNewLongObj(clobjv[i], (int)maxms);
Tcl_IncrRefCount(clobjv[i]);
result = Tcl_TimeRateObjCmd(dummy, interp, i+1, clobjv);
Tcl_DecrRefCount(clobjv[i]);
if (result != TCL_OK) {
return result;
}
maxCalTime += maxms;
/* increase maxms for preciser calibration */
maxms -= (-maxms / 4);
/* as long as new value more as 0.05% better */
} while ( (measureOverhead >= lastMeasureOverhead
|| measureOverhead / lastMeasureOverhead <= 0.9995)
&& maxCalTime > 0
);

return result;
}
if (maxms == 0) {
/* reset last measurement overhead */
measureOverhead = 0;
Tcl_SetObjResult(interp, Tcl_NewLongObj(0));
return TCL_OK;
}

/* if time is negative - make current overhead more precise */
if (maxms > 0) {
/* set last measurement overhead to max */
measureOverhead = (double)0x7FFFFFFFFFFFFFFFL;
} else {
maxms = -maxms;
}

}

if (maxms == -0x7FFFFFFFFFFFFFFFL) {
maxms = 1000;
}
if (overhead == -1) {
overhead = measureOverhead;
}

/* be sure that resetting of result will not smudge the further measurement */
Tcl_ResetResult(interp);

/* compile object */
if (!direct) {
if (TclInterpReady(interp) != TCL_OK) {
return TCL_ERROR;
}
codePtr = TclCompileObj(interp, objPtr, NULL, 0);
TclPreserveByteCode(codePtr);
}

/* get start and stop time */
#ifndef TCL_WIDE_CLICKS
Tcl_GetTime(&now);
start = now.sec; start *= 1000000; start += now.usec;
#else
start = TclpGetWideClicks();
#endif

/* start measurement */
stop = start + maxms * 1000;
middle = start;
while (1) {
/* eval single iteration */
count++;

if (!direct) {
/* precompiled */
rootPtr = TOP_CB(interp);
result = TclNRExecuteByteCode(interp, codePtr);
result = TclNRRunCallbacks(interp, result, rootPtr);
} else {
/* eval */
result = TclEvalObjEx(interp, objPtr, 0, NULL, 0);
}
if (result != TCL_OK) {
goto done;
}

/* don't check time up to threshold */
if (--threshold > 0) continue;

/* check stop time reached, estimate new threshold */
#ifndef TCL_WIDE_CLICKS
Tcl_GetTime(&now);
middle = now.sec; middle *= 1000000; middle += now.usec;
#else
middle = TclpGetWideClicks();
#endif
if (middle >= stop) {
break;
}
/* average iteration time in microsecs */
threshold = (middle - start) / count;
if (threshold > maxIterTm) {
maxIterTm = threshold;
}
/* as relation between remaining time and time since last check */
threshold = ((stop - middle) / maxIterTm) / 4;
if (threshold > 100000) { /* fix for too large threshold */
threshold = 100000;
}
}

{
Tcl_Obj *objarr[8], **objs = objarr;
Tcl_WideInt val;
const char *fmt;

middle -= start; /* execution time in microsecs */

/* if not calibrate */
if (!calibrate) {
/* minimize influence of measurement overhead */
if (overhead > 0) {
/* estimate the time of overhead (microsecs) */
Tcl_WideInt curOverhead = overhead * count;
if (middle > curOverhead) {
middle -= curOverhead;
} else {
middle = 1;
}
}
} else {
/* calibration - obtaining new measurement overhead */
if (measureOverhead > (double)middle / count) {
measureOverhead = (double)middle / count;
}
objs[0] = Tcl_NewDoubleObj(measureOverhead);
TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#-overhead"); /* mics */
objs += 2;
}

val = middle / count; /* microsecs per iteration */
if (val >= 1000000) {
objs[0] = Tcl_NewWideIntObj(val);
} else {
if (val < 10) { fmt = "%.6f"; } else
if (val < 100) { fmt = "%.4f"; } else
if (val < 1000) { fmt = "%.3f"; } else
if (val < 10000) { fmt = "%.2f"; } else
{ fmt = "%.1f"; };
objs[0] = Tcl_ObjPrintf(fmt, ((double)middle)/count);
}

objs[2] = Tcl_NewWideIntObj(count); /* iterations */

/* calculate speed as rate (count) per sec */
if (!middle) middle++; /* +1 ms, just to avoid divide by zero */
if (count < (0x7FFFFFFFFFFFFFFFL / 1000000)) {
val = (count * 1000000) / middle;
if (val < 100000) {
if (val < 100) { fmt = "%.3f"; } else
if (val < 1000) { fmt = "%.2f"; } else
{ fmt = "%.1f"; };
objs[4] = Tcl_ObjPrintf(fmt, ((double)(count * 1000000)) / middle);
} else {
objs[4] = Tcl_NewWideIntObj(val);
}
} else {
objs[4] = Tcl_NewWideIntObj((count / middle) * 1000000);
}

/* estimated net execution time (in millisecs) */
if (!calibrate) {
objs[6] = Tcl_ObjPrintf("%.3f", (double)middle / 1000);
TclNewLiteralStringObj(objs[7], "nett-ms");
}

/*
* Construct the result as a list because many programs have always parsed
* as such (extracting the first element, typically).
*/

TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#"); /* mics/# */
TclNewLiteralStringObj(objs[3], "#");
TclNewLiteralStringObj(objs[5], "#/sec");
Tcl_SetObjResult(interp, Tcl_NewListObj(8, objarr));
}

done:

if (codePtr != NULL) {
TclReleaseByteCode(codePtr);
}

return result;
}

/*
*----------------------------------------------------------------------
*
Expand Down
3 changes: 3 additions & 0 deletions generic/tclInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -3456,6 +3456,9 @@ MODULE_SCOPE int Tcl_ThrowObjCmd(ClientData dummy, Tcl_Interp *interp,
MODULE_SCOPE int Tcl_TimeObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
MODULE_SCOPE int Tcl_TimeRateObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
MODULE_SCOPE int Tcl_TraceObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
Expand Down
10 changes: 10 additions & 0 deletions library/reg/pkgIndex.tcl
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
if {([info commands ::tcl::pkgconfig] eq "")
|| ([info sharedlibextension] ne ".dll")} return
if {[::tcl::pkgconfig get debug]} {
if {[info exists [file join $dir tclreg13g.dll]]} {
package ifneeded registry 1.3.2 \
[list load [file join $dir tclreg13g.dll] registry]
} else {
package ifneeded registry 1.3.2 \
[list load tclreg13g registry]
}
} else {
if {[info exists [file join $dir tclreg13.dll]]} {
package ifneeded registry 1.3.2 \
[list load [file join $dir tclreg13.dll] registry]
} else {
package ifneeded registry 1.3.2 \
[list load tclreg13 registry]
}
}
Loading