В сообществе программистов хорошо известно, что Python не выигрывает ни в одной гонке.
Но несмотря на это, Python великолепен, и он всем нравится.
Но он очень медленный...
В этой статье я расскажу о различных функциях Python и разберу, почему они делают его одним из наиболее продвинутых языков на сегодняшний день — с тем недостатком, что он не такой быстрый .
Но сначала давайте усвоим некоторые основы языков программирования.
Уровни абстракции
Как вы, возможно, знаете, языки программирования часто характеризуются уровнем абстракции.
- Низкий уровень абстракции указывает на то, что язык ближе к аппаратному (труден для интерпретации).
- Высокий уровень показывает, что код ближе к пользователю (его легче интерпретировать).
C++, PHP, Java, Python… все они считаются современными (высокоуровневыми) языками программирования, поскольку они могут работать практически в любых системах.
На ассемблере вам нужно написать отдельную программу в соответствии с инструкциями каждого конкретного процессора (нельзя запускать один и тот же код на разных процессорах).
Например , если вы создаете программу, которая печатает «Hello world», и отправляете код своему другу (у которого другая модель компьютера), когда он попытается ее выполнить, она, вероятно, не запустится.
Современные языки — высший уровень абстракции
Несмотря на то, что это абстракция машинного кода, на последнем уровне пирамиды также существуют иерархии.
С одной стороны, вы можете найти процедурные языки, такие как C, где вам нужно точно знать, что вы делаете, шаг за шагом. Преимущество этого подхода состоит в том, что он очень эффективен, а недостаток заключается в том, что он сложен и не очень гибок .
С другой стороны, другие языки упрощают задачи, позволяя работать с более читаемым и гибким кодом .
Это случай - Python. Его можно использовать практически для чего угодно, и его легко реализовать, но у него есть недостаток: он не так эффективен для определенных задач.
Но почему именно Python такой «медленный»?
Давайте пересмотрим некоторые особенности языка, чтобы ответить на этот вопрос.
## Интерпретируемый язык
Прежде всего, Python — это интерпретируемый язык , а это означает, что код читается и выполняется программой (называемой интерпретатором) построчно во время выполнения .
Это один из способов преобразования вашего кода в машинный код.
Компилируемые языки
Еще один способ сделать ваш код «понимаемым машиной» — это процесс компиляции.
В этом случае исходный код преобразуется компилятором в машинный код перед его фактическим запуском на компьютере.
# Почему интерпретируемые языки медленнее?
В интерпретируемых языках каждая строка исходного кода транслируется в машинный код «на лету» во время выполнения.
Это означает, что каждый раз, когда программа запускается, интерпретатор должен анализировать и выполнять код , что увеличивает накладные расходы по сравнению с непосредственным запуском предварительно скомпилированного машинного кода.
Например: если фрагмент кода выполняется несколько раз (например, внутри цикла), интерпретатор должен читать и транслировать его каждый раз, когда он встречается .
Скомпилированная программа, напротив, будет запускать машинный код напрямую, без необходимости его повторной трансляции каждый раз, когда он проходит через цикл.
## CPython и его глобальная блокировка интерпретатора (GIL)
Стандартный интерпретатор Python — CPython. Он написан на C и Python и компилирует код Python в байт-код перед его интерпретацией .
Чтобы предотвратить одновременное выполнение байт-кодов Python несколькими собственными потоками, CPython использует глобальную блокировку интерпретатора.
Эта блокировка необходима, поскольку управление памятью CPython не является потокобезопасным .
Однако это может стать существенным узким местом в многопоточных программах, ограничивая прирост производительности от многопоточности на многоядерных процессора.
Динамическая типизация
Кроме того, Python является динамически типизированным, а это означает, что вам не нужно объявлять тип переменной при ее инициализации.
Но как это влияет на эффективность?
Ну, в динамически типизированных языках типы определяются во время выполнения .
Это означает, что интерпретатору необходимо выполнять проверку типов каждый раз, когда он выполняет фрагмент кода .
Это требует дополнительной обработки для определения типа каждой переменной и того, как следует выполнять операции на основе этих типов.
А что является противоположностью динамически типизированных языков?
Статически типизированные языки!
В этом случае тип переменной известен во время компиляции, а не во время выполнения .
Как следствие, типы известны во время компиляции, и компиляторы могут лучше оптимизировать выполнение кода . Это приводит к более быстрым, но не таким гибким программам.
Некоторыми языками, использующими этот подход, являются C++ и Rust.
Сбор мусора
Сбор мусора — это форма автоматического управления памятью, которую использует система выполнения языка программирования для освобождения памяти, которая больше не используется программой .
Python автоматически управляет выделением и освобождением памяти для своих объектов посредством сборки мусора.
Основным методом, который он использует для сборки мусора, является подсчет ссылок. Каждый объект в Python имеет счетчик ссылок , который представляет собой количество ссылок, указывающих на него.
Когда счетчик ссылок падает до нуля, то есть ссылок на объект не остается, он немедленно удаляется из памяти.
Однако вывоз мусора – палка о двух концах …
Он значительно упрощает управление памятью за счет автоматической очистки неиспользуемых объектов, что помогает предотвратить утечки памяти и другие ошибки, связанные с ручным управлением памятью.
Но это приводит к накладным расходам и непредсказуемости, которые могут повлиять на производительность приложения .
Заключение
В заключение отметим четыре основные особенности, которые делают Python медленным:
- Интерпретируемое выполнение, которое добавляет уровень абстракции между кодом и машинным языком, замедляя выполнение по сравнению с скомпилированными языками.
- Глобальная блокировка интерпретатора , которая не позволяет многопоточным программам полностью использовать многоядерные процессоры.
- Динамическая типизация, определяющая тип объекта во время выполнения, что означает дополнительную работу интерпретатора во время выполнения.
- И, наконец, сборка мусора , поскольку сбор неиспользуемых объектов может увеличить задержку выполнения программы, особенно при работе с большим количеством объектов или сложными структурами данных.
❤️ Если вам понравилась статья, ставьте лайк и подписывайтесь на мой канал "Заходи в Ай-Ти".
👍 Если у вас остались вопросы или есть интересные темы, которые вы хотите, чтобы я разобрал, то пишите в комментариях. Ваше мнение очень важно для меня!