Index: Include/compile.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/compile.h,v retrieving revision 2.40 diff -c -r2.40 compile.h *** Include/compile.h 5 Feb 2003 23:12:56 -0000 2.40 --- Include/compile.h 27 Jan 2004 17:04:42 -0000 *************** *** 25,30 **** --- 25,31 ---- PyObject *co_name; /* string (name, for reference) */ int co_firstlineno; /* first source line number */ PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */ + void *co_zombieframe; /* for optimization only (see frameobject.c) */ } PyCodeObject; /* Masks for co_flags above */ Index: Objects/frameobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/frameobject.c,v retrieving revision 2.77 diff -c -r2.77 frameobject.c *** Objects/frameobject.c 27 Jan 2004 16:08:07 -0000 2.77 --- Objects/frameobject.c 27 Jan 2004 17:05:25 -0000 *************** *** 350,421 **** {0} }; ! /* Stack frames are allocated and deallocated at a considerable rate. ! In an attempt to improve the speed of function calls, we maintain a ! separate free list of stack frames (just like integers are ! allocated in a special way -- see intobject.c). When a stack frame ! is on the free list, only the following members have a meaning: ! ob_type == &Frametype ! f_back next item on free list, or NULL ! f_nlocals number of locals ! f_stacksize size of value stack ! ob_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 ! malloc'ed object in its own right -- it is only the actual calls to ! malloc() that we are trying to save here, not the administration. ! After all, while a typical program may make millions of calls, a ! call depth of more than 20 or 30 is probably already exceptional ! unless the program contains run-away recursion. I hope. ! ! Later, MAXFREELIST was added to bound the # of frames saved on ! free_list. Else programs creating lots of cyclic trash involving ! frames could provoke free_list into growing without bound. */ ! static PyFrameObject *free_list = NULL; ! static int numfree = 0; /* number of frames currently in free_list */ ! #define MAXFREELIST 200 /* max value for numfree */ static void frame_dealloc(PyFrameObject *f) { ! int i, slots; ! PyObject **fastlocals; ! PyObject **p; PyObject_GC_UnTrack(f); Py_TRASHCAN_SAFE_BEGIN(f) /* Kill all local variables */ ! slots = f->f_nlocals + f->f_ncells + f->f_nfreevars; ! fastlocals = f->f_localsplus; ! for (i = slots; --i >= 0; ++fastlocals) { ! Py_XDECREF(*fastlocals); ! } /* Free stack */ if (f->f_stacktop != NULL) { ! for (p = f->f_valuestack; p < f->f_stacktop; p++) Py_XDECREF(*p); } Py_XDECREF(f->f_back); - Py_DECREF(f->f_code); Py_DECREF(f->f_builtins); Py_DECREF(f->f_globals); ! Py_XDECREF(f->f_locals); ! Py_XDECREF(f->f_trace); ! Py_XDECREF(f->f_exc_type); ! Py_XDECREF(f->f_exc_value); ! Py_XDECREF(f->f_exc_traceback); ! if (numfree < MAXFREELIST) { ! ++numfree; ! f->f_back = free_list; ! free_list = f; ! } ! else ! PyObject_GC_Del(f); Py_TRASHCAN_SAFE_END(f) } --- 350,419 ---- {0} }; ! /* When the refcount of a PyFrameObject reaches zero, the structure is ! not deallocated, but kept as a "zombie" in the co_zombieframe field ! of the associated code object. The next time a frame for the same ! code object is needed, the zombie is resurrected. The goal is to ! save not only the time spent allocating/deallocating the structure, ! but also the time spent initializing its numerous fields with ! information from the code object. ! ! In zombie mode, no field of PyFrameObject holds a reference, but ! the following fields are still valid: ! ! * ob_type, ob_size, f_code, f_valuestack, ! f_nlocals, f_ncells, f_nfreevars, f_stacksize; ! ! * f_locals, f_trace, ! f_exc_type, f_exc_value, f_exc_traceback are NULL; ! ! * f_localsplus does not require re-allocation and ! the local variables in f_localsplus are NULL. ! ! Each PyCodeObject can only have a single associated zombie frame. ! This gives a bound on the amount of wasted memory. */ ! #define ZAP(o) do { \ ! PyObject *_tmp = o; \ ! if (_tmp != NULL) { \ ! o = NULL; \ ! Py_DECREF(_tmp); \ ! } \ ! } while (0) static void frame_dealloc(PyFrameObject *f) { ! PyObject **p, **valuestack; ! PyCodeObject *co; PyObject_GC_UnTrack(f); Py_TRASHCAN_SAFE_BEGIN(f) /* Kill all local variables */ ! valuestack = f->f_valuestack; ! for (p = f->f_localsplus; p < valuestack; p++) ! ZAP(*p); /* Free stack */ if (f->f_stacktop != NULL) { ! for (p = valuestack; p < f->f_stacktop; p++) Py_XDECREF(*p); } Py_XDECREF(f->f_back); Py_DECREF(f->f_builtins); Py_DECREF(f->f_globals); ! ZAP(f->f_locals); ! ZAP(f->f_trace); ! ZAP(f->f_exc_type); ! ZAP(f->f_exc_value); ! ZAP(f->f_exc_traceback); ! co = f->f_code; ! if (co->co_zombieframe != NULL) ! PyObject_GC_Del(co->co_zombieframe); ! co->co_zombieframe = f; ! Py_DECREF(co); Py_TRASHCAN_SAFE_END(f) } *************** *** 540,546 **** PyFrameObject *back = tstate->frame; PyFrameObject *f; PyObject *builtins; - int extras, ncells, nfrees; #ifdef Py_DEBUG if (code == NULL || globals == NULL || !PyDict_Check(globals) || --- 538,543 ---- *************** *** 549,557 **** return NULL; } #endif - 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) { --- 546,551 ---- *************** *** 582,649 **** assert(builtins != NULL && PyDict_Check(builtins)); Py_INCREF(builtins); } ! if (free_list == NULL) { f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras); if (f == NULL) { Py_DECREF(builtins); return NULL; } ! } ! else { ! assert(numfree > 0); ! --numfree; ! f = free_list; ! free_list = free_list->f_back; ! if (f->ob_size < extras) { ! f = PyObject_GC_Resize(PyFrameObject, f, extras); ! if (f == NULL) { ! Py_DECREF(builtins); ! return NULL; ! } ! } ! _Py_NewReference((PyObject *)f); } f->f_builtins = builtins; Py_XINCREF(back); f->f_back = back; - Py_INCREF(code); - f->f_code = code; Py_INCREF(globals); f->f_globals = globals; /* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */ if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == (CO_NEWLOCALS | CO_OPTIMIZED)) ! locals = NULL; /* PyFrame_FastToLocals() will set. */ else if (code->co_flags & CO_NEWLOCALS) { locals = PyDict_New(); if (locals == NULL) { Py_DECREF(f); return NULL; } } else { if (locals == NULL) locals = globals; Py_INCREF(locals); } - f->f_locals = locals; - f->f_trace = NULL; - f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; f->f_tstate = tstate; f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_restricted = (builtins != tstate->interp->builtins); f->f_iblock = 0; - f->f_nlocals = code->co_nlocals; - f->f_stacksize = code->co_stacksize; - f->f_ncells = ncells; - f->f_nfreevars = nfrees; - extras = f->f_nlocals + ncells + nfrees; - memset(f->f_localsplus, 0, extras * sizeof(f->f_localsplus[0])); - - f->f_valuestack = f->f_localsplus + extras; f->f_stacktop = f->f_valuestack; _PyObject_GC_TRACK(f); return f; --- 576,642 ---- assert(builtins != NULL && PyDict_Check(builtins)); Py_INCREF(builtins); } ! if (code->co_zombieframe != NULL) { ! /* Resurrect the zombie frame */ ! f = code->co_zombieframe; ! code->co_zombieframe = NULL; ! _Py_NewReference((PyObject *)f); ! assert(f->f_code == code); ! } ! else { ! /* Build a new frame */ ! int extras, ncells, nfrees; ! ncells = PyTuple_GET_SIZE(code->co_cellvars); ! nfrees = PyTuple_GET_SIZE(code->co_freevars); ! extras = code->co_stacksize + code->co_nlocals + ncells + nfrees; f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras); if (f == NULL) { Py_DECREF(builtins); return NULL; } ! f->f_code = code; ! f->f_nlocals = code->co_nlocals; ! f->f_stacksize = code->co_stacksize; ! f->f_ncells = ncells; ! f->f_nfreevars = nfrees; ! extras = f->f_nlocals + ncells + nfrees; ! f->f_valuestack = f->f_localsplus + extras; ! memset(f->f_localsplus, 0, extras * sizeof(f->f_localsplus[0])); ! f->f_locals = NULL; ! f->f_trace = NULL; ! f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; } f->f_builtins = builtins; + Py_INCREF(code); Py_XINCREF(back); f->f_back = back; Py_INCREF(globals); f->f_globals = globals; /* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */ if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == (CO_NEWLOCALS | CO_OPTIMIZED)) ! ; /* f_locals == NULL, will be set by PyFrame_FastToLocals() */ else if (code->co_flags & CO_NEWLOCALS) { locals = PyDict_New(); if (locals == NULL) { Py_DECREF(f); return NULL; } + f->f_locals = locals; } else { if (locals == NULL) locals = globals; Py_INCREF(locals); + f->f_locals = locals; } f->f_tstate = tstate; f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_restricted = (builtins != tstate->interp->builtins); f->f_iblock = 0; f->f_stacktop = f->f_valuestack; _PyObject_GC_TRACK(f); return f; *************** *** 802,819 **** PyErr_Restore(error_type, error_value, error_traceback); } - /* Clear out the free list */ - void PyFrame_Fini(void) { - while (free_list != NULL) { - PyFrameObject *f = free_list; - free_list = free_list->f_back; - PyObject_GC_Del(f); - --numfree; - } - assert(numfree == 0); Py_XDECREF(builtin_object); builtin_object = NULL; } --- 795,803 ---- Index: Python/compile.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.298 diff -c -r2.298 compile.c *** Python/compile.c 29 Nov 2003 23:52:13 -0000 2.298 --- Python/compile.c 27 Jan 2004 17:05:37 -0000 *************** *** 165,170 **** --- 165,172 ---- Py_XDECREF(co->co_filename); Py_XDECREF(co->co_name); Py_XDECREF(co->co_lnotab); + if (co->co_zombieframe != NULL) + PyObject_GC_Del(co->co_zombieframe); PyObject_DEL(co); } *************** *** 464,469 **** --- 466,472 ---- if (PyTuple_GET_SIZE(freevars) == 0 && PyTuple_GET_SIZE(cellvars) == 0) co->co_flags |= CO_NOFREE; + co->co_zombieframe = NULL; } return co; }