You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A function call with 16 or more keyword arguments compiles to a
different, slower call opcode than the same call with 15. The switch
happens at an exact, fixed cutoff, not a gradual slowdown: adding one
more keyword argument (15 -> 16) makes the call ~4.5x slower.
codegen_call_helper_impl() in Python/codegen.c picks the call opcode
based on this check:
if (nelts+nkwelts*2>_PY_STACK_USE_GUIDELINE) {
goto ex_call;
}
nelts is the number of positional args, nkwelts the number of
keyword args, _PY_STACK_USE_GUIDELINE is 30. A call with only keyword
arguments trips this at 16. Above the threshold the compiler builds a
tuple+dict and calls CALL_FUNCTION_EX; below it, it just uses CALL_KW.
Drop the threshold entirely for the call case. In the same thread,
one maintainer argued cutoffs aren't worth it here, runtime
performance matters more than bytecode size
Improve the accuracy of the formula, so it tracks the CALL_KW
path's real stack use - close to nelts + nkwelts + (nkwelts != 0),
not nelts + nkwelts*2.
Benchmarked this version, _PY_STACK_USE_GUIDELINE left at 30:
n=16 CALL_KW 101.6 ns (was 440.1 ns)
...
n=29 CALL_KW 219.2 ns
n=30 CALL_FUNCTION_EX 895.6 ns (cliff still there, but moved to 30)
Related
#148817 (open) - a different change (gh-126835) stopped constant-folding
set/list literals over 30 items in in/not in/for, ~20x slower
3.13 -> 3.14. Different code path, same constant
Versions affected
Present in every release from 3.10.0 through 3.14.0, and current main
Bug report
Bug description:
A function call with 16 or more keyword arguments compiles to a
different, slower call opcode than the same call with 15. The switch
happens at an exact, fixed cutoff, not a gradual slowdown: adding one
more keyword argument (15 -> 16) makes the call ~4.5x slower.
codegen_call_helper_impl()inPython/codegen.cpicks the call opcodebased on this check:
https://github.com/python/cpython/blob/main/Python/codegen.c#L4430
neltsis the number of positional args,nkweltsthe number ofkeyword args,
_PY_STACK_USE_GUIDELINEis 30. A call with only keywordarguments trips this at 16. Above the threshold the compiler builds a
tuple+dict and calls
CALL_FUNCTION_EX; below it, it just usesCALL_KW.Reproducer
Calling an empty function with
nnamed params, all passed as keywords:Possible directions
Raise the threshold for this check. sets with >30 elements not constant-folded in
in/not inandforloops #148817 has a similar discussionfor a different code path, with 64 proposed as a round number
Drop the threshold entirely for the call case. In the same thread,
one maintainer argued cutoffs aren't worth it here, runtime
performance matters more than bytecode size
Improve the accuracy of the formula, so it tracks the
CALL_KWpath's real stack use - close to
nelts + nkwelts + (nkwelts != 0),not
nelts + nkwelts*2.Benchmarked this version,
_PY_STACK_USE_GUIDELINEleft at 30:Related
#148817 (open) - a different change (gh-126835) stopped constant-folding
set/list literals over 30 items in
in/not in/for, ~20x slower3.13 -> 3.14. Different code path, same constant
Versions affected
Present in every release from 3.10.0 through 3.14.0, and current main
CPython versions tested on:
3.10, 3.12, CPython main branch
Operating systems tested on:
macOS