[Python-checkins] cpython: map cells to arg slots at code creation time (closes #12399)

benjamin.peterson python-checkins at python.org
Sun Jun 26 05:54:51 CEST 2011


http://hg.python.org/cpython/rev/c5b0585624ef
changeset:   70987:c5b0585624ef
parent:      70984:039c316bd157
user:        Benjamin Peterson <benjamin at python.org>
date:        Sat Jun 25 22:54:45 2011 -0500
summary:
  map cells to arg slots at code creation time (closes #12399)

This removes nested loops in PyEval_EvalCodeEx.

files:
  Include/code.h       |   6 ++
  Lib/test/test_sys.py |   2 +-
  Objects/codeobject.c |  94 ++++++++++++++++++++++---------
  Python/ceval.c       |  64 +++++----------------
  4 files changed, 88 insertions(+), 78 deletions(-)


diff --git a/Include/code.h b/Include/code.h
--- a/Include/code.h
+++ b/Include/code.h
@@ -22,6 +22,7 @@
     PyObject *co_freevars;	/* tuple of strings (free variable names) */
     PyObject *co_cellvars;      /* tuple of strings (cell variable names) */
     /* The rest doesn't count for hash or comparisons */
+    unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
     PyObject *co_filename;	/* unicode (where it was loaded from) */
     PyObject *co_name;		/* unicode (name, for reference) */
     int co_firstlineno;		/* first source line number */
@@ -57,6 +58,11 @@
 
 #define CO_FUTURE_BARRY_AS_BDFL  0x40000
 
+/* This value is found in the co_cell2arg array when the associated cell
+   variable does not correspond to an argument. The maximum number of
+   arguments is 255 (indexed up to 254), so 255 work as a special flag.*/
+#define CO_CELL_NOT_AN_ARG 255
+
 /* This should be defined if a future statement modifies the syntax.
    For example, when a keyword is added.
 */
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -665,7 +665,7 @@
             return inner
         check(get_cell().__closure__[0], size(h + 'P'))
         # code
-        check(get_cell().__code__, size(h + '5i8Pi3P'))
+        check(get_cell().__code__, size(h + '5i9Pi3P'))
         # complex
         check(complex(0,1), size(h + '2d'))
         # method_descriptor (descriptor object)
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -51,7 +51,8 @@
            PyObject *lnotab)
 {
     PyCodeObject *co;
-    Py_ssize_t i;
+    unsigned char *cell2arg = NULL;
+    Py_ssize_t i, n_cellvars;
 
     /* Check argument types */
     if (argcount < 0 || kwonlyargcount < 0 || nlocals < 0 ||
@@ -68,12 +69,13 @@
         PyErr_BadInternalCall();
         return NULL;
     }
+    n_cellvars = PyTuple_GET_SIZE(cellvars);
     intern_strings(names);
     intern_strings(varnames);
     intern_strings(freevars);
     intern_strings(cellvars);
     /* Intern selected string constants */
-    for (i = PyTuple_Size(consts); --i >= 0; ) {
+    for (i = PyTuple_GET_SIZE(consts); --i >= 0; ) {
         PyObject *v = PyTuple_GetItem(consts, i);
         if (!PyUnicode_Check(v))
             continue;
@@ -81,35 +83,67 @@
             continue;
         PyUnicode_InternInPlace(&PyTuple_GET_ITEM(consts, i));
     }
+    /* Create mapping between cells and arguments if needed. */
+    if (n_cellvars) {
+        Py_ssize_t total_args = argcount + kwonlyargcount +
+            ((flags & CO_VARARGS) != 0) + ((flags & CO_VARKEYWORDS) != 0);
+        Py_ssize_t alloc_size = sizeof(unsigned char) * n_cellvars;
+        int used_cell2arg = 0;
+        cell2arg = PyMem_MALLOC(alloc_size);
+        if (cell2arg == NULL)
+            return NULL;
+        memset(cell2arg, CO_CELL_NOT_AN_ARG, alloc_size);
+        /* Find cells which are also arguments. */
+        for (i = 0; i < n_cellvars; i++) {
+            Py_ssize_t j;
+            PyObject *cell = PyTuple_GET_ITEM(cellvars, i);
+            for (j = 0; j < total_args; j++) {
+                PyObject *arg = PyTuple_GET_ITEM(varnames, j);
+                if (!PyUnicode_Compare(cell, arg)) {
+                    cell2arg[i] = j;
+                    used_cell2arg = 1;
+                    break;
+                }
+            }
+        }
+        if (!used_cell2arg) {
+            PyMem_FREE(cell2arg);
+            cell2arg = NULL;
+        }
+    }
     co = PyObject_NEW(PyCodeObject, &PyCode_Type);
-    if (co != NULL) {
-        co->co_argcount = argcount;
-        co->co_kwonlyargcount = kwonlyargcount;
-        co->co_nlocals = nlocals;
-        co->co_stacksize = stacksize;
-        co->co_flags = flags;
-        Py_INCREF(code);
-        co->co_code = code;
-        Py_INCREF(consts);
-        co->co_consts = consts;
-        Py_INCREF(names);
-        co->co_names = names;
-        Py_INCREF(varnames);
-        co->co_varnames = varnames;
-        Py_INCREF(freevars);
-        co->co_freevars = freevars;
-        Py_INCREF(cellvars);
-        co->co_cellvars = cellvars;
-        Py_INCREF(filename);
-        co->co_filename = filename;
-        Py_INCREF(name);
-        co->co_name = name;
-        co->co_firstlineno = firstlineno;
-        Py_INCREF(lnotab);
-        co->co_lnotab = lnotab;
-        co->co_zombieframe = NULL;
-        co->co_weakreflist = NULL;
+    if (co == NULL) {
+        if (cell2arg)
+            PyMem_FREE(cell2arg);
+        return NULL;
     }
+    co->co_argcount = argcount;
+    co->co_kwonlyargcount = kwonlyargcount;
+    co->co_nlocals = nlocals;
+    co->co_stacksize = stacksize;
+    co->co_flags = flags;
+    Py_INCREF(code);
+    co->co_code = code;
+    Py_INCREF(consts);
+    co->co_consts = consts;
+    Py_INCREF(names);
+    co->co_names = names;
+    Py_INCREF(varnames);
+    co->co_varnames = varnames;
+    Py_INCREF(freevars);
+    co->co_freevars = freevars;
+    Py_INCREF(cellvars);
+    co->co_cellvars = cellvars;
+    co->co_cell2arg = cell2arg;
+    Py_INCREF(filename);
+    co->co_filename = filename;
+    Py_INCREF(name);
+    co->co_name = name;
+    co->co_firstlineno = firstlineno;
+    Py_INCREF(lnotab);
+    co->co_lnotab = lnotab;
+    co->co_zombieframe = NULL;
+    co->co_weakreflist = NULL;
     return co;
 }
 
@@ -330,6 +364,8 @@
     Py_XDECREF(co->co_filename);
     Py_XDECREF(co->co_name);
     Py_XDECREF(co->co_lnotab);
+    if (co->co_cell2arg != NULL)
+        PyMem_FREE(co->co_cell2arg);
     if (co->co_zombieframe != NULL)
         PyObject_GC_Del(co->co_zombieframe);
     if (co->co_weakreflist != NULL)
diff --git a/Python/ceval.c b/Python/ceval.c
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -3357,56 +3357,24 @@
     }
 
     /* Allocate and initialize storage for cell vars, and copy free
-       vars into frame.  This isn't too efficient right now. */
-    if (PyTuple_GET_SIZE(co->co_cellvars)) {
-        int i, j, nargs, found;
-        Py_UNICODE *cellname, *argname;
+       vars into frame. */
+    for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
         PyObject *c;
-
-        nargs = total_args;
-        if (co->co_flags & CO_VARARGS)
-            nargs++;
-        if (co->co_flags & CO_VARKEYWORDS)
-            nargs++;
-
-        /* Initialize each cell var, taking into account
-           cell vars that are initialized from arguments.
-
-           Should arrange for the compiler to put cellvars
-           that are arguments at the beginning of the cellvars
-           list so that we can march over it more efficiently?
-        */
-        for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
-            cellname = PyUnicode_AS_UNICODE(
-                PyTuple_GET_ITEM(co->co_cellvars, i));
-            found = 0;
-            for (j = 0; j < nargs; j++) {
-                argname = PyUnicode_AS_UNICODE(
-                    PyTuple_GET_ITEM(co->co_varnames, j));
-                if (Py_UNICODE_strcmp(cellname, argname) == 0) {
-                    c = PyCell_New(GETLOCAL(j));
-                    if (c == NULL)
-                        goto fail;
-                    GETLOCAL(co->co_nlocals + i) = c;
-                    found = 1;
-                    break;
-                }
-            }
-            if (found == 0) {
-                c = PyCell_New(NULL);
-                if (c == NULL)
-                    goto fail;
-                SETLOCAL(co->co_nlocals + i, c);
-            }
-        }
+        int arg;
+        /* Possibly account for the cell variable being an argument. */
+        if (co->co_cell2arg != NULL &&
+            (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG)
+            c = PyCell_New(GETLOCAL(arg));
+        else
+            c = PyCell_New(NULL);
+        if (c == NULL)
+            goto fail;
+        SETLOCAL(co->co_nlocals + i, c);
     }
-    if (PyTuple_GET_SIZE(co->co_freevars)) {
-        int i;
-        for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
-            PyObject *o = PyTuple_GET_ITEM(closure, i);
-            Py_INCREF(o);
-            freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
-        }
+    for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
+        PyObject *o = PyTuple_GET_ITEM(closure, i);
+        Py_INCREF(o);
+        freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
     }
 
     if (co->co_flags & CO_GENERATOR) {

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list