]> granicus.if.org Git - python/commitdiff
Still somewhat experimental speedup. This appears to speed up the
authorGuido van Rossum <guido@python.org>
Wed, 29 Apr 1998 16:22:14 +0000 (16:22 +0000)
committerGuido van Rossum <guido@python.org>
Wed, 29 Apr 1998 16:22:14 +0000 (16:22 +0000)
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.

Modules/_tkinter.c

index 6226f6b46b6ee848a76615c5f0d2fd4e2bf73d28..7b4cf796bca3593567cc72e36897a9b90393114a 100644 (file)
@@ -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;