summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1998-04-29 16:22:14 (GMT)
committerGuido van Rossum <guido@python.org>1998-04-29 16:22:14 (GMT)
commit212643f1990a322b3cd95871df6c2401f9ba0230 (patch)
tree0d7c501cf46173b59f28cd54ef4884655a594d17
parent9d1b7ae65b103525a2f28ebcb43d2c580a04d2d4 (diff)
downloadcpython-212643f1990a322b3cd95871df6c2401f9ba0230.zip
cpython-212643f1990a322b3cd95871df6c2401f9ba0230.tar.gz
cpython-212643f1990a322b3cd95871df6c2401f9ba0230.tar.bz2
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.
-rw-r--r--Modules/_tkinter.c119
1 files changed, 110 insertions, 9 deletions
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index 6226f6b..7b4cf79 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -43,6 +43,15 @@ PERFORMANCE OF THIS SOFTWARE.
Use Tcl 8.0 if available (even alpha or beta).
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 *args;
{
- char *cmd = Merge(args);
- PyObject *res = NULL;
+ /* This is copied from Merge() */
+ 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)
- PyErr_SetString(Tkinter_TclError, "merge failed");
+ if (!(tmp = PyList_New(0)))
+ return NULL;
- else if (Tcl_Eval(Tkapp_Interp(self), cmd) == TCL_ERROR)
- res = Tkinter_Error(self);
+ argv = argvStore;
+ fv = fvStore;
- else
- res = PyString_FromString(Tkapp_Result(self));
+ if (args == NULL)
+ 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);
+ }
+ 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;
}
@@ -419,6 +514,12 @@ Tkapp_GlobalCall(self, args)
PyObject *self;
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);
PyObject *res = NULL;