Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Open
dg-pb wants to merge29 commits intopython:main
base:main
Choose a base branch
Loading
fromdg-pb:gh-119109-partial_vectorcall_kw
Open
Show file tree
Hide file tree
Changes from20 commits
Commits
Show all changes
29 commits
Select commitHold shift + click to select a range
69ba0e9
initial implementation
dg-pbSep 26, 2024
9a21b55
V2
dg-pbSep 26, 2024
f23021c
small fixes
dg-pbSep 26, 2024
d840ad7
V3
dg-pbSep 26, 2024
2dd7568
V4
dg-pbSep 27, 2024
862097f
fix compiler warnings
dg-pbSep 27, 2024
64c889b
V5 stable
dg-pbSep 29, 2024
a7142d5
add commented fix if merging after gh-124652
dg-pbSep 30, 2024
ba36d01
error check
dg-pbOct 4, 2024
acba269
fix error check
dg-pbOct 4, 2024
898a104
minor macro edit
dg-pbOct 5, 2024
10b9f3b
merge to main
dg-pbOct 17, 2024
3647c25
📜🤖 Added by blurb_it.
blurb-it[bot]Oct 17, 2024
f9e3fd4
small edits
dg-pbDec 18, 2024
42f3dc7
idiomatic C
dg-pbJan 5, 2025
4f23211
Merge remote-tracking branch 'upstream/main' into gh-119109-partial_v…
dg-pbJan 5, 2025
acd9c56
small edits and fixes
dg-pbJan 5, 2025
cc557e9
macros removed, post-resizing instead
dg-pbJan 5, 2025
e8fbaf8
regain previous performance
dg-pbJan 6, 2025
1a8a56c
small edit
dg-pbJan 6, 2025
b3ff73d
labels removed
dg-pbJan 6, 2025
00ebb4b
comment edits
dg-pbJan 6, 2025
4575b6c
minor fixes and improvements
dg-pbJan 6, 2025
25a91aa
small stack size doubled + small edits
dg-pbJan 8, 2025
e326fcf
removed commented fix when trailing placeholders allowed
dg-pbJan 9, 2025
85d658f
moved declarations to more sensible place
dg-pbJan 10, 2025
cefa7d8
reorder declarations
dg-pbJan 10, 2025
aa3a11f
Merge remote-tracking branch 'upstream/main' into gh-119109-partial_v…
dg-pbMay 8, 2025
8730ded
Merge branch 'main' into gh-119109-partial_vectorcall_kw
kumaraditya303May 17, 2025
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
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
:func:`functools.partial` calls are now faster when keyword arguments are used.
196 changes: 143 additions & 53 deletionsModules/_functoolsmodule.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -355,31 +355,20 @@ partial_descr_get(PyObject *self, PyObject *obj, PyObject *type)
return PyMethod_New(self, obj);
}

/* 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(PyObject *self, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
partialobject *pto = _PyPartialObject_CAST(self);;
PyThreadState *tstate = _PyThreadState_GET();
/* Sizes */
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;

/* 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);
}
/* Placeholder check */
Py_ssize_t pto_phcount = pto->phcount;
if (nargs < pto_phcount) {
PyErr_Format(PyExc_TypeError,
Expand All@@ -388,53 +377,140 @@ partial_vectorcall(PyObject *self, PyObject *const *args,
return NULL;
}

Py_ssize_t nargskw = nargs;
if (kwnames != NULL) {
nargskw += PyTuple_GET_SIZE(kwnames);
}

PyObject **pto_args = _PyTuple_ITEMS(pto->args);
Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args);

/* Fast path if we're called without arguments */
if (nargskw == 0) {
return _PyObject_VectorcallTstate(tstate, pto->fn,
pto_args, pto_nargs, NULL);
}
/* 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);
}

/* Fast path using 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;
// 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;
}
}

PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];
PyObject **stack;
/* 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;

Py_ssize_t tot_nargskw = pto_nargs + nargskw - pto_phcount;
if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
/* 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 {
stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *));
/* NOTE, in theory size * elsize could overflow */
stack = PyMem_Malloc(init_stack_size * sizeof(PyObject *));
if (stack == NULL) {
PyErr_NoMemory();
return NULL;
}
}

Py_ssize_t tot_nargs;
/* 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*));
}
}
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);
}

/* 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);
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) {
tot_nargs = pto_nargs + nargs - 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){
if (pto_args[i] == pto->placeholder){
stack[i] = args[j];
j += 1;
}
Expand All@@ -443,22 +519,36 @@ partial_vectorcall(PyObject *self, PyObject *const *args,
}
}
assert(j == pto_phcount);
if (nargskw > pto_phcount) {
memcpy(stack + pto_nargs, args + j, (nargskw - j) * sizeof(PyObject*));
/* Add remaining args from new_args */
if (nargs > pto_phcount) {
memcpy(stack + pto_nargs, args + j, (nargs - j) * sizeof(PyObject*));
}
}
else {
tot_nargs = pto_nargs + nargs;
/* Copy to new stack, using borrowed references */
memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*));
memcpy(stack + pto_nargs, args,nargskw * sizeof(PyObject*));
memcpy(stack + pto_nargs, args,nargs * sizeof(PyObject*));
}
PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn,
stack, tot_nargs, kwnames);

/* 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);
}
return NULL;
}

/* Set pto->vectorcall depending on the parameters of the partial object */
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp