From 2b724da8d9cd0c41a51e798eca75018ce155e997 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Mon, 29 Jan 2001 22:51:52 +0000 Subject: [PATCH] Remove f_closure slot of frameobject and use f_localsplus instead. This change eliminates an extra malloc/free when a frame with free variables is created. Any cell vars or free vars are stored in f_localsplus after the locals and before the stack. eval_code2() fills in the appropriate values after handling initialization of locals. To track the size the frame has an f_size member that tracks the total size of f_localsplus. It used to be implicitly f_nlocals + f_stacksize. --- Include/frameobject.h | 4 +++- Objects/frameobject.c | 37 ++++++++++++++----------------------- Python/ceval.c | 20 ++++++++++++++++---- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/Include/frameobject.h b/Include/frameobject.h index c19f1d8c276..d1a310a288d 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -20,7 +20,6 @@ typedef struct _frame { PyObject *f_builtins; /* builtin symbol table (PyDictObject) */ PyObject *f_globals; /* global symbol table (PyDictObject) */ PyObject *f_locals; /* local symbol table (PyDictObject) */ - PyObject *f_closure; /* environment for free variables */ PyObject **f_valuestack; /* points after the last local */ PyObject *f_trace; /* Trace function */ PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; @@ -31,7 +30,10 @@ typedef struct _frame { in this scope */ int f_iblock; /* index in f_blockstack */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ + int f_size; /* size of localsplus */ int f_nlocals; /* number of locals */ + int f_ncells; + int f_nfreevars; int f_stacksize; /* size of value stack */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ } PyFrameObject; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 0f6372bb194..18fb0b0adce 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -49,6 +49,7 @@ frame_setattr(PyFrameObject *f, char *name, PyObject *value) f_back next item on free list, or NULL f_nlocals number of locals f_stacksize size of value stack + f_size size of localsplus Note that the value and block stacks are preserved -- this can save another malloc() call or two (and two free() calls as well!). Also note that, unlike for integers, each frame object is a @@ -79,7 +80,6 @@ frame_dealloc(PyFrameObject *f) Py_XDECREF(f->f_builtins); Py_XDECREF(f->f_globals); Py_XDECREF(f->f_locals); - Py_XDECREF(f->f_closure); Py_XDECREF(f->f_trace); Py_XDECREF(f->f_exc_type); Py_XDECREF(f->f_exc_value); @@ -114,7 +114,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, static PyObject *builtin_object; PyFrameObject *f; PyObject *builtins; - int extras, ncells; + int extras, ncells, nfrees; if (builtin_object == NULL) { builtin_object = PyString_InternFromString("__builtins__"); @@ -128,8 +128,9 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyErr_BadInternalCall(); return NULL; } - extras = code->co_stacksize + code->co_nlocals; ncells = PyTuple_GET_SIZE(code->co_cellvars); + nfrees = PyTuple_GET_SIZE(code->co_freevars); + extras = code->co_stacksize + code->co_nlocals + ncells + nfrees; if (back == NULL || back->f_globals != globals) { builtins = PyDict_GetItem(globals, builtin_object); if (builtins != NULL && PyModule_Check(builtins)) @@ -150,19 +151,21 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, if (f == NULL) return (PyFrameObject *)PyErr_NoMemory(); PyObject_INIT(f, &PyFrame_Type); + f->f_size = extras; } else { f = free_list; free_list = free_list->f_back; - if (f->f_nlocals + f->f_stacksize < extras) { + if (f->f_size < extras) { f = (PyFrameObject *) PyObject_REALLOC(f, sizeof(PyFrameObject) + extras*sizeof(PyObject *)); if (f == NULL) return (PyFrameObject *)PyErr_NoMemory(); + f->f_size = extras; } else - extras = f->f_nlocals + f->f_stacksize; + extras = f->f_size; PyObject_INIT(f, &PyFrame_Type); } if (builtins == NULL) { @@ -199,22 +202,6 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, locals = globals; Py_INCREF(locals); } - if (closure || ncells) { - int i, size; - size = ncells; - if (closure) - size += PyTuple_GET_SIZE(closure); - f->f_closure = PyTuple_New(size); - for (i = 0; i < ncells; ++i) - PyTuple_SET_ITEM(f->f_closure, i, PyCell_New(NULL)); - for (i = ncells; i < size; ++i) { - PyObject *o = PyTuple_GET_ITEM(closure, i - ncells); - Py_INCREF(o); - PyTuple_SET_ITEM(f->f_closure, i, o); - } - } - else - f->f_closure = NULL; f->f_locals = locals; f->f_trace = NULL; f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; @@ -225,12 +212,14 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, f->f_restricted = (builtins != tstate->interp->builtins); f->f_iblock = 0; f->f_nlocals = code->co_nlocals; - f->f_stacksize = extras - code->co_nlocals; + f->f_stacksize = code->co_stacksize; + f->f_ncells = ncells; + f->f_nfreevars = nfrees; while (--extras >= 0) f->f_localsplus[extras] = NULL; - f->f_valuestack = f->f_localsplus + f->f_nlocals; + f->f_valuestack = f->f_localsplus + (f->f_nlocals + ncells + nfrees); return f; } @@ -261,6 +250,8 @@ PyFrame_BlockPop(PyFrameObject *f) /* Convert between "fast" version of locals and dictionary version */ +/* XXX should also copy free variables and cell variables */ + void PyFrame_FastToLocals(PyFrameObject *f) { diff --git a/Python/ceval.c b/Python/ceval.c index c7b5696d042..5d593f28b26 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -368,7 +368,7 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, register PyObject *t; register PyObject *stream = NULL; /* for PRINT opcodes */ register PyFrameObject *f; /* Current frame */ - register PyObject **fastlocals; + register PyObject **fastlocals, **freevars; PyObject *retval = NULL; /* Return value */ PyThreadState *tstate = PyThreadState_GET(); unsigned char *first_instr; @@ -439,6 +439,7 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, tstate->frame = f; fastlocals = f->f_localsplus; + freevars = f->f_localsplus + f->f_nlocals; if (co->co_argcount > 0 || co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) { @@ -572,6 +573,17 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, goto fail; } } + /* Allocate storage for cell vars and copy free vars into frame */ + if (f->f_ncells) { + int i; + for (i = 0; i < f->f_ncells; ++i) + freevars[i] = PyCell_New(NULL); + } + if (f->f_nfreevars) { + int i; + for (i = 0; i < f->f_nfreevars; ++i) + freevars[f->f_ncells + i] = PyTuple_GET_ITEM(closure, i); + } if (tstate->sys_tracefunc != NULL) { /* tstate->sys_tracefunc, if defined, is a function that @@ -1623,13 +1635,13 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, continue; case LOAD_CLOSURE: - x = PyTuple_GET_ITEM(f->f_closure, oparg); + x = freevars[oparg]; Py_INCREF(x); PUSH(x); break; case LOAD_DEREF: - x = PyTuple_GET_ITEM(f->f_closure, oparg); + x = freevars[oparg]; w = PyCell_Get(x); Py_INCREF(w); PUSH(w); @@ -1637,7 +1649,7 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, case STORE_DEREF: w = POP(); - x = PyTuple_GET_ITEM(f->f_closure, oparg); + x = freevars[oparg]; PyCell_Set(x, w); continue;