From 4822017ddfb81635b2a3c978225a67de910e27e8 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 17 May 2024 18:45:52 +0300 Subject: [PATCH 1/4] functools.partial.fallforward --- Modules/_functoolsmodule.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 9dee7bf3062710..f02e7d93649578 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -46,9 +46,10 @@ typedef struct { PyObject *fn; PyObject *args; PyObject *kw; - PyObject *dict; /* __dict__ */ - PyObject *weakreflist; /* List of weak references */ + PyObject *dict; /* __dict__ */ + PyObject *weakreflist; /* List of weak references */ vectorcallfunc vectorcall; + Py_ssize_t can_vcall; /* Cache whether function allows vector call */ } partialobject; static void partial_setvectorcall(partialobject *pto); @@ -198,32 +199,22 @@ partial_dealloc(partialobject *pto) } -/* Merging keyword arguments using the vectorcall convention is messy, so - * if we would need to do that, we stop using vectorcall and fall back - * to using partial_call() instead. */ -Py_NO_INLINE static PyObject * -partial_vectorcall_fallback(PyThreadState *tstate, partialobject *pto, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) -{ - pto->vectorcall = NULL; - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - return _PyObject_MakeTpCall(tstate, (PyObject *)pto, - args, nargs, kwnames); -} - static PyObject * partial_vectorcall(partialobject *pto, PyObject *const *args, size_t nargsf, PyObject *kwnames) { PyThreadState *tstate = _PyThreadState_GET(); - + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); /* pto->kw is mutable, so need to check every time */ if (PyDict_GET_SIZE(pto->kw)) { - return partial_vectorcall_fallback(tstate, pto, args, nargsf, kwnames); + /* Merging keyword arguments using the vectorcall convention is messy, so + * if we would need to do that, we stop using vectorcall and fall back + * to using partial_call() instead. */ + pto->vectorcall = NULL; + return _PyObject_MakeTpCall(tstate, (PyObject *)pto, args, nargs, kwnames); + // return partial_vectorcall_fallback(tstate, pto, args, nargsf, kwnames); } - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); Py_ssize_t nargs_total = nargs; if (kwnames != NULL) { nargs_total += PyTuple_GET_SIZE(kwnames); @@ -286,12 +277,14 @@ partial_setvectorcall(partialobject *pto) if (PyVectorcall_Function(pto->fn) == NULL) { /* Don't use vectorcall if the underlying function doesn't support it */ pto->vectorcall = NULL; + pto->can_vcall = 0; } /* We could have a special case if there are no arguments, * but that is unlikely (why use partial without arguments?), * so we don't optimize that */ else { pto->vectorcall = (vectorcallfunc)partial_vectorcall; + pto->can_vcall = 1; } } From 13e97978f2d12351d84c857a7c6f4405bcf3c60f Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 17 May 2024 19:51:12 +0300 Subject: [PATCH 2/4] second part of the code.. --- Modules/_functoolsmodule.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index f02e7d93649578..f1f925738fafde 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -212,7 +212,6 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, * to using partial_call() instead. */ pto->vectorcall = NULL; return _PyObject_MakeTpCall(tstate, (PyObject *)pto, args, nargs, kwnames); - // return partial_vectorcall_fallback(tstate, pto, args, nargsf, kwnames); } Py_ssize_t nargs_total = nargs; @@ -297,9 +296,17 @@ partial_call(partialobject *pto, PyObject *args, PyObject *kwargs) assert(PyTuple_Check(pto->args)); assert(PyDict_Check(pto->kw)); + /* pto->kw is mutable, so need to check every time + * if kwds are empty to switch to more efficient vectorcall */ + Py_ssize_t pto_nkwargs = PyDict_GET_SIZE(pto->kw); + if ((pto_nkwargs == 0) & pto->can_vcall){ + pto->vectorcall = (vectorcallfunc)partial_vectorcall; + return PyObject_Call((PyObject *)pto, args, kwargs); + } + /* Merge keywords */ PyObject *kwargs2; - if (PyDict_GET_SIZE(pto->kw) == 0) { + if (pto_nkwargs == 0) { /* kwargs can be NULL */ kwargs2 = Py_XNewRef(kwargs); } From 652f50a6290d0513ef2711878b7ef0b60d90a36a Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sat, 18 May 2024 15:25:15 +0300 Subject: [PATCH 3/4] unnecessary stylistic change --- Modules/_functoolsmodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index f1f925738fafde..88d3b6eb963927 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -46,10 +46,10 @@ typedef struct { PyObject *fn; PyObject *args; PyObject *kw; - PyObject *dict; /* __dict__ */ - PyObject *weakreflist; /* List of weak references */ + PyObject *dict; /* __dict__ */ + PyObject *weakreflist; /* List of weak references */ vectorcallfunc vectorcall; - Py_ssize_t can_vcall; /* Cache whether function allows vector call */ + Py_ssize_t can_vcall; /* Cache whether function allows vector call */ } partialobject; static void partial_setvectorcall(partialobject *pto); From 64889f70a214c054832801018f099f996d15b957 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 20 May 2024 16:23:23 +0300 Subject: [PATCH 4/4] int type for bool --- Modules/_functoolsmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 88d3b6eb963927..a8f3019a812748 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -49,7 +49,7 @@ typedef struct { PyObject *dict; /* __dict__ */ PyObject *weakreflist; /* List of weak references */ vectorcallfunc vectorcall; - Py_ssize_t can_vcall; /* Cache whether function allows vector call */ + int can_vcall; /* Cache whether function allows vector call */ } partialobject; static void partial_setvectorcall(partialobject *pto);