Title: Optimize class/module level annotation
Type: resource usage Stage: resolved
Components: Interpreter Core Versions: Python 3.10
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: BTaskaya, methane, pablogsal, uriyyo
Priority: normal Keywords:

Created on 2020-12-01 15:30 by uriyyo, last changed 2020-12-20 13:02 by serhiy.storchaka. This issue is now closed.

Messages (6)
msg382263 - (view) Author: Yurii Karabas (uriyyo) * (Python triager) Date: 2020-12-01 15:30
This issue is inspired by

We can do smth similar for class/module level annotations.

Inada Naoki what do you think regarding that?
msg382273 - (view) Author: Batuhan Taskaya (BTaskaya) * (Python committer) Date: 2020-12-01 17:51
Would it require a substantial amount of changes? If so, I don't think this is a critical optimization since nearly-all of class definitions happens on the module level and evaluated on the import time only once.
msg382288 - (view) Author: Inada Naoki (methane) * (Python committer) Date: 2020-12-02 01:23
I agree with Batuhan.

Although bytecode for class annotations seems inefficient, it is difficult to optimize without breaking backward compatibility.
You can write arbitrary code in class/module block which dynamically manipulate __annotations__.

class Foo:
    __annotations__["spam"] = "list"
    ham: tuple
msg382394 - (view) Author: Yurii Karabas (uriyyo) * (Python triager) Date: 2020-12-03 09:23
As all annotations are known at compilation time we can optimize annotations creating.

For instance, we have such code:
a: int
b: int

With the current implementation, we will have such bytecode:
  1           0 SETUP_ANNOTATIONS
              2 LOAD_CONST               0 ('int')
              4 LOAD_NAME                0 (__annotations__)
              6 LOAD_CONST               1 ('a')
              8 STORE_SUBSCR

  2          10 LOAD_CONST               0 ('int')
             12 LOAD_NAME                0 (__annotations__)
             14 LOAD_CONST               2 ('b')
             16 STORE_SUBSCR
             18 LOAD_CONST               3 (None)
             20 RETURN_VALUE

I would suggest using `BUILD_CONST_KEY_MAP` and bytecode will look like this:
 2           0 LOAD_CONST               0 ('int')
 3           2 LOAD_CONST               0 ('int')
 1           4 LOAD_CONST               1 (('a', 'b'))
             6 BUILD_CONST_KEY_MAP      2
             8 SETUP_ANNOTATIONS
             10 LOAD_CONST              2 (None)
             12 RETURN_VALUE

So `SETUP_ANNOTATIONS` bytecode will accept a dictionary with annotations of a current module/class.

I will look more like micro-optimization, I can implement this feature and run benchmarks to check performance boost.

I believe this optimization won't require a lot to change.
msg382399 - (view) Author: Batuhan Taskaya (BTaskaya) * (Python committer) Date: 2020-12-03 10:02
> For instance, we have such code:

But what about this, what would the bytecode will look like in this case (where the annotations don't exactly follow each other?)

a: int
T = TypeVar('T')
b: T
b1: Gen[T]
X = TypeVar('X')
c: X
d: str

Do you propose to build 2/3 different dicts and apply them one by one to allow calls to access that frame and recover the annotations?

> I will look more like micro-optimization, I can implement this feature and run benchmarks to check performance boost.

What kind of optimization do you target? It would be really cool if you could get us some numbers with a draft patch (like comparing to test the import time of a module heavy with annotations, or even maybe some real-world examples. Here is a list of heavily-typed projects:

Would love to see an import time comparison, or something similiar.
msg383427 - (view) Author: Yurii Karabas (uriyyo) * (Python triager) Date: 2020-12-20 10:55
After several attempts to optimize class/module annotations, I didn't find a solution that won't break existing code and can cover all existing edge cases.

The root cause was mentioned by Inada, the problem that `__annotations__` is exposed to locals and can be dynamically modified and that can't be predicted at compilation time.

Sorry about this issue, when I was creating this issue, I didn't realize the whole problem state.

We can close this issue.
Date User Action Args
2020-12-20 13:02:26serhiy.storchakasetstatus: open -> closed
resolution: rejected
stage: resolved
2020-12-20 10:55:47uriyyosetmessages: + msg383427
2020-12-03 10:03:03BTaskayasetnosy: + pablogsal
2020-12-03 10:02:50BTaskayasetmessages: + msg382399
2020-12-03 09:23:32uriyyosetmessages: + msg382394
2020-12-02 01:23:01methanesetmessages: + msg382288
2020-12-01 17:51:11BTaskayasetnosy: + BTaskaya
messages: + msg382273
2020-12-01 15:30:37uriyyocreate