This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Split function versions into 1-0xffff and 0x1000+ regions
Type: performance Stage:
Components: Interpreter Core Versions: Python 3.11
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Mark.Shannon, brandtbucher, kj
Priority: normal Keywords:

Created on 2021-12-16 11:06 by Mark.Shannon, last changed 2022-04-11 14:59 by admin.

Messages (3)
msg408686 - (view) Author: Mark Shannon (Mark.Shannon) * (Python committer) Date: 2021-12-16 11:06
Because functions are mutable, specifically because the __code__ attribute is mutable, we need to version functions when specializing.

However, some specializations (for special methods mainly) only have space for 16 bit versions.

It is likely that programs will have more than 2**16 functions versions, but it is much less likely that they will have more than 2**16 versions of special methods.

We should partition the version space into 1-0xffff for use by special methods and 0x1000+ for use by other methods.

See https://github.com/python/cpython/pull/30129 for an example of why this is needed.
msg408725 - (view) Author: Brandt Bucher (brandtbucher) * (Python committer) Date: 2021-12-16 18:43
Interesting idea!

> It is likely that programs will have more than 2**16 functions versions, but it is much less likely that they will have more than 2**16 versions of special methods.

Is it? The pyperformance suite isn't exactly representative of huge production apps, sure, but the most recent specialization stats don't seem to indicate that we ever run out of versions. Do we have reason to believe that real-world code would benefit from this?

These versions are only created for successful specializations in hot code (and we typically expect a high degree of stability thereafter), so I would think that in practice it's actually quite rare to bump the version after the first few are handed out.

> We should partition the version space into 1-0xffff for use by special methods and 0x1000+ for use by other methods.

Typo? It seems like the ranges 0x0001-0xFFFF and 0x10000-0x1FFFF are closer to what you're describing here. If so:
- Does this mean that it will now be impossible to specialize explicit calls to special methods, like "super().__init__()" or "object.__new__()"?
- Why not just maintain two separate 16-bit version counters, instead of one 17-bit counter with two distinct regions?

Or maybe you meant 0x0001-0x7FFF and 0x8000-0xFFFF? But then my concern would be that it essentially halves the number of "normal" functions that we can successfully specialize. If it's likely that more than 2**15 "normal" function versions will be used and unlikely that 2**15 special methods will be used, lots of the special method versions will remain unused long after we've exhausted the "normal" versions.

Also, just to make sure we're on the same page: when you say "other methods", are you actually referring to "other functions"? It doesn't seem like there's a reason for this versioning to only apply to method objects, but maybe I'm missing something.
msg408731 - (view) Author: Brandt Bucher (brandtbucher) * (Python committer) Date: 2021-12-16 19:34
Ah, never mind, I think I understand what you meant. Special methods get 0x0001-0xFFFF, all other functions get 0x00010000-0xFFFFFFFF. So we use 16-bit versions for special method caches, and 32-bit versions for normal call caches. "super().__init__()" and "object.__new__()" will specialize correctly, since special method versions are also valid function versions.

This seems like a solid idea, then, provided that we're reasonably confident this will actually improve real code. I guess the case this optimizes for is a program that specializes more than 2**16 normal calls, *then* tries to specialize a 2**16 special method calls? Seems uncommon, but not impossible.

(I think there might be a weird edge-case where a function is specialized first as a normal function, *then* as a special method. I imagine we just reassign it a special method version and continue as normal in that case, though.)
History
Date User Action Args
2022-04-11 14:59:53adminsetgithub: 90255
2021-12-16 19:34:22brandtbuchersetmessages: + msg408731
2021-12-16 18:43:21brandtbuchersetmessages: + msg408725
2021-12-16 14:24:25kjsetnosy: + kj
2021-12-16 11:06:10Mark.Shannoncreate