-
Notifications
You must be signed in to change notification settings - Fork 6
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Evolving the Stable ABI to use functions for INCREF, DECREF, TYPE #45
Comments
Nice. I had hoped that was the case (but didn't have the time to research it). Is it in all versions of the Stable ABI? (The functions themselves look like they were added 19 years ago.) It would be nice if we could somehow recommend a best practice for building future-proof wheels that uses this whenever user code calls |
Yes it looks like they've been there for quite a while; maybe I'm misremembering about Victor adding them. Anyways, since they were added in 3.2, I guess that means they're in all the versions of the Stable ABI. |
But what we'd need is a way to cause all stable-abi-using wheels compiled going forward to use these instead of accessing |
So, first step is to change Python 3.13: make the macros call the functions in limited API. I don't think there's a better way to evaluate the performance impact than to just do it and test with alphas. #15 (Array export and mass incref/decref) could help bring down overhead of multiple function calls. The way to cause all stable-abi-using wheels to avoid macros is to switch to a new version of stable ABI, and deprecate
You can't. Even if we could you'd still need to recompile -- wheels that are already uploaded definitely won't magically switch. And if you need to recompile, why not use a newer Python? |
Py_IncRef() checks for NULL and so might be a little bit less efficient: it's Py_XINCREF() as a function. So I added _Py_IncRef() which doesn't check for NULL: it's Py_INCREF(). |
But the latter aren’t in the Stable ABI yet, are they? |
_Py_IncRef() was added to the stable ABI in Python 3.10. You can check
|
Since 2020 (Python 3.10 and 3.11), I'm working on enforcing usage of function calls to get and set PyObject and PyVarObject members. My plan:
I already made multiple changes:
|
This is the part that we need to be able to repurpose part of the
Why is this needed? There is no "regular ABI", is there? |
As soon as you give access to structure members, people will do weird things and may crash Python. For example, PySide was accessing directly The less things are exposed, the more easy it is to evolve Python internals. See the current complexity of evolving Touching anything in a structure is always causing a lot of pain. What Python core devs do? Not change structures anymore, and teach other core devs to do the same. Well, that's basically the rationale of my withdrawn PEP 620: Hide implementation details from the C API. If Py_INCREF() remains implemented as a static inline function accessing directly |
Well, see also issue #22 which is related. |
That's debatable. Yes, there will be complaints, because sometimes the person complaining doesn't understand that the code they are using is doing something unsupported. But if they get an error during build, it's working as designed: they will have to fix their hack before they can continue. In the specific case of the Stable ABI, the whole point is that they are not recompiling for each version, and that's what blocks us (the CPython maintainers) from evolving the code. We explicitly promise that if you only use the Stable ABI, your binary wheels will keep working with newer versions of CPython. But we don't make such promises to users of the full API, which requires recompilation for each feature release (though generally not for bugfix releases). Nevertheless, in general I agree that we should minimize access to struct members. But some APIs need those for performance. And I think that non-stable-ABI use of INCREF/DECREF falls in that category. I also don't want to end up in a future where nobody can understand our headers any more because they are too full of hacks (like the C standard headers, which are very frustrating if you are trying to understand why something goes wrong). PS. I didn't try to understand what happened in the PySide2 issue you linked to -- the conclusion seems that it was their fault though? So again, things work as promised. |
I opened a discussion at: https://discuss.python.org/t/limited-c-api-implement-py-incref-and-py-decref-as-function-calls/27592 My implementation is now ready for review: python/cpython#105388 |
It can be fixed, it's mostly a matter of tradeoff between compatibility and performance. If all APIs are implemented as opaque function calls, as HPy and most of the limited C API do, there is no such ABI compatibility issue anymore. I'm tracking the quantity of "inlined" code via macros and static inline functions at: https://pythoncapi.readthedocs.io/stats.html#functions-defined-as-macros-and-static-inline-functions PEP 670: Convert macros to functions is a first step: fix the API to later consider converting static inline functions (as fast as macros) to opaque functions (unknown impact on performance, maybe not significant). Previously, many macros were abused for good or bad reasons in various ways, especially when functions implemented as macros had a return value whereas they "should not". I added |
IIUC the stable ABI version 3.12 now makes |
I close the issue. |
The specific problem here is that there are macros in the Stable ABI that access the
ob_refcnt
andob_type
directly. This makes it impossible to move those fields or change the meaning of some bits in them.At some point in the future it would be nice if the macros that access these (mainly
Py_INCREF
,Py_DECREF
andPy_TYPE
) were changed into real functions (not inline functions!), so that we can use some bits of these fields for flags. Possible uses include immortality, some GC state, and the nogil branch.I am creating this issue in order to request feedback on how problematic this would be for current heavy users of the Stable ABI. There's a tradeoff: we'd be able to offer more stability for a one-time change. The cost of the one-time change would be twofold:
INCREF
andDECREF
call (and forPy_TYPE
, but I assume that's less of a concern).This ties in with #39 -- we could arrange things so that 3.13 and several later versions still support the macros (or static inline functions -- for Stable ABI purposes that's pretty much the same thing), but any wheel built with 3.13 using the Stable ABI will use the functions. (You can still build wheels that use the macros, but they won't be compatible with future Python versions. I think this comes down to similar choices you have with HPy.)
My main question is whether this is ever going to be acceptable given the performance degradation due to the function calls. If it isn't, we have to do something else. If the performance hit is acceptable, we can talk about how we would evolve the Stable ABI to switch to function calls with minimal disruption for projects that use it. There would have to be a PEP in that case, I suspect.
The text was updated successfully, but these errors were encountered: