# Using tagged pointers to support efficient integer operations

**URL:** <https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950>\
**Category:** Core Development\
**Created:** [April 11, 2025, 11:03am UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950 "2025-04-11T11:03:56Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![markshannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/markshannon/32/72_2.png) [@markshannon](https://discuss.python.org/u/markshannon)\
**Post date:** [April 11, 2025, 11:03am UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/1 "2025-04-11T11:03:56Z")

</div>

Integers are ubiquitous, both in Python code and the virtual machine.  
However our representation of integers is somewhat clunky and inefficient.

There is an old technique for handling ints efficiently in dynamic language VMs, called [tagged pointers](https://en.wikipedia.org/wiki/Tagged_pointer).  
A normal object pointer always has its low bits set to zero, as objects are 8 or 16 byte aligned in memory.  
We can use those low bits as “tags” to denote the meaning of the high bits.  
Historically, the low bit has been set to zero to indicate that the high bits are a pointer, and to 1 to indicate that the high bits are an integer.

We already use another tagging scheme within the interpreter and in the frame stack, the tag indicating whether the reference is borrowed.

This techniques is used in many older VMs and runtimes, including many lisps and Smalltalk.  
It is also used in newer VMs, including Ruby.  
It is not used in high-performance javascript VMs, because all numbers in JS are floats, so a different scheme is used called “Nan boxing”

### Why do this?

Speed.

Python is notoriously slow at integer handling. While tagged integers are not as fast as C integers, they are at least an order of magnitude faster than the boxed integers that Python currently uses. The tags will add some small cost to non-integer operations, but very little as most operations already know whether they are operating on an int or another type.

### How would we implement this?

Fortunately, there is no need to implement this all at once, everywhere.  
We would add it to the interpreter first, using tagged ints to support faster iteration and arithmetic.  
Then we would add it to a few core objects that make heavy use of ints, such as `range` and `enumerate`.  
Thereafter, we would broaden the use of tagged pointers, adding support for them to builtin collections like `list`, `tuple` and `dict`.  
Finally, we would add them to the C API so that third-party code can avoid the overhead of tagged pointer ↔ `PyObject *` conversions.

#### Tagging schemes

The exact tagging scheme we use is likely to change and be a result of experimentation and experience, but [here is a possible scheme](https://github.com/faster-cpython/ideas/issues/676#issuecomment-2523053504)

---

<div class="post-metadata">

**Author:** ![danielhrisca](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/danielhrisca/32/3282_2.png) [@danielhrisca](https://discuss.python.org/u/danielhrisca)\
**Post date:** [April 11, 2025, 11:44am UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/2 "2025-04-11T11:44:35Z")

</div>

Which CPython release do you think would contain this implementation?

---

<div class="post-metadata">

**Author:** ![markshannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/markshannon/32/72_2.png) [@markshannon](https://discuss.python.org/u/markshannon)\
**Post date:** [April 11, 2025, 2:42pm UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/3 "2025-04-11T14:42:14Z")

</div>

No particular version, as we would implement this incrementally.  
Some very narrow use of tagged ints might happen in 3.14.

---

<div class="post-metadata">

**Author:** ![barry](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/barry/32/42_2.png) [@barry](https://discuss.python.org/u/barry)\
**Post date:** [April 11, 2025, 4:59pm UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/4 "2025-04-11T16:59:03Z")

</div>

Should this be a PEP?

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 11, 2025, 11:40pm UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/5 "2025-04-11T23:40:21Z")

</div>

If this is our _first_ exposure of tagged pointers to CPython C API users in the form of an opaque `PyObject*` that code may otherwise attempt to dereference… we should move with care here. If these aren’t intended to “escape” internal-only uses then I do not think it matters as much.

Past relevant discussions:

- [Tagged pointers, again. This time with deferred reference counting. · Issue #632 · faster-cpython/ideas · GitHub](https://github.com/faster-cpython/ideas/issues/632)
- [PEP 620 – Hide implementation details from the C API | peps.python.org](https://peps.python.org/pep-0620/)

> We already use another tagging scheme within the interpreter and in the frame stack, the tag indicating whether the reference is borrowed.

for those not deep in the context of code daily, got links to where in the code we do this today?

---

<div class="post-metadata">

**Author:** ![kj0](https://avatars.discourse-cdn.com/v4/letter/k/db5fbb/32.png) [@kj0](https://discuss.python.org/u/kj0)\
**Post date:** [April 12, 2025, 8:38am UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/6 "2025-04-12T08:38:10Z")

</div>

> If these aren’t intended to “escape” internal-only uses then I do not think it matters as much.

The current implementation explicitly takes care to not “escape” to external C API. It is explicitly limited to the evaluation stack, the fields of the internal frame struct, and cell objects. We convert back to `PyObject *` when it escapes.

> for those not deep in the context of code daily, got links to where in the code we do this today?

See [cpython/Include/internal/pycore\_stackref.h at main · python/cpython · GitHub](https://github.com/python/cpython/blob/main/Include/internal/pycore_stackref.h#L18)

Especially the parts that use `Py_TAG_DEFERRED`. The evaluation stack (localsplus and a few other fields) was changed to use `_PyStackRef` in [gh-117139: Convert the evaluation stack to stack refs by Fidget-Spinner · Pull Request #118450 · python/cpython · GitHub](https://github.com/python/cpython/pull/118450) .

> Should this be a PEP?

I think implementing it within CPython doesn’t need a PEP. But once it exposes itself to third party libraries in the C API, it needs a PEP.

---

<div class="post-metadata">

**Author:** ![markshannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/markshannon/32/72_2.png) [@markshannon](https://discuss.python.org/u/markshannon)\
**Post date:** [April 12, 2025, 10:38am UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/7 "2025-04-12T10:38:53Z")

</div>

The initial use of tagged pointers is going to be very limited in scope, probably only on the evaluation stack. That clearly won’t need a PEP.

Then we’ll broaden the use to all of the frame stack, which is a larger change.  
I think it is good to keep people informed, but I don’t think that needs a PEP as there will be no language change and no API change.

However, when we do expose these in the C API, that would need at least one PEP.  
But, but that point, I expect we will already want to be proposing PEP(s) for a new C API anyway.  
That API would use opaque references, like [HPy](https://hpyproject.org/). We would probably implement those references using tagged pointers, but that would be an implementation detail.

What part(s) do you think would need a PEP?

---

<div class="post-metadata">

**Author:** ![markshannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/markshannon/32/72_2.png) [@markshannon](https://discuss.python.org/u/markshannon)\
**Post date:** [April 12, 2025, 10:48am UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/8 "2025-04-12T10:48:13Z")

</div>

A `PyObject *` is a pointer. Making it a tagged pointer would be lying to both the C compiler and to API consumers. We won’t do that.

Using an opaque one word struct or union is the most likely approach, especially as we already do that for the `_PyStackRef` tagged pointer in the interpreter.

Using struct or unions gives quite good type safety, especially compared to using pointers, which is a real help when converting code from using `PyObject *` to using tagged pointers.

---

<div class="post-metadata">

**Author:** ![Thrameos](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/thrameos/32/22312_2.png) [@Thrameos](https://discuss.python.org/u/Thrameos)\
**Post date:** [May 11, 2025, 2:52pm UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/9 "2025-05-11T14:52:37Z")

</div>

I just want to confirm that this doesn’t yank the existance of the boxed types. I have to derive the Python boxed types for my classes in JPype. They carry the extra information for casting and method resolution. Once you interact with them they become ordinary Python number types so switching them to tagged types is fine. But if you break the ability to have a boxed type that derives from an existing Python PyLong or PyFloat you are going to break a lot of APIs and some of them irresolvablely.

---

<div class="post-metadata">

**Author:** ![kj0](https://avatars.discourse-cdn.com/v4/letter/k/db5fbb/32.png) [@kj0](https://discuss.python.org/u/kj0)\
**Post date:** [May 12, 2025, 12:56am UTC](https://discuss.python.org/t/using-tagged-pointers-to-support-efficient-integer-operations/87950/10 "2025-05-12T00:56:57Z")

</div>

No this doesn’t.
