-
Notifications
You must be signed in to change notification settings - Fork 1
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
gh-115754: Expose constants (ex: Py_None) as public ABI symbols (stable ABI) #16
Comments
I wrote:
In the past, I proposed adding |
I prefer that stable ABI is useful for alternative implementations: they nicely show the design space that CPython might want to explore in the future. If we want to hide implementation details, looking at PyPy is a pretty good way to do it.
I'd prefer that we do that now -- for new additions. |
A similar option, which I believe @zooba suggested:
|
Objects exposed directly by the C-API were one of the few hard problems I had to solve for a per-interpreter GIL. If it hadn't been for immortal objects, I would have had to either do some messy macro trickery or introduce costly fixup code across most of the C-API implementation (using the existing symbols as . In addition to the extra complexity, the macro hacks would have probably been a further pain point for other compilers/implementations. The fixup code solution would have been fragile. It also doesn't help that we expose values for some of the objects (e.g. the non-exception builtin types) rather than just pointers. Note that even with object immortality I still had to add a trick for static builtin types, to accommodate Everything would have been drastically simpler if we had never exposed the objects directly in the first place. With that in mind, I firmly favor option (B). Let's move away from exposing objects directly in the C-API, rather than doing it more. |
Hum, I will try to work on a benchmark to have numbers. It may help to take a decision. |
I wrote python/cpython#116572 to measure the performance overhead. I modified the constant everywhere for this benchmark, including inside Python internals. So the measurement is the worst case scenario. Results with PGO, LTO and CPU isolation on Linux: python/cpython#116572 (comment)
6 benchmarks are at least 1.05x slower:
3 benchmarks are at least 1.05x faster:
3x faster for coverage looks really strange. Maybe I didn't run benchmarks properly, I'm not sure. |
Since the majority seems to prefer function calls, I prepared a full PR which documents the change: python/cpython#116572 It's ready to be merged, waiting for a decision :-) @iritkatriel: Do you have an opinion on this decision? Tell me if you need more details. |
There doesn't seem much of a perf impact, so yeah I think option B makes more sense. |
I already ran a microbenchmark and pyperformance. Do you need more benchmarks? I'm not sure that I get your comment. |
I am used to having the benchmark output in the specific presentation that we use for our team's benchmarking: But I now see that you did run pyperformance and found that it doesn't have a significant effect so I'll accept that. My vote would currently be (A), since I abhor the idea of a macro that looks like a variable reference but actually expands to a call. Didn't we introduce immortal objects specifically so that we could have a single |
I wrote 3 implementations so they can be compared:
So the majority prefers option (B): Implement "Py_None" API as a function call. Between adding 5 functions for 5 constants and adding 2 functions for 10 constants, I prefer the "Py_GetConstantRef()" approach: only add 2 functions. Moreover, it gives the choice between borrowed and strong references, even if in practice, both are the same. |
I close the issue, the decision is to use function calls. The discussion on the actual implementation can happen directly on the pull request. |
Currently, the C API implements Py_None, Py_True, Py_False, Py_Ellipsis and Py_NotImplemented constants by referring to private symbols at the ABI level:
In the stable ABI, I would like to avoid referring to private symbols, but only use public symbols.
The issue python/cpython#115754 discussed two options:
PyObject *Py_None
symbol at the ABI level.#define Py_None Py_GetNone()
withPyObject* Py_GetNone(void)
, wherePy_GetNone()
cannot fail.Please read the issue python/cpython#115754 discussion, especially @zooba's comment python/cpython#115754 (comment) which lists possible constraints of such API. Extract:
Please vote for your preferred option, or add a comment if you want a 3rd option.
I wrote python/cpython#115755 which is a simple change implementing option (A).
I prefer option (A). I agree that option (B) avoids any potential/hypothetical issue in the future, but I don't want to write a perfect API/ABI, only make pragmatic changes.
Option (B) can have a (significant?) impact on performance, especially on hot code. So far, nobody ran microbenchmarks, no number is available.
I would prefer to only consider option (B) once we will deeply design the stable ABI. Right now, we are far from being able to support tagged pointers for example. I would prefer to not pay a performance overhead for a theoretical issue :-(
Note: Subinterpreters are sharing singletons thanks to immortal objects (PEP 683, Python 3.12).
By the way, I already added
Py_Is(x, y)
,Py_IsNone(x)
,Py_IsTrue(x)
andPy_IsNone(x)
for PyPy. In PyPy, you cannot simply compare an object to a constant memory address (if (x == Py_None) ...
), since PyPy objects can be moved in memory.This issue is specific to CPython. Other Python implementations are free to use a different implementation and a different ABI.
The text was updated successfully, but these errors were encountered: