Почему аннотации типов замедляют код Python?

Это результаты моих тестов в ipython.

Для int:

In [2]: %time for _  in range(1000): exec('a: int = 4')                                                                                                                         
CPU times: user 12.2 ms, sys: 12 µs, total: 12.2 ms
Wall time: 12.2 ms

In [3]: %time for _  in range(1000): exec('a = 4')                                                                                                                              
CPU times: user 9.5 ms, sys: 0 ns, total: 9.5 ms
Wall time: 9.54 ms

И для str:

In [4]: %time for _  in range(1000): exec('a: str = "hello"')                                                                                                                   
CPU times: user 13.3 ms, sys: 0 ns, total: 13.3 ms
Wall time: 13.4 ms

In [5]: %time for _  in range(1000): exec('a = "hello"')                                                                                                                        
CPU times: user 10.4 ms, sys: 0 ns, total: 10.4 ms
Wall time: 10.4 ms

А также для list:

In [6]: %time for _  in range(1000): exec('a: list = [1,2, "hello"]')                                                                                                           
CPU times: user 19.1 ms, sys: 0 ns, total: 19.1 ms
Wall time: 21.5 ms

In [7]: %time for _  in range(1000): exec('a = [1,2, "hello"]')                                                                                                                 
CPU times: user 15.8 ms, sys: 0 ns, total: 15.8 ms
Wall time: 15.8 ms

Я знаю, что теоретически не должно быть никакой разницы в аннотациях list или int, хотя в них нет никакой функциональности. Но я только что проверил эти типы, чтобы убедиться, что использование подсказок типа замедляет выполнение примерно на 25 процентов. Почему это? Насколько я знаю, подсказки типов ничего не делают с выполнением. Просто тратя больше времени на их разбор и добавление в словарь __annotations__, вы получаете огромную разницу во времени выполнения?

stackoverflow.com/a/41692943/10858217 Вы можете проверить этот ответ для справки
Krishna Manohar 19.03.2022 22:30

В вашем примере код почти ничего не делает, поэтому разбор и добавление в __annotations__ составляет большую часть общего времени.

mkrieger1 19.03.2022 22:30

Вот почему правильное использование timeit отделяет время компиляции от времени выполнения, поэтому вы не получаете такого эффекта распространения. Почему кого-то должно волновать, сколько времени потребуется, чтобы скомпилировать строку кода тысячу раз?

Charles Duffy 19.03.2022 22:32

Если вы заранее compile() кодовый объект, то exec кодовый объект, я нахожу меньшую разницу, но все же кажется, что разница есть

juanpa.arrivillaga 19.03.2022 22:36

Используйте модуль dis, чтобы увидеть байтовый код, сгенерированный каждым из них.

chepner 19.03.2022 22:37

@juanpa.arrivillaga Интересно, почему так? разве exec сначала не компилируется, а затем выполняется?

Amir reza Riahi 19.03.2022 22:37

@AmirrezaRiahi ну да, поэтому этап компиляции занимает больше времени, но это происходит только один раз в программе, так что обычно вы не беспокоитесь об этом. Особенно на таком языке, как Python, с минимальным штрафом за компиляцию (хотя это может иметь значение в некоторых случаях, когда вы не выполняете предварительно скомпилированный код и хотите много раз выполнять небольшую утилиту python, например)

juanpa.arrivillaga 19.03.2022 22:38

@chepner да, я сделал это прямо сейчас и вижу разницу. Но разница действительно намного больше, чем я думал. Я всегда рассматривал подсказки типов как нечто, не влияющее на производительность.

Amir reza Riahi 19.03.2022 22:39
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
8
48
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш метод проверяет время компиляции интерпретации необработанной строковой версии кода Python во что-то, что является исполняемым. Если бы вместо этого вы использовали timeit с функциями, вы не увидели бы заметной разницы:

import timeit


def method1():
    for _ in range(1000): a: int = 4


def method2():
    for _ in range(1000): a = 4


print(timeit.timeit(method1, number=200000))
print(timeit.timeit(method2, number=200000))
2.8046581
2.8103205999999994

Если я делаю dis.dis('a: int = 4'), я вижу инструкции относительно аннотации. Если я делаю dis.dis(method1), я их не вижу. Он показывает то же, что и dis.dis(method2). Таким образом, речь может идти не о компиляции и выполнении, а скорее о компиляции и какой-то другой компиляции.

Kelly Bundy 21.03.2022 00:23

Другие вопросы по теме