From bb2ab1b5c883a47aa51fe0537f78d2d016596be9 Mon Sep 17 00:00:00 2001 From: Vizonex Date: Tue, 20 Jan 2026 12:23:57 -0600 Subject: [PATCH 1/2] optimize context.run by making custom C Functions for it --- uvloop/includes/compat.h | 128 +++++++++++++++++++++++++++++++++++++ uvloop/includes/python.pxd | 4 ++ uvloop/loop.pyx | 11 ++-- 3 files changed, 139 insertions(+), 4 deletions(-) diff --git a/uvloop/includes/compat.h b/uvloop/includes/compat.h index 0c408c9e..9783a252 100644 --- a/uvloop/includes/compat.h +++ b/uvloop/includes/compat.h @@ -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 diff --git a/uvloop/includes/python.pxd b/uvloop/includes/python.pxd index 94007e53..b5475dd6 100644 --- a/uvloop/includes/python.pxd +++ b/uvloop/includes/python.pxd @@ -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() diff --git a/uvloop/loop.pyx b/uvloop/loop.pyx index 8dc96417..0f0c1c97 100644 --- a/uvloop/loop.pyx +++ b/uvloop/loop.pyx @@ -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 @@ -98,7 +101,7 @@ 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) @@ -106,7 +109,7 @@ cdef inline run_in_context(context, 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) @@ -114,7 +117,7 @@ cdef inline run_in_context1(context, method, arg): 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) From 36f8aa1e898ec4a86db3a01f0712abf8f08cc47d Mon Sep 17 00:00:00 2001 From: Vizonex Date: Tue, 20 Jan 2026 12:36:26 -0600 Subject: [PATCH 2/2] fix comma --- uvloop/loop.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvloop/loop.pyx b/uvloop/loop.pyx index 0f0c1c97..28ca7d0c 100644 --- a/uvloop/loop.pyx +++ b/uvloop/loop.pyx @@ -18,7 +18,7 @@ from .includes.python cimport ( Context_Exit, Context_RunNoArgs, Context_RunOneArg, - Context_RunTwoArgs + Context_RunTwoArgs, PyMemoryView_FromMemory, PyBUF_WRITE, PyMemoryView_FromObject, PyMemoryView_Check, PyOS_AfterFork_Parent, PyOS_AfterFork_Child,