diff options
author | Mark Shannon <mark@hotpy.org> | 2021-11-29 12:34:59 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-29 12:34:59 +0000 |
commit | 60929576e40038ec71d896230f69e4411c82be4b (patch) | |
tree | 34dc24d0a73ef0205514202820d66c152260dc22 | |
parent | 7431448b817d3bf87f71661cf8f3d537807ab2e2 (diff) | |
download | cpython-git-60929576e40038ec71d896230f69e4411c82be4b.tar.gz |
bpo-45786: Allocate space for frame in frame object. (GH-29729)
-rw-r--r-- | Include/cpython/frameobject.h | 6 | ||||
-rw-r--r-- | Include/internal/pycore_frame.h | 7 | ||||
-rw-r--r-- | Include/internal/pycore_gc.h | 1 | ||||
-rw-r--r-- | Include/internal/pycore_interp.h | 14 | ||||
-rw-r--r-- | Lib/test/test_exceptions.py | 6 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 7 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2021-11-23-15-25-00.bpo-45786.UdEciD.rst | 2 | ||||
-rw-r--r-- | Modules/gcmodule.c | 1 | ||||
-rw-r--r-- | Objects/frameobject.c | 141 | ||||
-rw-r--r-- | Objects/genobject.c | 22 | ||||
-rw-r--r-- | Python/ceval.c | 12 | ||||
-rw-r--r-- | Python/frame.c | 38 |
12 files changed, 76 insertions, 181 deletions
diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index e4cfac518b..67f98a7642 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -12,7 +12,9 @@ struct _frame { int f_lineno; /* Current line number. Only valid if non-zero */ char f_trace_lines; /* Emit per-line trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */ - char f_own_locals_memory; /* This frame owns the memory for the locals */ + char f_owns_frame; /* This frame owns the frame */ + /* The frame data, if this frame object owns the frame */ + PyObject *_f_frame_data[1]; }; /* Standard object interface */ @@ -26,7 +28,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, /* only internal use */ PyFrameObject* -_PyFrame_New_NoTrack(struct _interpreter_frame *, int); +_PyFrame_New_NoTrack(PyCodeObject *code); /* The rest of the interface is specific for frame objects */ diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index e36241f4a6..0015de8f8f 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -69,8 +69,7 @@ static inline void _PyFrame_StackPush(InterpreterFrame *f, PyObject *value) { #define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) -InterpreterFrame * -_PyInterpreterFrame_HeapAlloc(PyFunctionObject *func, PyObject *locals); +InterpreterFrame *_PyFrame_Copy(InterpreterFrame *frame); static inline void _PyFrame_InitializeSpecials( @@ -139,8 +138,8 @@ _PyFrame_GetFrameObject(InterpreterFrame *frame) * take should be set to 1 for heap allocated * frames like the ones in generators and coroutines. */ -int -_PyFrame_Clear(InterpreterFrame * frame, int take); +void +_PyFrame_Clear(InterpreterFrame * frame); int _PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg); diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 45e85b54c2..129d53921d 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -167,7 +167,6 @@ extern Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate); // Functions to clear types free lists -extern void _PyFrame_ClearFreeList(PyInterpreterState *interp); extern void _PyTuple_ClearFreeList(PyInterpreterState *interp); extern void _PyFloat_ClearFreeList(PyInterpreterState *interp); extern void _PyList_ClearFreeList(PyInterpreterState *interp); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 18f2143200..f52ee59410 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -93,7 +93,6 @@ struct _Py_unicode_state { # define PyTuple_MAXFREELIST 1 # define PyList_MAXFREELIST 0 # define PyDict_MAXFREELIST 0 -# define PyFrame_MAXFREELIST 0 # define _PyAsyncGen_MAXFREELIST 0 # define PyContext_MAXFREELIST 0 #endif @@ -158,18 +157,6 @@ struct _Py_dict_state { #endif }; -#ifndef PyFrame_MAXFREELIST -# define PyFrame_MAXFREELIST 200 -#endif - -struct _Py_frame_state { -#if PyFrame_MAXFREELIST > 0 - PyFrameObject *free_list; - /* number of frames currently in free_list */ - int numfree; -#endif -}; - #ifndef _PyAsyncGen_MAXFREELIST # define _PyAsyncGen_MAXFREELIST 80 #endif @@ -332,7 +319,6 @@ struct _is { struct _Py_tuple_state tuple; struct _Py_list_state list; struct _Py_dict_state dict_state; - struct _Py_frame_state frame; struct _Py_async_gen_state async_gen; struct _Py_context_state context; struct _Py_exc_state exc_state; diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 7f087d085a..c6660043c8 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -209,7 +209,7 @@ class ExceptionTests(unittest.TestCase): src = src.decode(encoding, 'replace') line = src.split('\n')[lineno-1] self.assertIn(line, cm.exception.text) - + def test_error_offset_continuation_characters(self): check = self.check check('"\\\n"(1 for c in I,\\\n\\', 2, 2) @@ -1342,9 +1342,7 @@ class ExceptionTests(unittest.TestCase): """ with SuppressCrashReport(): rc, out, err = script_helper.assert_python_failure("-c", code) - self.assertIn(b'Fatal Python error: _PyErr_NormalizeException: ' - b'Cannot recover from MemoryErrors while ' - b'normalizing exceptions.', err) + self.assertIn(b'MemoryError', err) @cpython_only def test_MemoryError(self): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b0688e1e60..db8d008208 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1320,9 +1320,10 @@ class SizeofTest(unittest.TestCase): # sys.floatinfo check(sys.float_info, vsize('') + self.P * len(sys.float_info)) # frame - import inspect - x = inspect.currentframe() - check(x, size('3Pi3c')) + def func(): + return sys._getframe() + x = func() + check(x, size('3Pi3c8P2iciP')) # function def func(): pass check(func, size('14Pi')) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-23-15-25-00.bpo-45786.UdEciD.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-23-15-25-00.bpo-45786.UdEciD.rst new file mode 100644 index 0000000000..c88f1165a0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-11-23-15-25-00.bpo-45786.UdEciD.rst @@ -0,0 +1,2 @@ +Allocate space for the interpreter frame in the frame object, to avoid an +additional allocation when the frame object outlives the frame activation. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 7d1a45bcae..b505676636 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -1038,7 +1038,6 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate, static void clear_freelists(PyInterpreterState *interp) { - _PyFrame_ClearFreeList(interp); _PyTuple_ClearFreeList(interp); _PyFloat_ClearFreeList(interp); _PyList_ClearFreeList(interp); diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 2a283b3113..926a32a510 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -610,7 +610,7 @@ static PyGetSetDef frame_getsetlist[] = { f_back next item on free list, or NULL */ -static void _Py_HOT_FUNCTION +static void frame_dealloc(PyFrameObject *f) { if (_PyObject_GC_IS_TRACKED(f)) { @@ -621,9 +621,10 @@ frame_dealloc(PyFrameObject *f) PyCodeObject *co = NULL; /* Kill all local variables including specials, if we own them */ - if (f->f_own_locals_memory) { - f->f_own_locals_memory = 0; - InterpreterFrame *frame = f->f_frame; + if (f->f_owns_frame) { + f->f_owns_frame = 0; + assert(f->f_frame == (InterpreterFrame *)f->_f_frame_data); + InterpreterFrame *frame = (InterpreterFrame *)f->_f_frame_data; /* Don't clear code object until the end */ co = frame->f_code; frame->f_code = NULL; @@ -633,27 +634,10 @@ frame_dealloc(PyFrameObject *f) for (int i = 0; i < frame->stacktop; i++) { Py_CLEAR(locals[i]); } - PyMem_Free(frame); } Py_CLEAR(f->f_back); Py_CLEAR(f->f_trace); -#if PyFrame_MAXFREELIST > 0 - struct _Py_frame_state *state = get_frame_state(); -#ifdef Py_DEBUG - // frame_dealloc() must not be called after _PyFrame_Fini() - assert(state->numfree != -1); -#endif - if (state->numfree < PyFrame_MAXFREELIST) { - ++state->numfree; - f->f_back = state->free_list; - state->free_list = f; - } - else -#endif - { - PyObject_GC_Del(f); - } - + PyObject_GC_Del(f); Py_XDECREF(co); Py_TRASHCAN_END; } @@ -663,7 +647,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { Py_VISIT(f->f_back); Py_VISIT(f->f_trace); - if (f->f_own_locals_memory == 0) { + if (f->f_owns_frame == 0) { return 0; } assert(f->f_frame->frame_obj == NULL); @@ -715,11 +699,9 @@ static PyObject * frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) { Py_ssize_t res; - res = sizeof(PyFrameObject); - if (f->f_own_locals_memory) { - PyCodeObject *code = f->f_frame->f_code; - res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); - } + res = offsetof(PyFrameObject, _f_frame_data) + offsetof(InterpreterFrame, localsplus); + PyCodeObject *code = f->f_frame->f_code; + res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); return PyLong_FromSsize_t(res); } @@ -747,7 +729,8 @@ static PyMethodDef frame_methods[] = { PyTypeObject PyFrame_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "frame", - sizeof(PyFrameObject), + offsetof(PyFrameObject, _f_frame_data) + + offsetof(InterpreterFrame, localsplus), sizeof(PyObject *), (destructor)frame_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ @@ -781,67 +764,21 @@ PyTypeObject PyFrame_Type = { _Py_IDENTIFIER(__builtins__); -static InterpreterFrame * -allocate_heap_frame(PyFunctionObject *func, PyObject *locals) +static void +init_frame(InterpreterFrame *frame, PyFunctionObject *func, PyObject *locals) { PyCodeObject *code = (PyCodeObject *)func->func_code; - int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE; - InterpreterFrame *frame = (InterpreterFrame *)PyMem_Malloc(sizeof(PyObject *)*size); - if (frame == NULL) { - PyErr_NoMemory(); - return NULL; - } _PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus); for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) { frame->localsplus[i] = NULL; } - return frame; } -static inline PyFrameObject* -frame_alloc(InterpreterFrame *frame, int owns) -{ - PyFrameObject *f; -#if PyFrame_MAXFREELIST > 0 - struct _Py_frame_state *state = get_frame_state(); - if (state->free_list == NULL) -#endif - { - f = PyObject_GC_New(PyFrameObject, &PyFrame_Type); - if (f == NULL) { - if (owns) { - Py_XDECREF(frame->f_code); - Py_XDECREF(frame->f_builtins); - Py_XDECREF(frame->f_globals); - Py_XDECREF(frame->f_locals); - PyMem_Free(frame); - } - return NULL; - } - } -#if PyFrame_MAXFREELIST > 0 - else - { -#ifdef Py_DEBUG - // frame_alloc() must not be called after _PyFrame_Fini() - assert(state->numfree != -1); -#endif - assert(state->numfree > 0); - --state->numfree; - f = state->free_list; - state->free_list = state->free_list->f_back; - _Py_NewReference((PyObject *)f); - } -#endif - f->f_frame = frame; - f->f_own_locals_memory = owns; - return f; -} - -PyFrameObject* _Py_HOT_FUNCTION -_PyFrame_New_NoTrack(InterpreterFrame *frame, int owns) +PyFrameObject* +_PyFrame_New_NoTrack(PyCodeObject *code) { - PyFrameObject *f = frame_alloc(frame, owns); + int slots = code->co_nlocalsplus + code->co_stacksize; + PyFrameObject *f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, slots); if (f == NULL) { return NULL; } @@ -876,15 +813,16 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, if (func == NULL) { return NULL; } - InterpreterFrame *frame = allocate_heap_frame(func, locals); - Py_DECREF(func); - if (frame == NULL) { + PyFrameObject *f = _PyFrame_New_NoTrack(code); + if (f == NULL) { + Py_DECREF(func); return NULL; } - PyFrameObject *f = _PyFrame_New_NoTrack(frame, 1); - if (f) { - _PyObject_GC_TRACK(f); - } + init_frame((InterpreterFrame *)f->_f_frame_data, func, locals); + f->f_frame = (InterpreterFrame *)f->_f_frame_data; + f->f_owns_frame = 1; + Py_DECREF(func); + _PyObject_GC_TRACK(f); return f; } @@ -1087,42 +1025,15 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) _PyFrame_LocalsToFast(f->f_frame, clear); } -/* Clear out the free list */ -void -_PyFrame_ClearFreeList(PyInterpreterState *interp) -{ -#if PyFrame_MAXFREELIST > 0 - struct _Py_frame_state *state = &interp->frame; - while (state->free_list != NULL) { - PyFrameObject *f = state->free_list; - state->free_list = state->free_list->f_back; - PyObject_GC_Del(f); - --state->numfree; - } - assert(state->numfree == 0); -#endif -} - void _PyFrame_Fini(PyInterpreterState *interp) { - _PyFrame_ClearFreeList(interp); -#if defined(Py_DEBUG) && PyFrame_MAXFREELIST > 0 - struct _Py_frame_state *state = &interp->frame; - state->numfree = -1; -#endif } /* Print summary info about the state of the optimized allocator */ void _PyFrame_DebugMallocStats(FILE *out) { -#if PyFrame_MAXFREELIST > 0 - struct _Py_frame_state *state = get_frame_state(); - _PyDebugAllocatorStats(out, - "free PyFrameObject", - state->numfree, sizeof(PyFrameObject)); -#endif } diff --git a/Objects/genobject.c b/Objects/genobject.c index 24d5f3579c..04d98a2b4e 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -38,7 +38,7 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg) Py_VISIT(gen->gi_qualname); InterpreterFrame *frame = gen->gi_xframe; if (frame != NULL) { - assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); + assert(frame->frame_obj == NULL || frame->frame_obj->f_owns_frame == 0); int err = _PyFrame_Traverse(frame, visit, arg); if (err) { return err; @@ -136,7 +136,8 @@ gen_dealloc(PyGenObject *gen) gen->gi_xframe = NULL; frame->generator = NULL; frame->previous = NULL; - _PyFrame_Clear(frame, 1); + _PyFrame_Clear(frame); + PyMem_Free(frame); } if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin); @@ -254,7 +255,8 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, frame->generator = NULL; gen->gi_xframe = NULL; - _PyFrame_Clear(frame, 1); + _PyFrame_Clear(frame); + PyMem_Free(frame); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } @@ -845,7 +847,8 @@ make_gen(PyTypeObject *type, PyFunctionObject *func, InterpreterFrame *frame) PyGenObject *gen = PyObject_GC_New(PyGenObject, type); if (gen == NULL) { assert(frame->frame_obj == NULL); - _PyFrame_Clear(frame, 1); + _PyFrame_Clear(frame); + PyMem_Free(frame); return NULL; } gen->gi_xframe = frame; @@ -929,10 +932,15 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, /* Take ownership of the frame */ assert(f->f_frame->frame_obj == NULL); - assert(f->f_own_locals_memory); - gen->gi_xframe = f->f_frame; + assert(f->f_owns_frame); + gen->gi_xframe = _PyFrame_Copy((InterpreterFrame *)f->_f_frame_data); + if (gen->gi_xframe == NULL) { + Py_DECREF(f); + Py_DECREF(gen); + return NULL; + } gen->gi_xframe->frame_obj = f; - f->f_own_locals_memory = 0; + f->f_owns_frame = 0; gen->gi_xframe->generator = (PyObject *) gen; assert(PyObject_GC_IsTracked((PyObject *)f)); diff --git a/Python/ceval.c b/Python/ceval.c index 9beb1a4368..0427361a03 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5694,7 +5694,8 @@ make_coro_frame(PyThreadState *tstate, } assert(frame->frame_obj == NULL); if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) { - _PyFrame_Clear(frame, 1); + _PyFrame_Clear(frame); + PyMem_Free(frame); return NULL; } return frame; @@ -5750,7 +5751,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, localsarray[i] = NULL; } if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) { - _PyFrame_Clear(frame, 0); + _PyFrame_Clear(frame); return NULL; } return frame; @@ -5773,11 +5774,8 @@ static int _PyEvalFrameClearAndPop(PyThreadState *tstate, InterpreterFrame * frame) { --tstate->recursion_remaining; - assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0); - if (_PyFrame_Clear(frame, 0)) { - ++tstate->recursion_remaining; - return -1; - } + assert(frame->frame_obj == NULL || frame->frame_obj->f_owns_frame == 0); + _PyFrame_Clear(frame); ++tstate->recursion_remaining; _PyThreadState_PopFrame(tstate, frame); return 0; diff --git a/Python/frame.c b/Python/frame.c index 79b0f77a06..21f2ced959 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -27,22 +27,24 @@ _PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame) assert(frame->frame_obj == NULL); PyObject *error_type, *error_value, *error_traceback; PyErr_Fetch(&error_type, &error_value, &error_traceback); - PyFrameObject *f = _PyFrame_New_NoTrack(frame, 0); + + PyFrameObject *f = _PyFrame_New_NoTrack(frame->f_code); if (f == NULL) { Py_XDECREF(error_type); Py_XDECREF(error_value); Py_XDECREF(error_traceback); } else { + f->f_owns_frame = 0; + f->f_frame = frame; + frame->frame_obj = f; PyErr_Restore(error_type, error_value, error_traceback); } - frame->frame_obj = f; return f; } - -static InterpreterFrame * -copy_frame_to_heap(InterpreterFrame *frame) +InterpreterFrame * +_PyFrame_Copy(InterpreterFrame *frame) { assert(frame->stacktop >= frame->f_code->co_nlocalsplus); Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame; @@ -68,10 +70,11 @@ clear_specials(InterpreterFrame *frame) static void take_ownership(PyFrameObject *f, InterpreterFrame *frame) { - assert(f->f_own_locals_memory == 0); - assert(frame->frame_obj == NULL); - - f->f_own_locals_memory = 1; + assert(f->f_owns_frame == 0); + Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame; + memcpy((InterpreterFrame *)f->_f_frame_data, frame, size); + frame = (InterpreterFrame *)f->_f_frame_data; + f->f_owns_frame = 1; f->f_frame = frame; assert(f->f_back == NULL); if (frame->previous != NULL) { @@ -82,7 +85,6 @@ take_ownership(PyFrameObject *f, InterpreterFrame *frame) assert(PyErr_ExceptionMatches(PyExc_MemoryError)); /* Nothing we can do about it */ PyErr_Clear(); - _PyErr_WriteUnraisableMsg("Out of memory lazily allocating frame->f_back", NULL); } else { f->f_back = (PyFrameObject *)Py_NewRef(back); @@ -94,8 +96,8 @@ take_ownership(PyFrameObject *f, InterpreterFrame *frame) } } -int -_PyFrame_Clear(InterpreterFrame * frame, int take) +void +_PyFrame_Clear(InterpreterFrame * frame) { /* It is the responsibility of the owning generator/coroutine * to have cleared the generator pointer */ @@ -104,15 +106,9 @@ _PyFrame_Clear(InterpreterFrame * frame, int take) PyFrameObject *f = frame->frame_obj; frame->frame_obj = NULL; if (Py_REFCNT(f) > 1) { - if (!take) { - frame = copy_frame_to_heap(frame); - if (frame == NULL) { - return -1; - } - } take_ownership(f, frame); Py_DECREF(f); - return 0; + return; } Py_DECREF(f); } @@ -121,8 +117,4 @@ _PyFrame_Clear(InterpreterFrame * frame, int take) Py_XDECREF(frame->localsplus[i]); } clear_specials(frame); - if (take) { - PyMem_Free(frame); - } - return 0; } |