Введение
Язык программирования Python — это интерфейс, который можно реализовать различными способами. Примерами служат CPython, использующий язык C, Jython, реализованный на Java, и т. д.
Несмотря на свою популярность, CPython не самый быстрый. PyPy — это альтернативная реализация Python, которая одновременно совместима и быстра. PyPy использует JIT-компиляцию, что значительно сокращает время выполнения длительных операций.
В этом руководстве мы познакомим новичков с PyPy и выделим его отличия от CPython. Мы также обсудим его преимущества и ограничения. Затем мы рассмотрим, как скачать и использовать PyPy для запуска простого скрипта на Python.
В частности, данное обучение охватывает следующее:
- Краткий обзор CPython
- Введение в PyPy и его возможности
- Ограничения PyPy
- Запуск PyPy в Ubuntu
- PyPy против среды выполнения CPython
Краткий обзор CPython
Прежде чем обсуждать PyPy, важно понять, как работает CPython. Ниже представлен рисунок конвейера выполнения скрипта Python, реализованного с помощью CPython.
Исходный код скрипта Python (.py) сначала компилируется в байт-код с помощью компилятора CPython. Байт-код генерируется и сохраняется в файле с расширением pyc. Затем байт-код выполняется в виртуальной среде с помощью интерпретатора CPython.
Использование компилятора для преобразования исходного кода в байт-код имеет свои преимущества. Если компилятор не используется, интерпретатор работает непосредственно с исходным кодом и транслирует его строка за строкой в машинный код. Недостатком такого подхода является необходимость применения некоторых процессов для трансляции каждой строки исходного кода в машинный код, которые повторяются для каждой строки. Например, синтаксический анализ применяется к каждой строке независимо от других, поэтому интерпретатор тратит много времени на трансляцию кода. Компилятор решает эту проблему, поскольку может обрабатывать весь код сразу, и поэтому синтаксический анализ применяется только один раз, а не для каждой строки кода. Таким образом, байт-код, полученный компилятором, легко интерпретируется. Обратите внимание, что компиляция всего исходного кода может быть бесполезна в некоторых случаях, и мы увидим наглядный пример этого при обсуждении PyPy.
После генерации байт-кода он выполняется интерпретатором, работающим в виртуальной машине. Виртуальная среда выгодна тем, что отделяет байт-код CPython от машины, что делает Python кроссплатформенным.
К сожалению, простого использования компилятора для генерации байт-кода недостаточно для ускорения работы CPython. Интерпретатор транслирует код в машинный код при каждом выполнении. Таким образом, если выполнение строки занимает LX секунд, то её 10-кратное выполнение займёт X*10 секунд. Для длительных операций это очень затратно с точки зрения времени выполнения.
Основываясь на ошибках CPython, давайте теперь взглянем на PyPy.
Введение в PyPy и его возможности
PyPy — это реализация Python, похожая на CPython, которая одновременно соответствует стандартам и отличается высокой скоростью. “Совместимость” означает, что PyPy совместим с CPython, то есть вы можете использовать практически все команды CPython в PyPy. Как уже упоминалось, существуют некоторые различия в совместимости. Самое важное преимущество PyPy — это его скорость. PyPy намного быстрее CPython. Позже мы увидим несколько тестов, в которых PyPy работает примерно в 7 раз быстрее. В некоторых случаях он может быть даже в десятки или сотни раз быстрее CPython. Так как же PyPy достигает такой скорости?
Скорость
PyPy использует JIT-компилятор, который может значительно ускорить выполнение скриптов Python. В CPython используется компиляция с опережением (AOT), что означает, что весь код транслируется в байт-код перед выполнением. JIT-компиляция транслирует код только во время выполнения, только когда это необходимо.
Исходный код может содержать блоки кода, которые вообще не выполняются, но всё же транслируются компилятором AOT. Это приводит к замедлению обработки. Если исходный код большой и содержит тысячи строк, использование JIT имеет большое значение. При использовании AOT транслируется весь исходный код, что занимает много времени. При использовании JIT выполняются только необходимые части кода, что значительно ускоряет процесс.
После того, как PyPy переводит фрагмент кода, он кэшируется. Это означает, что код переводится только один раз и используется позже. Интерпретатор CPython повторяет перевод при каждом выполнении кода, что является ещё одной причиной его медленной работы.
Легко
PyPy — не единственный способ повысить производительность скриптов Python, но самый простой. Например, Cython можно использовать для ускорения присваивания переменным типов C. Проблема в том, что Cython требует от разработчика вручную просматривать исходный код и оптимизировать его. Это утомительно и усложняется по мере увеличения размера кода. Используя PyPy, вы просто запускаете обычный код Python гораздо быстрее, без каких-либо усилий.
Нет стека
Стандартный Python использует стек C. В этом стеке хранится последовательность функций, вызываемых друг из друга (возвращаемых значений). Поскольку размер стека ограничен, количество вызовов функций ограничено.
PyPy использует Stackless Python — реализацию Python, которая не использует стек C. Вместо этого вызовы функций хранятся в стеке вместе с объектами. Размер стека больше размера кучи, поэтому вы можете делать больше вызовов функций.
Stackless Python также поддерживает микропотоки, которые лучше обычных потоков Python. В потоке Stackless Python можно запускать тысячи задач, называемых «тасклетами», все из которых выполняются в одном потоке.
Использование тасклетов позволяет выполнять задачи одновременно. Параллелизм означает, что две задачи выполняются одновременно, используя одни и те же ресурсы. Одна задача выполняется некоторое время, а затем останавливается, чтобы освободить место для второй. Обратите внимание, что это отличается от параллелизма, который подразумевает выполнение двух отдельных, но одновременных задач.
Использование тасклетов сокращает количество создаваемых потоков, тем самым снижая накладные расходы операционной системы на управление всеми этими потоками. В результате ускорение выполнения за счёт переключения между двумя потоками занимает больше времени, чем переключение между двумя задачами.
Использование Stackless Python также проложило путь к продолжениям. Продолжения позволяют сохранять состояние задачи и восстанавливать его позже для продолжения. Обратите внимание, что Stackless Python ничем не отличается от стандартного Python. Он просто добавляет больше функциональности. Всё, что доступно в стандартном Python, будет доступно и в Stackless Python.
Обсудив преимущества PyPy, давайте в следующем разделе поговорим о его ограничениях.
Ограничения PyPy
В то время как CPython можно использовать на любой машине и с любой архитектурой процессора, PyPy имеет относительно ограниченную поддержку.
Вот архитектуры ЦП, поддерживаемые и обслуживаемые PyPy (источник):
- x86 (IA-32) и x86_64
- Платформы ARM (ARMv6 или ARMv7, с VFPv3)
- AArch64
- PowerPC 64 бит, как с прямым, так и с обратным порядком байтов
- Система Z (s390x)
PyPy работает не во всех дистрибутивах Linux, поэтому будьте внимательны и используйте один из поддерживаемых дистрибутивов. Запуск исполняемого файла PyPy для Linux в неподдерживаемом дистрибутиве приведёт к ошибке. PyPy поддерживает только одну версию Python 2 и Python 3: PyPy 2.7 и PyPy 3.6.
Если код, работающий в PyPy, написан на чистом Python, ускорение обычно значительно. Однако, если код содержит расширения C, такие как NumPy, PyPy может даже ускорить выполнение. Проект PyPy активно развивается и, следовательно, может обеспечить лучшую поддержку расширений C в будущем.
PyPy не поддерживается рядом популярных фреймворков Python, таких как Kivy. Kivy позволяет CPython работать на всех платформах, включая Android и iOS. Это означает, что PyPy не может работать на мобильных устройствах.
Теперь, когда мы рассмотрели преимущества и ограничения PyPy, давайте объясним, как запустить PyPy в Ubuntu.
Запуск PyPy в Ubuntu
Вы можете запустить PyPy на Mac, Linux или Windows, но мы рассмотрим запуск в Ubuntu. Важно отметить, что исполняемые файлы PyPy для Linux поддерживаются только определёнными дистрибутивами Linux. Вы можете ознакомиться с доступными исполняемыми файлами PyPy и поддерживаемыми ими дистрибутивами на этой странице. Например, PyPy (или Python 2.7 или Python 3.6) поддерживается только тремя версиями Ubuntu: 18.04, 16.04 и 14.04. Если у вас установлена последняя версия Ubuntu на данный момент (19.10), вы не сможете запустить PyPy. Попытка запустить PyPy в неподдерживаемом дистрибутиве приведёт к следующей ошибке:
Бинарные файлы PyPy поставляются в виде сжатых файлов. Всё, что вам нужно сделать, это распаковать скачанный файл. Внутри распакованной папки находится папка bin, в которой находится исполняемый файл PyPy. Я использую Python 3.6, поэтому файл называется pypy3. Для Python 2.7 он называется просто pypy.
Если вы хотите запустить Python 3 из терминала в CPython, просто введите команду python3 . Чтобы запустить PyPy, просто введите команду pypy3 .
Как показано на следующем рисунке, ввод команды pypy3 в терминале может вернуть сообщение ‘pypy3 не найден’. Это связано с тем, что путь к PyPy не был добавлен в переменную окружения PATH. Фактически работает команда ./pypy3, учитывая, что текущий путь в терминале находится внутри каталога bin PyPy. Точка . указывает на текущий каталог, а символ / добавляется для доступа к объекту в текущем каталоге. Выполнение команды ./pypy3 успешно выполняет Python.
Теперь вы можете работать с Python как обычно, используя PyPy. Например, мы можем создать простой скрипт Python, который складывает 1000 чисел, и запустить его с помощью PyPy. Код выглядит следующим образом.
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)Если этот скрипт называется test.py, вы можете просто запустить его с помощью следующей команды (предполагается, что файл Python находится внутри папки bin PyPy, которая находится там же, где и команда pypy3).
./pypy3 test.py
На следующем рисунке показан результат выполнения предыдущего кода.
PyPy против среды выполнения CPython
Чтобы сравнить время выполнения PyPy и CPython для суммы 1000 чисел, код для измерения времени изменяется следующим образом.
import time
t1 = time.time()
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)
t2 = time.time()
t = t2 - t1
print("Elapsed time is : ", t, " seconds")Для PyPy время близко к 0,00045 секунды по сравнению с 0,0002 секунды для CPython (я запустил код на своём компьютере с Core i7-6500U на частоте 2,5 ГГц). В этом случае CPython выполняет задачу быстрее, чем PyPy, что вполне ожидаемо, поскольку это не такая уж длительная задача. Если бы код выполнял сложение 1 миллиона чисел вместо 1000, PyPy в конечном итоге победил бы. В этом случае PyPy выполнил бы задачу за 0,00035 секунды, а CPython — за 0,1 секунды. Преимущество PyPy теперь очевидно. Это должно дать вам представление о том, насколько медленнее CPython справляется с длительными задачами.
Результат
В этом руководстве мы познакомимся с PyPy, самой быстрой реализацией Python. Главное преимущество PyPy — JIT-компиляция, которая кэширует скомпилированный машинный код, предотвращая его повторное выполнение. Также будут рассмотрены ограничения PyPy, главное из которых заключается в том, что он хорошо работает с чистым кодом Python, но неэффективен для расширений на языке C.
Мы также посмотрели, как PyPy работает в Ubuntu, и сравнили время выполнения CPython и PyPy, отметив производительность PyPy при выполнении длительных задач. В то же время, CPython может превзойти PyPy при выполнении краткосрочных задач. Мы рассмотрим более подробное сравнение PyPy, CPython и Cython в следующих статьях.












