Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32k
gh-119109: improvefunctools.partial
vectorcall with keywords#124584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Changes from20 commits
69ba0e9
9a21b55
f23021c
d840ad7
2dd7568
862097f
64c889b
a7142d5
ba36d01
acba269
898a104
10b9f3b
3647c25
f9e3fd4
42f3dc7
4f23211
acd9c56
cc557e9
e8fbaf8
1a8a56c
b3ff73d
00ebb4b
4575b6c
25a91aa
e326fcf
85d658f
cefa7d8
aa3a11f
8730ded
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
:func:`functools.partial` calls are now faster when keyword arguments are used. | ||
dg-pb marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -355,31 +355,20 @@ partial_descr_get(PyObject *self, PyObject *obj, PyObject *type) | ||
return PyMethod_New(self, obj); | ||
} | ||
static PyObject * | ||
partial_vectorcall(PyObject *self, PyObject *const *args, | ||
size_t nargsf, PyObject *kwnames) | ||
{ | ||
partialobject *pto = _PyPartialObject_CAST(self);; | ||
PyThreadState *tstate = _PyThreadState_GET(); | ||
/* Sizes */ | ||
dg-pb marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); | ||
Py_ssize_t pto_nkwds = PyDict_GET_SIZE(pto->kw); | ||
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); | ||
Py_ssize_t nkwds = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); | ||
Py_ssize_t nargskw = nargs + nkwds; | ||
/* Placeholder check */ | ||
Py_ssize_t pto_phcount = pto->phcount; | ||
if (nargs < pto_phcount) { | ||
PyErr_Format(PyExc_TypeError, | ||
@@ -388,53 +377,140 @@ partial_vectorcall(PyObject *self, PyObject *const *args, | ||
return NULL; | ||
} | ||
PyObject **pto_args = _PyTuple_ITEMS(pto->args); | ||
/* Special cases */ | ||
if (!pto_nkwds) { | ||
/* Fast path if we're called without arguments */ | ||
if (nargskw == 0) { | ||
return _PyObject_VectorcallTstate(tstate, pto->fn, pto_args, | ||
pto_nargs, NULL); | ||
} | ||
// TO BE ADDED ON MERGE TO MAIN | ||
// IF https://github.com/python/cpython/pull/124788 | ||
// IS MERGED BEFORE THIS | ||
// /* Fast path if all Placeholders */ | ||
// if (pto_nargs == pto_phcount) { | ||
// /* NOTE: Without this, following single argument Fast Path | ||
// * is incorrect */ | ||
// return _PyObject_VectorcallTstate(tstate, pto->fn, | ||
// args, nargs, kwnames); | ||
// } | ||
/* Use PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single | ||
* positional argument. */ | ||
if (pto_nargs == 1 && (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) { | ||
PyObject **newargs = (PyObject **)args - 1; | ||
PyObject *tmp = newargs[0]; | ||
newargs[0] = pto_args[0]; | ||
PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, newargs, | ||
nargs + 1, kwnames); | ||
newargs[0] = tmp; | ||
return ret; | ||
} | ||
} | ||
/* Total sizes */ | ||
Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; | ||
Py_ssize_t tot_nkwds = pto_nkwds + nkwds; | ||
Py_ssize_t tot_nargskw = tot_nargs + tot_nkwds; | ||
PyObject *pto_kw_merged = NULL; // pto_kw with duplicates merged (if any) | ||
PyObject *tot_kwnames; | ||
/* Allocate Stack */ | ||
PyObject **tmp_stack, **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; | ||
Py_ssize_t init_stack_size = tot_nargskw; | ||
if (pto_nkwds) { | ||
// If pto_nkwds, allocate additional space for temporary new keys | ||
init_stack_size += nkwds; | ||
} | ||
if (init_stack_size <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { | ||
stack = small_stack; | ||
} | ||
else { | ||
/* NOTE, in theory size * elsize could overflow */ | ||
stack = PyMem_Malloc(init_stack_size * sizeof(PyObject *)); | ||
if (stack == NULL) { | ||
PyErr_NoMemory(); | ||
return NULL; | ||
} | ||
} | ||
/* Copy keywords to stack */ | ||
if (!pto_nkwds) { | ||
tot_kwnames = kwnames; | ||
if (nkwds) { | ||
/* if !pto_nkwds & nkwds, then simply append kw */ | ||
memcpy(stack + tot_nargs, args + nargs, nkwds * sizeof(PyObject*)); | ||
dg-pb marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
} | ||
} | ||
else { | ||
/* stack is now [<positionals>, <pto_kwds>, <kwds>, <kwds_keys>] | ||
* Will truncate to [<positionals>, <merged_kwds>] */ | ||
PyObject *key, *val; | ||
/* Merge kw to pto_kw or add to tail (if not duplicate) */ | ||
Py_ssize_t n_tail = 0; | ||
for (Py_ssize_t i = 0; i < nkwds; ++i) { | ||
key = PyTuple_GET_ITEM(kwnames, i); | ||
val = args[nargs + i]; | ||
if (PyDict_Contains(pto->kw, key)) { | ||
if (pto_kw_merged == NULL) { | ||
pto_kw_merged = PyDict_Copy(pto->kw); | ||
if (pto_kw_merged == NULL) { | ||
goto error_2; | ||
} | ||
} | ||
if (PyDict_SetItem(pto_kw_merged, key, val) < 0) { | ||
goto error_1; | ||
} | ||
} | ||
else { | ||
/* Copy keyword tail to stack */ | ||
stack[tot_nargs + pto_nkwds + n_tail] = val; | ||
stack[tot_nargskw + n_tail] = key; | ||
n_tail++; | ||
} | ||
} | ||
Py_ssize_t n_merges = nkwds - n_tail; | ||
/* Create tot_kwnames */ | ||
tot_kwnames = PyTuple_New(pto_nkwds + n_tail); | ||
for (Py_ssize_t i = 0; i < n_tail; ++i) { | ||
key = stack[tot_nargskw + i]; | ||
PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); | ||
Py_INCREF(key); | ||
dg-pb marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
} | ||
/* Copy pto_kw_merged to stack */ | ||
Py_ssize_t pos = 0, i = 0; | ||
while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { | ||
PyTuple_SET_ITEM(tot_kwnames, i, key); | ||
Py_INCREF(key); | ||
dg-pb marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
stack[tot_nargs + i] = val; | ||
i++; | ||
} | ||
/* Resize Stack */ | ||
if (n_merges && stack != small_stack) { | ||
tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); | ||
if (tmp_stack == NULL) { | ||
PyErr_NoMemory(); | ||
goto error_0; | ||
} | ||
stack = tmp_stack; | ||
} | ||
Py_XDECREF(pto_kw_merged); | ||
} | ||
/* Copy Positionals to stack */ | ||
if (pto_phcount) { | ||
Py_ssize_t j = 0; // New args index | ||
for (Py_ssize_t i = 0; i < pto_nargs; i++) { | ||
if (pto_args[i] == pto->placeholder){ | ||
stack[i] = args[j]; | ||
j += 1; | ||
} | ||
@@ -443,22 +519,36 @@ partial_vectorcall(PyObject *self, PyObject *const *args, | ||
} | ||
} | ||
assert(j == pto_phcount); | ||
/* Add remaining args from new_args */ | ||
if (nargs > pto_phcount) { | ||
memcpy(stack + pto_nargs, args + j, (nargs - j) * sizeof(PyObject*)); | ||
dg-pb marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
} | ||
} | ||
else { | ||
memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); | ||
memcpy(stack + pto_nargs, args,nargs * sizeof(PyObject*)); | ||
dg-pb marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
} | ||
/* Call / Maintenance / Return */ | ||
PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, | ||
tot_nargs, tot_kwnames); | ||
if (stack != small_stack) { | ||
PyMem_Free(stack); | ||
} | ||
if (pto_nkwds) { | ||
Py_DECREF(tot_kwnames); | ||
} | ||
return ret; | ||
error_0: | ||
Py_DECREF(tot_kwnames); | ||
error_1: | ||
Py_XDECREF(pto_kw_merged); | ||
error_2: | ||
if (stack != small_stack) { | ||
PyMem_Free(stack); | ||
} | ||
erlend-aasland marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
return NULL; | ||
} | ||
/* Set pto->vectorcall depending on the parameters of the partial object */ | ||
Uh oh!
There was an error while loading.Please reload this page.