bpo-45963: Make space for the InterpreterFrame of a generator in that generator. (GH-29891)

* Make generator, coroutine and async gen structs all the same size.

* Store interpreter frame in generator (and coroutine). Reduces the number of allocations neeeded for a generator from two to one.
This commit is contained in:
Mark Shannon 2021-12-06 10:13:49 +00:00 committed by GitHub
parent f34d181fa1
commit 299483c95d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 128 additions and 145 deletions

View file

@ -5854,8 +5854,8 @@ fail_post_args:
return -1;
}
static InterpreterFrame *
make_coro_frame(PyThreadState *tstate,
static int
initialize_coro_frame(InterpreterFrame *frame, PyThreadState *tstate,
PyFunctionObject *func, PyObject *locals,
PyObject *const *args, Py_ssize_t argcount,
PyObject *kwnames)
@ -5863,37 +5863,15 @@ make_coro_frame(PyThreadState *tstate,
assert(is_tstate_valid(tstate));
assert(func->func_defaults == NULL || PyTuple_CheckExact(func->func_defaults));
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) {
goto fail_no_memory;
}
_PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);
for (int i = 0; i < code->co_nlocalsplus; i++) {
frame->localsplus[i] = NULL;
}
assert(frame->frame_obj == NULL);
if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) {
_PyFrame_Clear(frame);
PyMem_Free(frame);
return NULL;
}
return frame;
fail_no_memory:
/* Consume the references */
for (Py_ssize_t i = 0; i < argcount; i++) {
Py_DECREF(args[i]);
}
if (kwnames) {
Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
for (Py_ssize_t i = 0; i < kwcount; i++) {
Py_DECREF(args[i+argcount]);
}
}
PyErr_NoMemory();
return NULL;
return initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames);
}
/* Consumes all the references to the args */
static PyObject *
make_coro(PyThreadState *tstate, PyFunctionObject *func,
@ -5902,14 +5880,17 @@ make_coro(PyThreadState *tstate, PyFunctionObject *func,
PyObject *kwnames)
{
assert (((PyCodeObject *)func->func_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR));
InterpreterFrame *frame = make_coro_frame(tstate, func, locals, args, argcount, kwnames);
if (frame == NULL) {
return NULL;
}
PyObject *gen = _Py_MakeCoro(func, frame);
PyObject *gen = _Py_MakeCoro(func);
if (gen == NULL) {
return NULL;
}
InterpreterFrame *frame = (InterpreterFrame *)((PyGenObject *)gen)->gi_iframe;
if (initialize_coro_frame(frame, tstate, func, locals, args, argcount, kwnames)) {
Py_DECREF(gen);
return NULL;
}
frame->generator = gen;
((PyGenObject *)gen)->gi_frame_valid = 1;
return gen;
}

View file

@ -43,18 +43,12 @@ _PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame)
return f;
}
InterpreterFrame *
_PyFrame_Copy(InterpreterFrame *frame)
void
_PyFrame_Copy(InterpreterFrame *src, InterpreterFrame *dest)
{
assert(frame->stacktop >= frame->f_code->co_nlocalsplus);
Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame;
InterpreterFrame *copy = PyMem_Malloc(size);
if (copy == NULL) {
PyErr_NoMemory();
return NULL;
}
memcpy(copy, frame, size);
return copy;
assert(src->stacktop >= src->f_code->co_nlocalsplus);
Py_ssize_t size = ((char*)&src->localsplus[src->stacktop]) - (char *)src;
memcpy(dest, src, size);
}
static inline void
@ -112,7 +106,7 @@ _PyFrame_Clear(InterpreterFrame * frame)
}
Py_DECREF(f);
}
assert(_PyFrame_GetStackPointer(frame) >= _PyFrame_Stackbase(frame));
assert(frame->stacktop >= 0);
for (int i = 0; i < frame->stacktop; i++) {
Py_XDECREF(frame->localsplus[i]);
}