-
Notifications
You must be signed in to change notification settings - Fork 1
Add unstable C API function for enabling deferred reference counting (PyUnstable_Object_EnableDeferredRefcount) #42
Description
In the free-threaded build, marking an object as supporting deferred reference counting allows the interpreter to skip reference count modifications for values pushed and popped from the interpreter's evaluation stack and local variables. This is important for avoiding contention on reference count modifications that inhibit scaling. See also PEP 703: "Deferred Reference Counting". The tradeoff is that these objects are only collected when the tracing GC runs.
In the free-threaded build, we currently have an internal-only function _PyObject_SetDeferredRefcount that allows an object to have deferred references to it. We use this on methods, top-level functions, modules, heap types, and a few other types of objects.
I think we should expose this as an unstable API for C extensions that "hints" the runtime to make this tradeoff:
- PR: gh-123619: Add an unstable C API function for enabling deferred reference counting python/cpython#123635 (by @ZeroIntensity)
By "hint", I mean that the runtime is free to ignore this suggestion. In particular, the the function is a no-op in the default build.
Motivation
Some C API extensions like nanobind create their own function and method-like objects. The nanobind.nb_func and nanobind.nb_method behave like functions and methods, but they are not subclasses of PyFunction_Type, PyCFunction_Type, or PyMethod_Type.
Without deferred reference counting (or immortalization) enabled, calling nanobind functions from multiple threads doesn't scale at all -- nanobind would not be very useful in the free-threaded build.
I think this is likely to come up in other extensions as well -- Peter's PR was created in response to a separate issue by @Yiling-J, for example.
Alternatives
Nanobind's support for free-threading currently relies on making nb_func and nb_method objects immortal in the free-threaded build immortal by basically copy-pasting _Py_SetImmortal. That's obviously not great - I'd much rather that nanobind is able to use an official, unstable API than to modify internals in a way that's likely to break in new CPython versions.
Note that in 3.13 deferred reference counting and immortalization are entangled due to time limitations, but this is no longer the case in main/3.14.
Python API?
I don't think we should expose this as a Python API, at least for now.