summaryrefslogtreecommitdiffstats
path: root/Objects/frameobject.c
diff options
context:
space:
mode:
Diffstat (limited to 'Objects/frameobject.c')
-rw-r--r--Objects/frameobject.c220
1 files changed, 124 insertions, 96 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 9aabc7a..fcb5e4e 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -350,13 +350,32 @@ static PyGetSetDef frame_getsetlist[] = {
};
/* 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:
+ In an attempt to improve the speed of function calls, we:
+
+ 1. Hold a single "zombie" frame on each code object. This retains
+ the allocated and initialised frame object from an invocation of
+ the code object. The zombie is reanimated the next time we need a
+ frame object for that code object. Doing this saves the malloc/
+ realloc required when using a free_list frame that isn't the
+ correct size. It also saves some field initialisation.
+
+ 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_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.
+
+ 2. We also 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
@@ -380,41 +399,43 @@ static int numfree = 0; /* number of frames currently in free_list */
static void
frame_dealloc(PyFrameObject *f)
{
- int i, slots;
- PyObject **fastlocals;
- PyObject **p;
+ PyObject **p, **valuestack;
+ PyCodeObject *co;
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);
- }
+ valuestack = f->f_valuestack;
+ for (p = f->f_localsplus; p < valuestack; p++)
+ Py_CLEAR(*p);
/* Free stack */
if (f->f_stacktop != NULL) {
- for (p = f->f_valuestack; p < f->f_stacktop; p++)
+ for (p = 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) {
+ Py_CLEAR(f->f_locals);
+ Py_CLEAR(f->f_trace);
+ Py_CLEAR(f->f_exc_type);
+ Py_CLEAR(f->f_exc_value);
+ Py_CLEAR(f->f_exc_traceback);
+
+ co = f->f_code;
+ if (co != NULL && co->co_zombieframe == NULL)
+ co->co_zombieframe = f;
+ else if (numfree < MAXFREELIST) {
++numfree;
f->f_back = free_list;
free_list = f;
- }
- else
+ }
+ else
PyObject_GC_Del(f);
+
+ Py_XDECREF(co);
Py_TRASHCAN_SAFE_END(f)
}
@@ -435,7 +456,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
Py_VISIT(f->f_exc_traceback);
/* locals */
- slots = f->f_nlocals + f->f_ncells + f->f_nfreevars;
+ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars);
fastlocals = f->f_localsplus;
for (i = slots; --i >= 0; ++fastlocals)
Py_VISIT(*fastlocals);
@@ -468,7 +489,7 @@ frame_clear(PyFrameObject *f)
Py_CLEAR(f->f_trace);
/* locals */
- slots = f->f_nlocals + f->f_ncells + f->f_nfreevars;
+ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars);
fastlocals = f->f_localsplus;
for (i = slots; --i >= 0; ++fastlocals)
Py_CLEAR(*fastlocals);
@@ -532,7 +553,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
PyFrameObject *back = tstate->frame;
PyFrameObject *f;
PyObject *builtins;
- Py_ssize_t extras, ncells, nfrees, i;
+ Py_ssize_t i;
#ifdef Py_DEBUG
if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
@@ -541,9 +562,6 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
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) {
@@ -574,71 +592,82 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
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;
- }
+ if (code->co_zombieframe != NULL) {
+ f = code->co_zombieframe;
+ code->co_zombieframe = NULL;
+ _Py_NewReference((PyObject *)f);
+ assert(f->f_code == code);
}
- 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);
+ else {
+ Py_ssize_t 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;
+ 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_code = code;
+ extras = code->co_nlocals + ncells + nfrees;
+ f->f_valuestack = f->f_localsplus + extras;
+ for (i=0; i<extras; i++)
+ f->f_localsplus[i] = NULL;
+ 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_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. */
+ ; /* 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_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;
- /* Tim said it's ok to replace memset */
- for (i=0; i<extras; i++)
- f->f_localsplus[i] = NULL;
-
- f->f_valuestack = f->f_localsplus + extras;
- f->f_stacktop = f->f_valuestack;
+
+ f->f_stacktop = f->f_valuestack;
_PyObject_GC_TRACK(f);
return f;
}
@@ -725,7 +754,9 @@ PyFrame_FastToLocals(PyFrameObject *f)
PyObject *locals, *map;
PyObject **fast;
PyObject *error_type, *error_value, *error_traceback;
+ PyCodeObject *co;
Py_ssize_t j;
+ int ncells, nfreevars;
if (f == NULL)
return;
locals = f->f_locals;
@@ -736,27 +767,24 @@ PyFrame_FastToLocals(PyFrameObject *f)
return;
}
}
- map = f->f_code->co_varnames;
+ co = f->f_code;
+ map = co->co_varnames;
if (!PyTuple_Check(map))
return;
PyErr_Fetch(&error_type, &error_value, &error_traceback);
fast = f->f_localsplus;
j = PyTuple_GET_SIZE(map);
- if (j > f->f_nlocals)
- j = f->f_nlocals;
- if (f->f_nlocals)
+ if (j > co->co_nlocals)
+ j = co->co_nlocals;
+ if (co->co_nlocals)
map_to_dict(map, j, locals, fast, 0);
- if (f->f_ncells || f->f_nfreevars) {
- if (!(PyTuple_Check(f->f_code->co_cellvars)
- && PyTuple_Check(f->f_code->co_freevars))) {
- return;
- }
- map_to_dict(f->f_code->co_cellvars,
- PyTuple_GET_SIZE(f->f_code->co_cellvars),
- locals, fast + f->f_nlocals, 1);
- map_to_dict(f->f_code->co_freevars,
- PyTuple_GET_SIZE(f->f_code->co_freevars),
- locals, fast + f->f_nlocals + f->f_ncells, 1);
+ ncells = PyTuple_GET_SIZE(co->co_cellvars);
+ nfreevars = PyTuple_GET_SIZE(co->co_freevars);
+ if (ncells || nfreevars) {
+ map_to_dict(co->co_cellvars, ncells,
+ locals, fast + co->co_nlocals, 1);
+ map_to_dict(co->co_freevars, nfreevars,
+ locals, fast + co->co_nlocals + ncells, 1);
}
PyErr_Restore(error_type, error_value, error_traceback);
}
@@ -768,11 +796,14 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
PyObject *locals, *map;
PyObject **fast;
PyObject *error_type, *error_value, *error_traceback;
+ PyCodeObject *co;
Py_ssize_t j;
+ int ncells, nfreevars;
if (f == NULL)
return;
locals = f->f_locals;
- map = f->f_code->co_varnames;
+ co = f->f_code;
+ map = co->co_varnames;
if (locals == NULL)
return;
if (!PyTuple_Check(map))
@@ -780,21 +811,18 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
PyErr_Fetch(&error_type, &error_value, &error_traceback);
fast = f->f_localsplus;
j = PyTuple_GET_SIZE(map);
- if (j > f->f_nlocals)
- j = f->f_nlocals;
- if (f->f_nlocals)
- dict_to_map(f->f_code->co_varnames, j, locals, fast, 0, clear);
- if (f->f_ncells || f->f_nfreevars) {
- if (!(PyTuple_Check(f->f_code->co_cellvars)
- && PyTuple_Check(f->f_code->co_freevars)))
- return;
- dict_to_map(f->f_code->co_cellvars,
- PyTuple_GET_SIZE(f->f_code->co_cellvars),
- locals, fast + f->f_nlocals, 1, clear);
- dict_to_map(f->f_code->co_freevars,
- PyTuple_GET_SIZE(f->f_code->co_freevars),
- locals, fast + f->f_nlocals + f->f_ncells, 1,
- clear);
+ if (j > co->co_nlocals)
+ j = co->co_nlocals;
+ if (co->co_nlocals)
+ dict_to_map(co->co_varnames, j, locals, fast, 0, clear);
+ ncells = PyTuple_GET_SIZE(co->co_cellvars);
+ nfreevars = PyTuple_GET_SIZE(co->co_freevars);
+ if (ncells || nfreevars) {
+ dict_to_map(co->co_cellvars, ncells,
+ locals, fast + co->co_nlocals, 1, clear);
+ dict_to_map(co->co_freevars, nfreevars,
+ locals, fast + co->co_nlocals + ncells, 1,
+ clear);
}
PyErr_Restore(error_type, error_value, error_traceback);
}