| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Since Python 3.9 a call with a lone ** unpacking compiled to BUILD_MAP 0 + DICT_MERGE 1 + CALL_FUNCTION_EX, copying the kwargs dict on every call. For vectorcall callees (all Python functions and most builtins) that copy is wasted work: the dict is immediately unpacked onto a flat argument vector. The compiler now pushes the ** operand as-is. CALL_FUNCTION_EX converts a non-exact mapping to a dict itself (reusing the DICT_MERGE machinery, so error messages are unchanged), and PyObject_Call copies the dict only on the tp_call path, where the callee would otherwise receive the caller's dict directly -- so a callee still can never mutate the caller's kwargs, and the documented PyObject_Call/callable(*args, **kwargs) equivalence now holds for the C API too (pythongh-86795). _PyStack_UnpackDict takes a critical section while copying the dict's items out, retrying if the dict was resized in between, which makes unpacking a shared dict safe on free-threaded builds. f(**d) with a small dict is ~1.4x faster; calls without ** unpacking are unaffected. Co-authored-by: Josh Rosenberg <1178095+MojoVampire@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Back | FazBrowse Home | New Git URL |
This PR addresses #86199 (performance regression in f(**kw)) and #86795 (incorrect behavior of PyObject_Call leading to crashes). The work is based on gh-92192 by @MojoVampire, which went stale.
caller's kwargs dict.
It took some iterations before ending up at the current PR. The main optimization is due to the compiler pushing the ** operand as-is instead of BUILD_MAP/DICT_MERGE. CALL_FUNCTION_EX converts a non-exact mapping to an exact dict itself, reusing the DICT_MERGE machinery so error messages are unchanged. PyObject_Call copies the dict only on the tp_call path, where the callee would otherwise receive the caller's dict directly. For the vectorcall protocoll the _PyStack_UnpackDict takes a critical section while copying the dict's items out (retrying on a concurrent resize).