Skip to content

Commit 6092957

Browse files
authored
bpo-45786: Allocate space for frame in frame object. (GH-29729)
1 parent 7431448 commit 6092957

File tree

12 files changed

+76
-181
lines changed

12 files changed

+76
-181
lines changed

Include/cpython/frameobject.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ struct _frame {
1212
int f_lineno; /* Current line number. Only valid if non-zero */
1313
char f_trace_lines; /* Emit per-line trace events? */
1414
char f_trace_opcodes; /* Emit per-opcode trace events? */
15-
char f_own_locals_memory; /* This frame owns the memory for the locals */
15+
char f_owns_frame; /* This frame owns the frame */
16+
/* The frame data, if this frame object owns the frame */
17+
PyObject *_f_frame_data[1];
1618
};
1719

1820
/* Standard object interface */
@@ -26,7 +28,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,
2628

2729
/* only internal use */
2830
PyFrameObject*
29-
_PyFrame_New_NoTrack(struct _interpreter_frame *, int);
31+
_PyFrame_New_NoTrack(PyCodeObject *code);
3032

3133

3234
/* The rest of the interface is specific for frame objects */

Include/internal/pycore_frame.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ static inline void _PyFrame_StackPush(InterpreterFrame *f, PyObject *value) {
6969

7070
#define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *))
7171

72-
InterpreterFrame *
73-
_PyInterpreterFrame_HeapAlloc(PyFunctionObject *func, PyObject *locals);
72+
InterpreterFrame *_PyFrame_Copy(InterpreterFrame *frame);
7473

7574
static inline void
7675
_PyFrame_InitializeSpecials(
@@ -139,8 +138,8 @@ _PyFrame_GetFrameObject(InterpreterFrame *frame)
139138
* take should be set to 1 for heap allocated
140139
* frames like the ones in generators and coroutines.
141140
*/
142-
int
143-
_PyFrame_Clear(InterpreterFrame * frame, int take);
141+
void
142+
_PyFrame_Clear(InterpreterFrame * frame);
144143

145144
int
146145
_PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg);

Include/internal/pycore_gc.h

-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ extern Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate);
167167

168168

169169
// Functions to clear types free lists
170-
extern void _PyFrame_ClearFreeList(PyInterpreterState *interp);
171170
extern void _PyTuple_ClearFreeList(PyInterpreterState *interp);
172171
extern void _PyFloat_ClearFreeList(PyInterpreterState *interp);
173172
extern void _PyList_ClearFreeList(PyInterpreterState *interp);

Include/internal/pycore_interp.h

-14
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ struct _Py_unicode_state {
9393
# define PyTuple_MAXFREELIST 1
9494
# define PyList_MAXFREELIST 0
9595
# define PyDict_MAXFREELIST 0
96-
# define PyFrame_MAXFREELIST 0
9796
# define _PyAsyncGen_MAXFREELIST 0
9897
# define PyContext_MAXFREELIST 0
9998
#endif
@@ -158,18 +157,6 @@ struct _Py_dict_state {
158157
#endif
159158
};
160159

161-
#ifndef PyFrame_MAXFREELIST
162-
# define PyFrame_MAXFREELIST 200
163-
#endif
164-
165-
struct _Py_frame_state {
166-
#if PyFrame_MAXFREELIST > 0
167-
PyFrameObject *free_list;
168-
/* number of frames currently in free_list */
169-
int numfree;
170-
#endif
171-
};
172-
173160
#ifndef _PyAsyncGen_MAXFREELIST
174161
# define _PyAsyncGen_MAXFREELIST 80
175162
#endif
@@ -332,7 +319,6 @@ struct _is {
332319
struct _Py_tuple_state tuple;
333320
struct _Py_list_state list;
334321
struct _Py_dict_state dict_state;
335-
struct _Py_frame_state frame;
336322
struct _Py_async_gen_state async_gen;
337323
struct _Py_context_state context;
338324
struct _Py_exc_state exc_state;

Lib/test/test_exceptions.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def check(self, src, lineno, offset, encoding='utf-8'):
209209
src = src.decode(encoding, 'replace')
210210
line = src.split('\n')[lineno-1]
211211
self.assertIn(line, cm.exception.text)
212-
212+
213213
def test_error_offset_continuation_characters(self):
214214
check = self.check
215215
check('"\\\n"(1 for c in I,\\\n\\', 2, 2)
@@ -1342,9 +1342,7 @@ def recurse(cnt):
13421342
"""
13431343
with SuppressCrashReport():
13441344
rc, out, err = script_helper.assert_python_failure("-c", code)
1345-
self.assertIn(b'Fatal Python error: _PyErr_NormalizeException: '
1346-
b'Cannot recover from MemoryErrors while '
1347-
b'normalizing exceptions.', err)
1345+
self.assertIn(b'MemoryError', err)
13481346

13491347
@cpython_only
13501348
def test_MemoryError(self):

Lib/test/test_sys.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1320,9 +1320,10 @@ class C(object): pass
13201320
# sys.floatinfo
13211321
check(sys.float_info, vsize('') + self.P * len(sys.float_info))
13221322
# frame
1323-
import inspect
1324-
x = inspect.currentframe()
1325-
check(x, size('3Pi3c'))
1323+
def func():
1324+
return sys._getframe()
1325+
x = func()
1326+
check(x, size('3Pi3c8P2iciP'))
13261327
# function
13271328
def func(): pass
13281329
check(func, size('14Pi'))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Allocate space for the interpreter frame in the frame object, to avoid an
2+
additional allocation when the frame object outlives the frame activation.

Modules/gcmodule.c

-1
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,6 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
10381038
static void
10391039
clear_freelists(PyInterpreterState *interp)
10401040
{
1041-
_PyFrame_ClearFreeList(interp);
10421041
_PyTuple_ClearFreeList(interp);
10431042
_PyFloat_ClearFreeList(interp);
10441043
_PyList_ClearFreeList(interp);

Objects/frameobject.c

+26-115
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ static PyGetSetDef frame_getsetlist[] = {
610610
f_back next item on free list, or NULL
611611
*/
612612

613-
static void _Py_HOT_FUNCTION
613+
static void
614614
frame_dealloc(PyFrameObject *f)
615615
{
616616
if (_PyObject_GC_IS_TRACKED(f)) {
@@ -621,9 +621,10 @@ frame_dealloc(PyFrameObject *f)
621621
PyCodeObject *co = NULL;
622622

623623
/* Kill all local variables including specials, if we own them */
624-
if (f->f_own_locals_memory) {
625-
f->f_own_locals_memory = 0;
626-
InterpreterFrame *frame = f->f_frame;
624+
if (f->f_owns_frame) {
625+
f->f_owns_frame = 0;
626+
assert(f->f_frame == (InterpreterFrame *)f->_f_frame_data);
627+
InterpreterFrame *frame = (InterpreterFrame *)f->_f_frame_data;
627628
/* Don't clear code object until the end */
628629
co = frame->f_code;
629630
frame->f_code = NULL;
@@ -633,27 +634,10 @@ frame_dealloc(PyFrameObject *f)
633634
for (int i = 0; i < frame->stacktop; i++) {
634635
Py_CLEAR(locals[i]);
635636
}
636-
PyMem_Free(frame);
637637
}
638638
Py_CLEAR(f->f_back);
639639
Py_CLEAR(f->f_trace);
640-
#if PyFrame_MAXFREELIST > 0
641-
struct _Py_frame_state *state = get_frame_state();
642-
#ifdef Py_DEBUG
643-
// frame_dealloc() must not be called after _PyFrame_Fini()
644-
assert(state->numfree != -1);
645-
#endif
646-
if (state->numfree < PyFrame_MAXFREELIST) {
647-
++state->numfree;
648-
f->f_back = state->free_list;
649-
state->free_list = f;
650-
}
651-
else
652-
#endif
653-
{
654-
PyObject_GC_Del(f);
655-
}
656-
640+
PyObject_GC_Del(f);
657641
Py_XDECREF(co);
658642
Py_TRASHCAN_END;
659643
}
@@ -663,7 +647,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
663647
{
664648
Py_VISIT(f->f_back);
665649
Py_VISIT(f->f_trace);
666-
if (f->f_own_locals_memory == 0) {
650+
if (f->f_owns_frame == 0) {
667651
return 0;
668652
}
669653
assert(f->f_frame->frame_obj == NULL);
@@ -715,11 +699,9 @@ static PyObject *
715699
frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
716700
{
717701
Py_ssize_t res;
718-
res = sizeof(PyFrameObject);
719-
if (f->f_own_locals_memory) {
720-
PyCodeObject *code = f->f_frame->f_code;
721-
res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *);
722-
}
702+
res = offsetof(PyFrameObject, _f_frame_data) + offsetof(InterpreterFrame, localsplus);
703+
PyCodeObject *code = f->f_frame->f_code;
704+
res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *);
723705
return PyLong_FromSsize_t(res);
724706
}
725707

@@ -747,7 +729,8 @@ static PyMethodDef frame_methods[] = {
747729
PyTypeObject PyFrame_Type = {
748730
PyVarObject_HEAD_INIT(&PyType_Type, 0)
749731
"frame",
750-
sizeof(PyFrameObject),
732+
offsetof(PyFrameObject, _f_frame_data) +
733+
offsetof(InterpreterFrame, localsplus),
751734
sizeof(PyObject *),
752735
(destructor)frame_dealloc, /* tp_dealloc */
753736
0, /* tp_vectorcall_offset */
@@ -781,67 +764,21 @@ PyTypeObject PyFrame_Type = {
781764

782765
_Py_IDENTIFIER(__builtins__);
783766

784-
static InterpreterFrame *
785-
allocate_heap_frame(PyFunctionObject *func, PyObject *locals)
767+
static void
768+
init_frame(InterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)
786769
{
787770
PyCodeObject *code = (PyCodeObject *)func->func_code;
788-
int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE;
789-
InterpreterFrame *frame = (InterpreterFrame *)PyMem_Malloc(sizeof(PyObject *)*size);
790-
if (frame == NULL) {
791-
PyErr_NoMemory();
792-
return NULL;
793-
}
794771
_PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);
795772
for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) {
796773
frame->localsplus[i] = NULL;
797774
}
798-
return frame;
799775
}
800776

801-
static inline PyFrameObject*
802-
frame_alloc(InterpreterFrame *frame, int owns)
803-
{
804-
PyFrameObject *f;
805-
#if PyFrame_MAXFREELIST > 0
806-
struct _Py_frame_state *state = get_frame_state();
807-
if (state->free_list == NULL)
808-
#endif
809-
{
810-
f = PyObject_GC_New(PyFrameObject, &PyFrame_Type);
811-
if (f == NULL) {
812-
if (owns) {
813-
Py_XDECREF(frame->f_code);
814-
Py_XDECREF(frame->f_builtins);
815-
Py_XDECREF(frame->f_globals);
816-
Py_XDECREF(frame->f_locals);
817-
PyMem_Free(frame);
818-
}
819-
return NULL;
820-
}
821-
}
822-
#if PyFrame_MAXFREELIST > 0
823-
else
824-
{
825-
#ifdef Py_DEBUG
826-
// frame_alloc() must not be called after _PyFrame_Fini()
827-
assert(state->numfree != -1);
828-
#endif
829-
assert(state->numfree > 0);
830-
--state->numfree;
831-
f = state->free_list;
832-
state->free_list = state->free_list->f_back;
833-
_Py_NewReference((PyObject *)f);
834-
}
835-
#endif
836-
f->f_frame = frame;
837-
f->f_own_locals_memory = owns;
838-
return f;
839-
}
840-
841-
PyFrameObject* _Py_HOT_FUNCTION
842-
_PyFrame_New_NoTrack(InterpreterFrame *frame, int owns)
777+
PyFrameObject*
778+
_PyFrame_New_NoTrack(PyCodeObject *code)
843779
{
844-
PyFrameObject *f = frame_alloc(frame, owns);
780+
int slots = code->co_nlocalsplus + code->co_stacksize;
781+
PyFrameObject *f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, slots);
845782
if (f == NULL) {
846783
return NULL;
847784
}
@@ -876,15 +813,16 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
876813
if (func == NULL) {
877814
return NULL;
878815
}
879-
InterpreterFrame *frame = allocate_heap_frame(func, locals);
880-
Py_DECREF(func);
881-
if (frame == NULL) {
816+
PyFrameObject *f = _PyFrame_New_NoTrack(code);
817+
if (f == NULL) {
818+
Py_DECREF(func);
882819
return NULL;
883820
}
884-
PyFrameObject *f = _PyFrame_New_NoTrack(frame, 1);
885-
if (f) {
886-
_PyObject_GC_TRACK(f);
887-
}
821+
init_frame((InterpreterFrame *)f->_f_frame_data, func, locals);
822+
f->f_frame = (InterpreterFrame *)f->_f_frame_data;
823+
f->f_owns_frame = 1;
824+
Py_DECREF(func);
825+
_PyObject_GC_TRACK(f);
888826
return f;
889827
}
890828

@@ -1087,42 +1025,15 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
10871025
_PyFrame_LocalsToFast(f->f_frame, clear);
10881026
}
10891027

1090-
/* Clear out the free list */
1091-
void
1092-
_PyFrame_ClearFreeList(PyInterpreterState *interp)
1093-
{
1094-
#if PyFrame_MAXFREELIST > 0
1095-
struct _Py_frame_state *state = &interp->frame;
1096-
while (state->free_list != NULL) {
1097-
PyFrameObject *f = state->free_list;
1098-
state->free_list = state->free_list->f_back;
1099-
PyObject_GC_Del(f);
1100-
--state->numfree;
1101-
}
1102-
assert(state->numfree == 0);
1103-
#endif
1104-
}
1105-
11061028
void
11071029
_PyFrame_Fini(PyInterpreterState *interp)
11081030
{
1109-
_PyFrame_ClearFreeList(interp);
1110-
#if defined(Py_DEBUG) && PyFrame_MAXFREELIST > 0
1111-
struct _Py_frame_state *state = &interp->frame;
1112-
state->numfree = -1;
1113-
#endif
11141031
}
11151032

11161033
/* Print summary info about the state of the optimized allocator */
11171034
void
11181035
_PyFrame_DebugMallocStats(FILE *out)
11191036
{
1120-
#if PyFrame_MAXFREELIST > 0
1121-
struct _Py_frame_state *state = get_frame_state();
1122-
_PyDebugAllocatorStats(out,
1123-
"free PyFrameObject",
1124-
state->numfree, sizeof(PyFrameObject));
1125-
#endif
11261037
}
11271038

11281039

0 commit comments

Comments
 (0)