Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2b724da

Browse files
committed
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.
1 parent 55087f0 commit 2b724da

3 files changed

Lines changed: 33 additions & 28 deletions

File tree

Include/frameobject.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ typedef struct _frame {
2020
PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
2121
PyObject *f_globals; /* global symbol table (PyDictObject) */
2222
PyObject *f_locals; /* local symbol table (PyDictObject) */
23-
PyObject *f_closure; /* environment for free variables */
2423
PyObject **f_valuestack; /* points after the last local */
2524
PyObject *f_trace; /* Trace function */
2625
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
@@ -31,7 +30,10 @@ typedef struct _frame {
3130
in this scope */
3231
int f_iblock; /* index in f_blockstack */
3332
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
33+
int f_size; /* size of localsplus */
3434
int f_nlocals; /* number of locals */
35+
int f_ncells;
36+
int f_nfreevars;
3537
int f_stacksize; /* size of value stack */
3638
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
3739
} PyFrameObject;

Objects/frameobject.c

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ frame_setattr(PyFrameObject *f, char *name, PyObject *value)
4949
f_back next item on free list, or NULL
5050
f_nlocals number of locals
5151
f_stacksize size of value stack
52+
f_size size of localsplus
5253
Note that the value and block stacks are preserved -- this can save
5354
another malloc() call or two (and two free() calls as well!).
5455
Also note that, unlike for integers, each frame object is a
@@ -79,7 +80,6 @@ frame_dealloc(PyFrameObject *f)
7980
Py_XDECREF(f->f_builtins);
8081
Py_XDECREF(f->f_globals);
8182
Py_XDECREF(f->f_locals);
82-
Py_XDECREF(f->f_closure);
8383
Py_XDECREF(f->f_trace);
8484
Py_XDECREF(f->f_exc_type);
8585
Py_XDECREF(f->f_exc_value);
@@ -114,7 +114,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
114114
static PyObject *builtin_object;
115115
PyFrameObject *f;
116116
PyObject *builtins;
117-
int extras, ncells;
117+
int extras, ncells, nfrees;
118118

119119
if (builtin_object == NULL) {
120120
builtin_object = PyString_InternFromString("__builtins__");
@@ -128,8 +128,9 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
128128
PyErr_BadInternalCall();
129129
return NULL;
130130
}
131-
extras = code->co_stacksize + code->co_nlocals;
132131
ncells = PyTuple_GET_SIZE(code->co_cellvars);
132+
nfrees = PyTuple_GET_SIZE(code->co_freevars);
133+
extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
133134
if (back == NULL || back->f_globals != globals) {
134135
builtins = PyDict_GetItem(globals, builtin_object);
135136
if (builtins != NULL && PyModule_Check(builtins))
@@ -150,19 +151,21 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
150151
if (f == NULL)
151152
return (PyFrameObject *)PyErr_NoMemory();
152153
PyObject_INIT(f, &PyFrame_Type);
154+
f->f_size = extras;
153155
}
154156
else {
155157
f = free_list;
156158
free_list = free_list->f_back;
157-
if (f->f_nlocals + f->f_stacksize < extras) {
159+
if (f->f_size < extras) {
158160
f = (PyFrameObject *)
159161
PyObject_REALLOC(f, sizeof(PyFrameObject) +
160162
extras*sizeof(PyObject *));
161163
if (f == NULL)
162164
return (PyFrameObject *)PyErr_NoMemory();
165+
f->f_size = extras;
163166
}
164167
else
165-
extras = f->f_nlocals + f->f_stacksize;
168+
extras = f->f_size;
166169
PyObject_INIT(f, &PyFrame_Type);
167170
}
168171
if (builtins == NULL) {
@@ -199,22 +202,6 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
199202
locals = globals;
200203
Py_INCREF(locals);
201204
}
202-
if (closure || ncells) {
203-
int i, size;
204-
size = ncells;
205-
if (closure)
206-
size += PyTuple_GET_SIZE(closure);
207-
f->f_closure = PyTuple_New(size);
208-
for (i = 0; i < ncells; ++i)
209-
PyTuple_SET_ITEM(f->f_closure, i, PyCell_New(NULL));
210-
for (i = ncells; i < size; ++i) {
211-
PyObject *o = PyTuple_GET_ITEM(closure, i - ncells);
212-
Py_INCREF(o);
213-
PyTuple_SET_ITEM(f->f_closure, i, o);
214-
}
215-
}
216-
else
217-
f->f_closure = NULL;
218205
f->f_locals = locals;
219206
f->f_trace = NULL;
220207
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,
225212
f->f_restricted = (builtins != tstate->interp->builtins);
226213
f->f_iblock = 0;
227214
f->f_nlocals = code->co_nlocals;
228-
f->f_stacksize = extras - code->co_nlocals;
215+
f->f_stacksize = code->co_stacksize;
216+
f->f_ncells = ncells;
217+
f->f_nfreevars = nfrees;
229218

230219
while (--extras >= 0)
231220
f->f_localsplus[extras] = NULL;
232221

233-
f->f_valuestack = f->f_localsplus + f->f_nlocals;
222+
f->f_valuestack = f->f_localsplus + (f->f_nlocals + ncells + nfrees);
234223

235224
return f;
236225
}
@@ -261,6 +250,8 @@ PyFrame_BlockPop(PyFrameObject *f)
261250

262251
/* Convert between "fast" version of locals and dictionary version */
263252

253+
/* XXX should also copy free variables and cell variables */
254+
264255
void
265256
PyFrame_FastToLocals(PyFrameObject *f)
266257
{

Python/ceval.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
368368
register PyObject *t;
369369
register PyObject *stream = NULL; /* for PRINT opcodes */
370370
register PyFrameObject *f; /* Current frame */
371-
register PyObject **fastlocals;
371+
register PyObject **fastlocals, **freevars;
372372
PyObject *retval = NULL; /* Return value */
373373
PyThreadState *tstate = PyThreadState_GET();
374374
unsigned char *first_instr;
@@ -439,6 +439,7 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
439439

440440
tstate->frame = f;
441441
fastlocals = f->f_localsplus;
442+
freevars = f->f_localsplus + f->f_nlocals;
442443

443444
if (co->co_argcount > 0 ||
444445
co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) {
@@ -572,6 +573,17 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
572573
goto fail;
573574
}
574575
}
576+
/* Allocate storage for cell vars and copy free vars into frame */
577+
if (f->f_ncells) {
578+
int i;
579+
for (i = 0; i < f->f_ncells; ++i)
580+
freevars[i] = PyCell_New(NULL);
581+
}
582+
if (f->f_nfreevars) {
583+
int i;
584+
for (i = 0; i < f->f_nfreevars; ++i)
585+
freevars[f->f_ncells + i] = PyTuple_GET_ITEM(closure, i);
586+
}
575587

576588
if (tstate->sys_tracefunc != NULL) {
577589
/* tstate->sys_tracefunc, if defined, is a function that
@@ -1623,21 +1635,21 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
16231635
continue;
16241636

16251637
case LOAD_CLOSURE:
1626-
x = PyTuple_GET_ITEM(f->f_closure, oparg);
1638+
x = freevars[oparg];
16271639
Py_INCREF(x);
16281640
PUSH(x);
16291641
break;
16301642

16311643
case LOAD_DEREF:
1632-
x = PyTuple_GET_ITEM(f->f_closure, oparg);
1644+
x = freevars[oparg];
16331645
w = PyCell_Get(x);
16341646
Py_INCREF(w);
16351647
PUSH(w);
16361648
break;
16371649

16381650
case STORE_DEREF:
16391651
w = POP();
1640-
x = PyTuple_GET_ITEM(f->f_closure, oparg);
1652+
x = freevars[oparg];
16411653
PyCell_Set(x, w);
16421654
continue;
16431655

0 commit comments

Comments
 (0)