mirror of
https://github.com/python/cpython.git
synced 2025-07-16 15:55:18 +00:00
bpo-41756: Introduce PyGen_Send C API (GH-22196)
The new API allows to efficiently send values into native generators and coroutines avoiding use of StopIteration exceptions to signal returns. ceval loop now uses this method instead of the old "private" _PyGen_Send C API. This translates to 1.6x increased performance of 'await' calls in micro-benchmarks. Aside from CPython core improvements, this new API will also allow Cython to generate more efficient code, benefiting high-performance IO libraries like uvloop.
This commit is contained in:
parent
ec8a15b034
commit
2b05361bf7
7 changed files with 148 additions and 39 deletions
|
@ -2223,29 +2223,53 @@ main_loop:
|
|||
case TARGET(YIELD_FROM): {
|
||||
PyObject *v = POP();
|
||||
PyObject *receiver = TOP();
|
||||
int err;
|
||||
if (PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver)) {
|
||||
retval = _PyGen_Send((PyGenObject *)receiver, v);
|
||||
int is_gen_or_coro = PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver);
|
||||
int gen_status;
|
||||
if (tstate->c_tracefunc == NULL && is_gen_or_coro) {
|
||||
gen_status = PyGen_Send((PyGenObject *)receiver, v, &retval);
|
||||
} else {
|
||||
_Py_IDENTIFIER(send);
|
||||
if (v == Py_None)
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
else
|
||||
retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v);
|
||||
if (is_gen_or_coro) {
|
||||
retval = _PyGen_Send((PyGenObject *)receiver, v);
|
||||
}
|
||||
else {
|
||||
_Py_IDENTIFIER(send);
|
||||
if (v == Py_None) {
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
}
|
||||
else {
|
||||
retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v);
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == NULL) {
|
||||
if (tstate->c_tracefunc != NULL
|
||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
|
||||
if (_PyGen_FetchStopIterationValue(&retval) == 0) {
|
||||
gen_status = PYGEN_RETURN;
|
||||
}
|
||||
else {
|
||||
gen_status = PYGEN_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
gen_status = PYGEN_NEXT;
|
||||
}
|
||||
}
|
||||
Py_DECREF(v);
|
||||
if (retval == NULL) {
|
||||
PyObject *val;
|
||||
if (tstate->c_tracefunc != NULL
|
||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
|
||||
err = _PyGen_FetchStopIterationValue(&val);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (gen_status == PYGEN_ERROR) {
|
||||
assert (retval == NULL);
|
||||
goto error;
|
||||
}
|
||||
if (gen_status == PYGEN_RETURN) {
|
||||
assert (retval != NULL);
|
||||
|
||||
Py_DECREF(receiver);
|
||||
SET_TOP(val);
|
||||
SET_TOP(retval);
|
||||
retval = NULL;
|
||||
DISPATCH();
|
||||
}
|
||||
assert (gen_status == PYGEN_NEXT);
|
||||
/* receiver remains on stack, retval is value to be yielded */
|
||||
/* and repeat... */
|
||||
assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue