Still somewhat experimental speedup. This appears to speed up the
most common interface to Tcl, the call() method, by maybe 20-25%. The speedup code avoids the construction of a Tcl command string from the argument list -- the Tcl argument list is immediately parsed back by Tcl_Eval() into a list that is *guaranteed* (by Tcl_Merge()) to be exactly the same list, so instead we look up the command info and call the command function directly. If the lookup fails, we fall back to the old method (Tcl_Merge() + Tcl_Eval()) so we don't need to worry about special cases like undefined commands or the occasional command ("after") that sets the info.proc pointer to NULL -- let TclEval() deal with these.
This commit is contained in:
parent
9d1b7ae65b
commit
212643f199
|
@ -43,6 +43,15 @@ PERFORMANCE OF THIS SOFTWARE.
|
||||||
Use Tcl 8.0 if available (even alpha or beta).
|
Use Tcl 8.0 if available (even alpha or beta).
|
||||||
The oldest usable version is 4.1p1/7.5p1.
|
The oldest usable version is 4.1p1/7.5p1.
|
||||||
|
|
||||||
|
XXX Further speed-up ideas, involving Tcl 8.0 features:
|
||||||
|
|
||||||
|
- In Tcl_Call(), create Tcl objects from the arguments, possibly using
|
||||||
|
intelligent mappings between Python objects and Tcl objects (e.g. ints,
|
||||||
|
floats and Tcl window pointers could be handled specially).
|
||||||
|
|
||||||
|
- Register a new Tcl type, "Python callable", which can be called more
|
||||||
|
efficiently and passed to Tcl_EvalObj() directly (if this is possible).
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,21 +404,107 @@ Tkapp_Call(self, args)
|
||||||
PyObject *self;
|
PyObject *self;
|
||||||
PyObject *args;
|
PyObject *args;
|
||||||
{
|
{
|
||||||
char *cmd = Merge(args);
|
/* This is copied from Merge() */
|
||||||
PyObject *res = NULL;
|
PyObject *tmp = NULL;
|
||||||
|
char *argvStore[ARGSZ];
|
||||||
|
char **argv = NULL;
|
||||||
|
int fvStore[ARGSZ];
|
||||||
|
int *fv = NULL;
|
||||||
|
int argc = 0, i;
|
||||||
|
PyObject *res = NULL; /* except this has a different type */
|
||||||
|
Tcl_CmdInfo info; /* and this is added */
|
||||||
|
Tcl_Interp *interp = Tkapp_Interp(self); /* and this too */
|
||||||
|
|
||||||
if (!cmd)
|
if (!(tmp = PyList_New(0)))
|
||||||
PyErr_SetString(Tkinter_TclError, "merge failed");
|
return NULL;
|
||||||
|
|
||||||
else if (Tcl_Eval(Tkapp_Interp(self), cmd) == TCL_ERROR)
|
argv = argvStore;
|
||||||
res = Tkinter_Error(self);
|
fv = fvStore;
|
||||||
|
|
||||||
else
|
if (args == NULL)
|
||||||
res = PyString_FromString(Tkapp_Result(self));
|
argc = 0;
|
||||||
|
|
||||||
if (cmd)
|
else if (!PyTuple_Check(args)) {
|
||||||
|
argc = 1;
|
||||||
|
fv[0] = 0;
|
||||||
|
argv[0] = AsString(args, tmp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
argc = PyTuple_Size(args);
|
||||||
|
|
||||||
|
if (argc > ARGSZ) {
|
||||||
|
argv = (char **)ckalloc(argc * sizeof(char *));
|
||||||
|
fv = (int *)ckalloc(argc * sizeof(int));
|
||||||
|
if (argv == NULL || fv == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
PyObject *v = PyTuple_GetItem(args, i);
|
||||||
|
if (PyTuple_Check(v)) {
|
||||||
|
fv[i] = 1;
|
||||||
|
if (!(argv[i] = Merge(v)))
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
else if (v == Py_None) {
|
||||||
|
argc = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fv[i] = 0;
|
||||||
|
argv[i] = AsString(v, tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* End code copied from Merge() */
|
||||||
|
|
||||||
|
/* All this to avoid a call to Tcl_Merge() and the corresponding call
|
||||||
|
to Tcl_SplitList() inside Tcl_Eval()... It can save a bundle! */
|
||||||
|
if (Py_VerboseFlag >= 2) {
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
fprintf(stderr, "%s ", argv[i]);
|
||||||
|
}
|
||||||
|
if (argc < 1 ||
|
||||||
|
!Tcl_GetCommandInfo(interp, argv[0], &info) ||
|
||||||
|
info.proc == NULL)
|
||||||
|
{
|
||||||
|
char *cmd;
|
||||||
|
if (Py_VerboseFlag >= 2)
|
||||||
|
fprintf(stderr, "... use TclEval ");
|
||||||
|
cmd = Tcl_Merge(argc, argv);
|
||||||
|
i = Tcl_Eval(interp, cmd);
|
||||||
ckfree(cmd);
|
ckfree(cmd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Tcl_ResetResult(interp);
|
||||||
|
i = (*info.proc)(info.clientData, interp, argc, argv);
|
||||||
|
}
|
||||||
|
if (i == TCL_ERROR) {
|
||||||
|
if (Py_VerboseFlag >= 2)
|
||||||
|
fprintf(stderr, "... error: '%s'\n",
|
||||||
|
interp->result);
|
||||||
|
Tkinter_Error(self);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (Py_VerboseFlag >= 2)
|
||||||
|
fprintf(stderr, "-> '%s'\n", interp->result);
|
||||||
|
res = PyString_FromString(interp->result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copied from Merge() again */
|
||||||
|
finally:
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
if (fv[i]) {
|
||||||
|
ckfree(argv[i]);
|
||||||
|
}
|
||||||
|
if (argv != argvStore)
|
||||||
|
ckfree(FREECAST argv);
|
||||||
|
if (fv != fvStore)
|
||||||
|
ckfree(FREECAST fv);
|
||||||
|
|
||||||
|
Py_DECREF(tmp);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,6 +514,12 @@ Tkapp_GlobalCall(self, args)
|
||||||
PyObject *self;
|
PyObject *self;
|
||||||
PyObject *args;
|
PyObject *args;
|
||||||
{
|
{
|
||||||
|
/* Could do the same here as for Tkapp_Call(), but this is not used
|
||||||
|
much, so I can't be bothered. Unfortunately Tcl doesn't export a
|
||||||
|
way for the user to do what all its Global* variants do (save and
|
||||||
|
reset the scope pointer, call the local version, restore the saved
|
||||||
|
scope pointer). */
|
||||||
|
|
||||||
char *cmd = Merge(args);
|
char *cmd = Merge(args);
|
||||||
PyObject *res = NULL;
|
PyObject *res = NULL;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue