Это результаты моих тестов в 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__
, вы получаете огромную разницу во времени выполнения?
В вашем примере код почти ничего не делает, поэтому разбор и добавление в __annotations__
составляет большую часть общего времени.
Вот почему правильное использование timeit отделяет время компиляции от времени выполнения, поэтому вы не получаете такого эффекта распространения. Почему кого-то должно волновать, сколько времени потребуется, чтобы скомпилировать строку кода тысячу раз?
Если вы заранее compile()
кодовый объект, то exec
кодовый объект, я нахожу меньшую разницу, но все же кажется, что разница есть
Используйте модуль dis
, чтобы увидеть байтовый код, сгенерированный каждым из них.
@juanpa.arrivillaga Интересно, почему так? разве exec
сначала не компилируется, а затем выполняется?
@AmirrezaRiahi ну да, поэтому этап компиляции занимает больше времени, но это происходит только один раз в программе, так что обычно вы не беспокоитесь об этом. Особенно на таком языке, как Python, с минимальным штрафом за компиляцию (хотя это может иметь значение в некоторых случаях, когда вы не выполняете предварительно скомпилированный код и хотите много раз выполнять небольшую утилиту python, например)
@chepner да, я сделал это прямо сейчас и вижу разницу. Но разница действительно намного больше, чем я думал. Я всегда рассматривал подсказки типов как нечто, не влияющее на производительность.
Ваш метод проверяет время компиляции интерпретации необработанной строковой версии кода 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)
. Таким образом, речь может идти не о компиляции и выполнении, а скорее о компиляции и какой-то другой компиляции.