Skip to content
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

Use tagged pointers on the stack in the default build. #127705

Open
markshannon opened this issue Dec 6, 2024 · 1 comment
Open

Use tagged pointers on the stack in the default build. #127705

markshannon opened this issue Dec 6, 2024 · 1 comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@markshannon
Copy link
Member

markshannon commented Dec 6, 2024

Currently all references to objects in frameobjects use _PyStackRef instead of PyObject *.
This is necessary for the free-threaded build to support deferred references.

For the default build _PyStackRef is just an alias for PyObject *.
We should change _PyStackRef to use proper tagged pointers in the default build for two important reasons:

  • It will reduce the maintenance burden of using tagged pointers if they were the same in both builds
  • It offers a lot of optimization potential. The overhead of reference counting operations is large, and tagged pointers will allow us to reduce that overhead considerably.

My initial implementation is 0.8% slower, although I'd like to get that closer to 0 before merging anything. There is some speedup in the GC due to streamlined immortality checks, and some slowdown due to increased overhead of turning new PyObject * references into _PyStackRefs.

This small slowdown will allow us a large speedup (maybe more than 5%) as we can do the following:

  • Reduce the overhead of refcount operations by using tagged references for the majority of LOAD_ instructions in the interpreter.
  • Completely eliminate many decref operations by tracking which references are tagged in the JIT.

The tagging scheme:

Tag Meaning
00 Normal pointers
01 Pointers with embedded reference count
10 Unused
11 Pointer to immortal object1 (including NULL)

This tagging scheme is chosen as it provides the best performance for the most common operations:

  • PyStackRef_DUP: Can check to see if the object's reference count needs updating with a single check and no memory read: ptr & 1
  • PyStackRef_CLOSE: As for PyStackRef_DUP, only a single bit check is needed
  • PyStackRef_XCLOSE: Since NULL is treated as immortal and tagged, this is the same as PyStackRef_CLOSE.

Maintaining the invariant that tag 11 is used for all immortal objects is a bit expensive, but can be mitigated by pushing the conversion from PyObject * to _PyStackRef down to a point where it is known whether an object is newly created or not.
For newly created objects PyStackRef_FromPyObjectStealMortal can be used which performs no immortality check.


  1. Actually, any object that was immortal when the reference was created. References to objects that are made immortal after the reference is created would have the low bits set to 00, or 01. This is OK as immortal refcounts have a huge margin of error and the number of possible references to one of these immortal objects is very small.

Linked PRs

@picnixz picnixz added type-feature A feature request or enhancement interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Dec 6, 2024
@markshannon
Copy link
Member Author

Stackrefs on the heap.

We use _PyStackRef for all locals variable and a couple of other frame fields, and frames can be present in paused generators.
This means that we can have _PyStackRefs in the heap. This is unsafe in general, but we can make stack refs safe to be stored in the heap provided that they do not have an embedded reference count, i.e. tag 01.

The free-threaded build allows any _PyStackRefs in the heap, but needs support from the cyclic GC and memory allocator to do so. For now, we make sure that no heap allocated _PyStackRef can be tagged 01.

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) type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

2 participants