Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions uvloop/includes/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,134 @@ int Context_Exit(PyObject *ctx) {

#endif

/* Originally apart of loop.pyx via calling context.run
moved here so we can began optimizing more
reason why something like this is more costly is because
we have to find the .run method if these were C Functions instead
This Call would no longer be needed and we skip right to the
meat of the function (run) immediately however, we can go further
to optimize that code too.

Before:
cdef inline run_in_context1(context, method, arg):
Py_INCREF(method)
try:
return context.run(method, arg)
finally:
Py_DECREF(method)

After:
cdef inline run_in_context1(context, method, arg):
Py_INCREF(method)
try:
return Context_RunNoArgs(context, method)
finally:
Py_DECREF(method)
*/

/* Context.run is literally this code right here referenced from python 3.15.1

static PyObject *
context_run(PyObject *self, PyObject *const *args,
Py_ssize_t nargs, PyObject *kwnames)
{
PyThreadState *ts = _PyThreadState_GET();

if (nargs < 1) {
_PyErr_SetString(ts, PyExc_TypeError,
"run() missing 1 required positional argument");
return NULL;
}

if (_PyContext_Enter(ts, self)) {
return NULL;
}

PyObject *call_result = _PyObject_VectorcallTstate(
ts, args[0], args + 1, nargs - 1, kwnames);

if (_PyContext_Exit(ts, self)) {
Py_XDECREF(call_result);
return NULL;
}

return call_result;
}

As we can see this code is not very expensive to maintain
at all and can be simply reproduced and improved upon
for our needs of being faster.

Will name them after the different object calls made
to keep things less confusing.
We also eliminate needing to find the run method by doing so.

These changes reflect recent addtions made to winloop.
SEE: https://github.com/Vizonex/Winloop/pull/112
*/

static PyObject* Context_RunNoArgs(PyObject* context, PyObject* method){
/* NOTE: Were looking for -1 but we can also say it's not
a no-zero value so we could even treat it as a true case... */
if (Context_Enter(context)){
return NULL;
}

#if PY_VERSION_HEX >= 0x030a0000
PyObject* call_result = PyObject_CallNoArgs(method);
#else
PyObject* call_result = PyObject_CallFunctionObjArgs(method, NULL);
#endif

if (Context_Exit(context)){
Py_XDECREF(call_result);
return NULL;
}

return call_result;
}

static PyObject* Context_RunOneArg(PyObject* context, PyObject* method, PyObject* arg){
if (Context_Enter(context)){
return NULL;
}
/* Introduced in 3.9 */
/* NOTE: Kept around backwards compatability since the same features are planned for uvloop */
#if PY_VERSION_HEX >= 0x03090000
PyObject* call_result = PyObject_CallOneArg(method, arg);
#else /* verison < 3.9 */
PyObject* call_result = PyObject_CallFunctionObjArgs(method, arg, NULL);
#endif
if (Context_Exit(context)){
Py_XDECREF(call_result);
return NULL;
}
return call_result;
}

static PyObject* Context_RunTwoArgs(PyObject* context, PyObject* method, PyObject* arg1, PyObject* arg2){
/* Cython can't really do this PyObject array packing so writing this in C
has a really good advantage */
if (Context_Enter(context)){
return NULL;
}
#if PY_VERSION_HEX >= 0x03090000
/* begin packing for call... */
PyObject* args[2];
args[0] = arg1;
args[1] = arg2;
PyObject* call_result = PyObject_Vectorcall(method, args, 2, NULL);
#else
PyObject* call_result = PyObject_CallFunctionObjArgs(method, arg1, arg2, NULL);
#endif
if (Context_Exit(context)){
Py_XDECREF(call_result);
return NULL;
}
return call_result;
}


/* inlined from cpython/Modules/signalmodule.c
* https://github.com/python/cpython/blob/v3.13.0a6/Modules/signalmodule.c#L1931-L1951
* private _Py_RestoreSignals has been moved to CPython internals in Python 3.13
Expand Down
4 changes: 4 additions & 0 deletions uvloop/includes/python.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ cdef extern from "includes/compat.h":
object Context_CopyCurrent()
int Context_Enter(object) except -1
int Context_Exit(object) except -1

object Context_RunNoArgs(object context, object method)
object Context_RunOneArg(object context, object method, object arg)
object Context_RunTwoArgs(object context, object method, object arg1, object arg2)

void PyOS_BeforeFork()
void PyOS_AfterFork_Parent()
Expand Down
11 changes: 7 additions & 4 deletions uvloop/loop.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ from .includes.python cimport (
Context_CopyCurrent,
Context_Enter,
Context_Exit,
Context_RunNoArgs,
Context_RunOneArg,
Context_RunTwoArgs,
PyMemoryView_FromMemory, PyBUF_WRITE,
PyMemoryView_FromObject, PyMemoryView_Check,
PyOS_AfterFork_Parent, PyOS_AfterFork_Child,
PyOS_BeforeFork,
PyUnicode_FromString
PyUnicode_FromString,
)
from .includes.flowcontrol cimport add_flowcontrol_defaults

Expand Down Expand Up @@ -98,23 +101,23 @@ cdef inline run_in_context(context, method):
# See also: edgedb/edgedb#2222
Py_INCREF(method)
try:
return context.run(method)
return Context_RunNoArgs(context, method)
finally:
Py_DECREF(method)


cdef inline run_in_context1(context, method, arg):
Py_INCREF(method)
try:
return context.run(method, arg)
return Context_RunOneArg(context, method, arg)
finally:
Py_DECREF(method)


cdef inline run_in_context2(context, method, arg1, arg2):
Py_INCREF(method)
try:
return context.run(method, arg1, arg2)
return Context_RunTwoArgs(context, method, arg1, arg2)
finally:
Py_DECREF(method)

Expand Down
Loading