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
{{ message }}
Repository navigation
[C API] Move usage of tp_vectorcall_offset from public headers to the internal C API #89602
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2021-10-15.00:42:16.482>created_at=<Date2021-10-11.22:18:05.305>labels= ['expert-C-API', '3.11']
title='[C API] Move usage of tp_vectorcall_offset from public headers to the internal C API'updated_at=<Date2021-10-15.00:42:16.477>user='https://github.com/vstinner'
5 years ago, I added _PyObject_CallArg1() (similar to PyObject_CallOneArg()) and then I removed it since it consumed more stack memory than existing function, whereas I added _PyObject_CallArg1() to reduce the stack consumption.
Issue bpo-28858: Remove _PyObject_CallArg1() macro
Replace
_PyObject_CallArg1(func, arg)
with
PyObject_CallFunctionObjArgs(func, arg, NULL)
Using the _PyObject_CallArg1() macro increases the usage of the C stack, which
was unexpected and unwanted. PyObject_CallFunctionObjArgs() doesn't have this
issue.
I decided to merge my PR to address https://bugs.python.org/issue45439 initial issue: "[C API] Move usage of **tp_vectorcall_offset** from public headers to the internal C API".
Last years, I added tstate parameters to internal C functions. The agreement was that only internal functions should use it, and indirectly that this tstate parameter should be hidden. I'm now sure exactly, but tstate started to pop up in [Include/cpython/abstract.h](https://github.com/python/cpython/blob/main/Include/cpython/abstract.h) around "call" functions. This PR fix this issue.
About the impact on performances: well, it's really hard to draw a clear conclusion. Inlining, LTO and PGO give different results on runtime performance and stack memory usage.
IMO the fact that public C API functions are now regular functions should not prevent us to continue (micro) optimizing Python. We can always add a variant to the internal C API using an API a little bit different (e.g. add tstate parameter) or defined as a static inline function, rather than a regular function.
The unclear part is if PyObject_CallOneArg() (regular function call) is faster than _PyObject_CallOneArg() (static inline function, inlined). The performance may depend if it's called in the Python executable or in a dynamic library (PLT indirection which may be avoided by gcc -fno-semantic-interposition).
Well, happy hacking and let's continue *continuous* benchmarking Python!
Activity
vstinner commentedon Oct 11, 2021
The public C API should avoid accessing directly PyTypeObject members: see bpo-40170.
I propose to move static inline functions to the internal C API, and only expose opaque function calls to the public C API.
vstinner commentedon Oct 11, 2021
New changeset fb8f208 by Victor Stinner in branch 'main':
bpo-45439: _PyObject_Call() only checks tp_vectorcall_offset once (GH-28890)
fb8f208
vstinner commentedon Oct 11, 2021
New changeset ce3489c by Victor Stinner in branch 'main':
bpo-45439: Rename _PyObject_CallNoArg() to _PyObject_CallNoArgs() (GH-28891)
ce3489c
vstinner commentedon Oct 12, 2021
New changeset d943d19 by Victor Stinner in branch 'main':
bpo-45439: Move _PyObject_CallNoArgs() to pycore_call.h (GH-28895)
d943d19
vstinner commentedon Oct 13, 2021
I should also check again the stack consumption. Old issues:
See also: "Stack consumption" of https://vstinner.github.io/contrib-cpython-2017q1.html
vstinner commentedon Oct 13, 2021
5 years ago, I added _PyObject_CallArg1() (similar to PyObject_CallOneArg()) and then I removed it since it consumed more stack memory than existing function, whereas I added _PyObject_CallArg1() to reduce the stack consumption.
commit 7bfb42d
Author: Victor Stinner <victor.stinner@gmail.com>
Date: Mon Dec 5 17:04:32 2016 +0100
vstinner commentedon Oct 13, 2021
I measured the stack consumption using attached sys_call.patch and stack_overflow-4.py.
Using gcc -O3, the stack consumption with PR 28893 is *way better* on the 6 benchmarks (6 ways to call functions), especially:
PyObject_CallOneArg(): 624 bytes/call => 528 bytes/call (-96 bytes)
PyObject_CallNoArg(): 608 bytes/call => 512 bytes/call (-96 bytes)
_PyObject_CallNoArg(): 608 bytes/call => 512 bytes/call (-96 bytes)
Python built in release mode with gcc -O3:
./configure && make
=== ref ===
=> total: 80726 calls, 3824 bytes
=== PR ===
=> total: 94164 calls, 3264 bytes
vstinner commentedon Oct 13, 2021
Using LTO, the PR 28893 *increases* the stack memory usage. It's the opposite :-)
PyObject_CallOneArg(): 672 bytes/call => 688 bytes/call (+16 bytes)
PyObject_CallNoArg(): 640 bytes/call => 672 bytes/call (+32 bytes)
_PyObject_CallNoArg(): 640 bytes/call => 672 bytes/call (+32 bytes)
clang with LTO:
=== ref ===
=> total: 75607 calls, 4096 bytes
=== PR ===
=> total: 73082 calls, 4224 bytes
vstinner commentedon Oct 14, 2021
New changeset 3cc56c8 by Victor Stinner in branch 'main':
bpo-45439: Move _PyObject_VectorcallTstate() to pycore_call.h (GH-28893)
3cc56c8
vstinner commentedon Oct 14, 2021
I decided to merge my PR to address https://bugs.python.org/issue45439 initial issue: "[C API] Move usage of **tp_vectorcall_offset** from public headers to the internal C API".
Last years, I added
tstateparameters to internal C functions. The agreement was that only internal functions should use it, and indirectly that thiststateparameter should be hidden. I'm now sure exactly, buttstatestarted to pop up in[Include/cpython/abstract.h](https://github.com/python/cpython/blob/main/Include/cpython/abstract.h)around "call" functions. This PR fix this issue.About the impact on performances: well, it's really hard to draw a clear conclusion. Inlining, LTO and PGO give different results on runtime performance and stack memory usage.
IMO the fact that public C API functions are now regular functions should not prevent us to continue (micro) optimizing Python. We can always add a variant to the internal C API using an API a little bit different (e.g. add
tstateparameter) or defined as a static inline function, rather than a regular function.The unclear part is if PyObject_CallOneArg() (regular function call) is faster than _PyObject_CallOneArg() (static inline function, inlined). The performance may depend if it's called in the Python executable or in a dynamic library (PLT indirection which may be avoided by
gcc -fno-semantic-interposition).Well, happy hacking and let's continue *continuous* benchmarking Python!
mikita-sakalouski commentedon May 22, 2024
@vstinner Can this change be a root cause for #99152 ?
This line: