PEP 683: "Immortal Objects, Using a Fixed Refcount" (round 4, last call)

@markshannon

PEP 683 needs some explanation as to why the scheme for marking an object as immortal was chosen. Specifically why two faster alternatives were rejected.

As Petr mentioned, the PEP is just using a generic reference to what a scheme could look like. In fact, the current PR is using the 32 bit saturating reference count: PR#19474 precisely for performance. Plus, we’ll have all the upper bits for future use! If you think it’s of use, we can talk about the specific implementation in the PEP. Though I believe one of the suggestions in the first round was to abstract the PEP of the actual implementation.

@encukou

stable ABI extensions may panic when they see a negative refcount

Yeah that was indeed considered! Fortunately, as Mark said, extensions would be reading the full word and it will look as positive to them. The only place where the lower 32 bit is read is in the runtime, which we can fully control what negative, 0, and positive mean.

Mark mentioned reserving the sign bit to always be zero, though I think that in practice, this bit is already implicitly reserved as indeed, extensions will panic if this bit were to be set.

Do you have any reference to extensions that would do this?

I’m not qualified to critique the PEP, but I see that it mentions the per-interpreter GIL and shared objects as a use case. I was wondering if this proposed change might also address a similar issue I had with the nogil fork, specifically that an object shared across threads breaks parallelism due to the need to maintain refcount (I am not sure if this is true of “immutable” or singletons in nogil, but it is certainly true of normal objects). I encountered this issue when I needed all my threads to access a dictionary of constants. I had to give each thread its own copy (meaning very slow start up) even though the dictionary is never written to and would could happily live in memory for the life of the program rather than being subject to GC.

I also note that you don’t initially propose any public API, but in a case like mine even an unsupported/experimental way to mark normal objects as immortal would be interesting and potentially beneficial.

As you can probably tell, I’m interested but not expert in this area so any advice is welcome.

I’m not familiar enough with the no-gil fork to offer much of an answer here, though it seems like no-gil would benefit from immortal objects in some cases. @colesbury would have better insight.

As to the API, there will certainly be an undocumented, internal one. Anyone can use the internal API out of Include/internal/pycore_*.h (but there be dragons).

Anyone can use the internal API to build a proof of concept, or use it with a fork (or pinned build) of CPython. But you can’t expect any kind of stability, so e.g. putting it on PyPI and encouraging people to use it would be dishonest.
If the proof of concept is successful, it could make a good argument for opening the API up.

1 Like

The Steering Council accepts PEP 683 (Immortal Objects) with reservations.

The main concern for the SC is ABI compatibility.
To mimimize possible risk, even to 32-bit Stable-ABI extensions, please implement the proposed “Solution for Accidental De-Immortalization” – that is, only immortalize objects whose deallocator will reset the refcount. (Note that this rules out automatically marking all objects immortal at the end of runtime init.)

Please update the PEP with performance measurements as the implementation is being finalized. We’ll need to have another round of public disscussions about the performance impact once it’s known.

— Petr, on behalf of the SC

5 Likes

Oh, and I just noticed the PEP doesn’t link here. I hope this is the right place to post…
Eric, could you add Discourse/Python-dev links to Post-History, and update Discussions-To to point to the latest thread?
Let me know if you need help (or ask the PEP editors).

Will do!

(This space intentionally left blank.)

I’ve marked the PEP as accepted, added the conditions you listed, and updated the other headers. The changes should show up soon.

3 Likes

Hi Python Core Developers:

Making inmortal objects just by having a huge RefCount is no guarantee the object is going to stay inmortal. The word “Inmortal” implies, the object will never die, prove of this is there is some code cheching for this to rise an error. Hence it would have better performance to have a flag tagging an object as inmortal and make AddRef,DecRef checking it and leaving the ref count = 1 for the entire life.

@eelizondo @eric.snow
Since the implementation is merged, let’s update the PEP 683 with final performance numbers (for all platforms in 32- and 64-bit modes).

Additionally, could you please update the PEP to reflect that the final implementation uses 0xFFFFFFFF (32 lower bits) in 64-bit mode instead of 2^62 or 2^60 _Py_IMMORTAL_BIT? This affects calculations in the Accidental Immortality section: 16 seconds to immortalize instead of 2,500 days and other sections such as _Py_IMMORTAL_REFCNT.

You could also elaborate the Maintainability section to mention that there are now 3 different ways of working with refcounts (normal, immortal 32-bit and immortal 64-bit) and string interning became more complicated. And I think you meant “it should not have much impact on maintainability” (add not).

Finally, in the last section Reference Counting, with Cyclic Garbage Collection:

  1. This is just generic info about GC. While helpful, it does immediately clear how is it related to immortal objects. I assume the whole reference cycle will not be collected if there is at least one immortal object in it. Could you please add this for clarity?
  2. Fix typo: “before it’s memory is freed” → “before its memory is freed”

I hope you find my suggestions helpful! I can go ahead and create a PR if you’d like my help, but PEP1 recommends to discuss with the author first.

Once PEPs are accepted and implemented, the PEP itself becomes a historical document. If there’s important details that need to be documented they should go into Python’s documentation itself.

Okay. It’s just this may add confusion since the implemented approach is different from the proposed one. And PEPs are the place where people go to understand what’s going on. When Python 3.12 comes out, it will have PEP 683 in its change log and everyone will read it. Maybe it’s worth at least adding a disclaimer that final implementation is different with a link to code or docs?

Also, do you still support adding performance numbers as @encukou suggested?

1 Like

PEP 1 says:

If changes based on implementation experience and user feedback are made to Standards track PEPs while in the Provisional or (with SC approval) Accepted state, they should be noted in the PEP, such that the PEP accurately describes the implementation at the point where it is marked Final.

So, yes, the PEP should be updated.

3 Likes

This all sounds good! I’ll start preparing the updates over the next couple of days and work with @eric.snow to get it merged!

4 Likes

Hi, Eric!
Hi, Eddie!

I’ve spotted a couple of small issues in PEP 683:

  1. it contains several TODO: which is looking not beautiful
  2. it contains a broken link [with a pre-fork model](https://peps.python.org/pep-0683/Facebook)

Do you have any plans to fix them?

1 Like