Skip to content

Add a C API function to detect temporaries #133164

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

Closed
ngoldbaum opened this issue Apr 29, 2025 · 3 comments
Closed

Add a C API function to detect temporaries #133164

ngoldbaum opened this issue Apr 29, 2025 · 3 comments
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-C-API type-feature A feature request or enhancement

Comments

@ngoldbaum
Copy link
Contributor

ngoldbaum commented Apr 29, 2025

Feature or enhancement

Proposal:

NumPy has an optimization to detect temporaries created via the NumPy C API (e.g. in NumPy internals) and elide them. This can lead to a significant performance improvement for some operations.

In numpy/numpy#28681, @colesbury proposed adding some code to handle the change to use stackrefs internally in CPython, which broke the NumPy temporary elision heuristics in 3.14.

We later added that code more or less verbatim to the NumPy main branch:

https://github.com/numpy/numpy/blob/d692fbccd98cb880812b32936e5f94fcfe55053f/numpy/_core/src/multiarray/temp_elide.c#L119-L152

This unblocks testing NumPy on the 3.14 beta but we should really have at least an unstable C API function we can call here rather than relying on CPython internals.

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

#133140 (comment)

Linked PRs

@ngoldbaum ngoldbaum added the type-feature A feature request or enhancement label Apr 29, 2025
@colesbury
Copy link
Contributor

To add some more context, in NumPy, if you write:

x = np.array([ ... ])
foo(x)

In 3.14, x will have a reference count of 1 within the call to foo() (from the local variable x), whereas before 3.14, it would be at least 2. The reason is that interpreter now safely borrows references when it can, such as in function calls, if it knows that it already holds a strong reference. It's an optimization in the generated bytecode that replaces some LOAD_FAST bytecodes with the new LOAD_FAST_BORROW bytecode.

This means that in some contexts, it's not safe to assume that Py_REFCNT(x) == 1 means that you can modify x in-place. They weren't 100% true before because of the C API (see the explanation in numpy/numpy@e6c397b), but the 3.14 change definitely breaks some code.

We want to provide some C API that NumPy and other packages can uses to safely replace their Py_REFCNT(x) == 1 checks. Ideally, it should also avoid the need for backtrace() too.

The workaround for NumPy, which I think we should encapsulate in a new C API function, is to check that:

  1. The reference count is 1
  2. There exists a single temporary on the interpreter's stack that is a strong (not borrowed) reference to the relevant object

We might want to call this something like: PyUnstable_Object_IsUniqueTemporary(ob)

also cc @pablogsal

colesbury added a commit to colesbury/cpython that referenced this issue Apr 29, 2025
After pythongh-130704, the interpreter replaces some uses of `LOAD_FAST` with
`LOAD_FAST_BORROW` which avoid incref/decrefs by "borrowing" references
on the interpreter stack when the bytecode compiler can determine that
its safe.

This change broke some checks in C API extensions that relied on
`Py_REFCNT()` of `1` to determine if it's safe to modify an object
in-place. Objects may have a reference count of one, but still be
referenced further up the interpreter stack due to borrowing of
references.

This provides a replacement function for those checks.
`PyUnstable_Object_IsUniqueTemporary` is more conservative: it checks
that the object has a reference count of one and that it exists as a
unique strong reference in the interpreter's stack of temporary
variables in the top most frame.

See also:

* numpy/numpy#28681
@picnixz picnixz added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Apr 30, 2025
colesbury added a commit to colesbury/cpython that referenced this issue Apr 30, 2025
@ZeroIntensity
Copy link
Member

Should this (and probably #133140) be a 3.14 blocker? That beta freeze is creeping up on us quickly.

colesbury added a commit to colesbury/cpython that referenced this issue May 2, 2025
colesbury added a commit that referenced this issue May 2, 2025
…h-133170)

After gh-130704, the interpreter replaces some uses of `LOAD_FAST` with
`LOAD_FAST_BORROW` which avoid incref/decrefs by "borrowing" references
on the interpreter stack when the bytecode compiler can determine that
it's safe.

This change broke some checks in C API extensions that relied on
`Py_REFCNT()` of `1` to determine if it's safe to modify an object
in-place. Objects may have a reference count of one, but still be
referenced further up the interpreter stack due to borrowing of
references.

This provides a replacement function for those checks.
`PyUnstable_Object_IsUniqueReferencedTemporary` is more conservative:
it checks that the object has a reference count of one and that it exists as a
unique strong reference in the interpreter's stack of temporary
variables in the top most frame.

See also:

* numpy/numpy#28681

Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com>
Co-authored-by: T. Wouters <thomas@python.org>
Co-authored-by: mpage <mpage@cs.stanford.edu>
Co-authored-by: Mark Shannon <mark@hotpy.org>
Co-authored-by: Victor Stinner <vstinner@python.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) topic-C-API type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

4 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy