Предположим, вы разработали приложение или библиотеку на Python и уже готовитесь передать его / её заказчику. И в этот момент возникают вопросы, о которых многие даже не задумываются.
Во-первых, так может оказаться, что вы разработали супер крутой алгоритм, которого ни у кого нет, и показывать его хочется только избранным.
Во-вторых, возникает вопрос окружения - хочется быть уверенным, что заказчик справится с установкой правильной версии Python и всех вспомогательных библиотек, но это не всегда простая задача. Было бы удобно упаковать приложение в автономный исполняемый файл.
И, наконец, хочется, чтобы конечное приложение работало быстрее, чем в среде разработки.
И вот тут настало время скомпилировать Python-код. Меня зовут Руслан, я старший разработчик компании «Цифровое проектирование». Сегодня я расскажу, как выбрать тот самый компилятор из множества доступных.
def approximate_pi:
step = 1.0 / n
result = 0
for i in range:
x = (i + 0.5) * step
result += 4.0 / (1.0 + x * x)
return step * result
Эксперименты будем проводить на процессоре Intel Core i7 10510U. На CPython 3.9.7 время вычисления числа пи до 100.000.000 знака заняло 5.82 секунды.
PyInstaller упаковывает приложения Python в автономные исполняемые файлы в Windows, GNU / Linux, Mac OS X, FreeBSD, Solaris и AIX.
Устанавливается через pip:
pip install pyinstaller
После установки для создания исполняемого файла достаточно выполнить команду:
pyinstaller <имя_файла>.py
В результате будет создано:
PyInstaller знает о многих Python-пакетах и умеет их учитывать при сборке исполняемого файла. Но не о всех. Например, фреймворк uvicorn практически весь нужно явно импортировать в файл, к которому будет применена команда pyinstaller. Полный список поддерживаемых из коробки пакетов можно посмотреть здесь.
Cython
Cython - это оптимизирующий статический компилятор как для языка программирования Python, так и для расширенного языка программирования Cython. С его помощью можно код на Python транслировать в С и затем скомпилировать в бинарник, совместимый с CPython. Компиляцию придется делать под все операционные системы и архитектуры процессора.
Ставится Cython через pip:
pip install Cython
Рассмотрим его работу на примере с вычислением числа пи:
Создадим файл build.py:
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize("bench_cython.pyx"),
)
Запустим: python build.py build_ext –-inplace
В результате будет сгенерирован .so/.dll файл.
Nuitka
Nuitka способна упаковывать приложения Python в автономные исполняемые файлы, а также транслировать Python-код в С для его последующей компиляции. Работает с разными версиями Python (2.6, 2.7, 3.3 - 3.9).
Ставится через pip:
pip install nuitka
Для генерации исполняемого файла достаточно выполнить команду:
python -m nuitka --follow-import some_program.py
Для компиляции модуля:
python -m nuitka --module some_module.py
Для компиляции пакета:
python -m nuitka --module some_package --include-package = some_package
Pythran
Pythran – статический компилятор Python, позиционирующий себя как ориентированный на научные вычисления и использующий преимущества многоядерных процессоров и блоков инструкций SIMD. Он транслирует Python-код, аннотированный описаниями интерфейса, в C++. До версии 0.9.5 (включительно) Pythran поддерживал Python 3 и Python 2.7. Последние версии поддерживают только Python 3.
Установка:
pip install pythran
Генерируем бинарный файл <имя файла>.so:
pythran <имя файла>.py
Pythran по умолчанию не умеет в пользовательские классы, поэтому при попытке их компиляции будет выброшена ошибка:
#pythran export approximate_pi(int)
def approximate_pi:
step = 1.0 / n
result = 0
for i in range:
x = (i + 0.5) * step
result += 4.0 / (1.0 + x * x)
return step * result
Скомпилируем и бенчмарк выдает 0,00007 секунды.
cx-Freeze
cx-Freeze – это набор скриптов и модулей преобразования скриптов Python в исполняемые файлы. cx_Freeze - кроссплатформенный. Он поддерживает Python 3.5.2 и выше.
Ставится с помощью pip:
pip install cx_Freeze
Для генерации исполняемого файла достаточно выполнить команду:
cxfreeze <имя файла>.py
Сборку можно настроить с помощью параметров командной строки:
import sys
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {"packages": ["os"], "excludes": ["tkinter"]}
# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
base = "Win32GUI"
setup( name = "guifoo",
version = "0.1",
description = "My GUI application!",
options = {"build_exe": build_exe_options},
executables = [Executable("guifoo.py", base=base)])
Сборка исполняемого файла:
python setup.py build
PyPy
PyPy - интерпретатор языка программирования Python 2.7 и Python 3.7. Он написан на RPython и содержит:
Основные особенности (сравнение с CPython):
PyPy работает на Mac, Linux (не все дистрибутивы) или Windows.
Для запуска кода с помощью PyPy вместо команды python3 (как c помощью CPython) достаточно воспользоваться командой pypy3:
pypy3 something.py
Pyston
Pyston - это форк CPython 3.8.8 с дополнительной оптимизацией производительности. В настоящее время он поддерживает установку только из исходников. Или с помощью pyenv.
В Pyston поддерживаются все возможности CPython, в том числе C API для разработки расширений на языке Си. Среди основных отличий Pyston от CPython помимо общих оптимизаций выделяется использование DynASM JIT и inline-кэширования.
Среднее относительное время выполнения кода на различных версиях CPython, интерпретаторах с поддержкой JIT-компиляции и после сборки на фреймворках AOT-компиляции:
Во-первых, так может оказаться, что вы разработали супер крутой алгоритм, которого ни у кого нет, и показывать его хочется только избранным.
Во-вторых, возникает вопрос окружения - хочется быть уверенным, что заказчик справится с установкой правильной версии Python и всех вспомогательных библиотек, но это не всегда простая задача. Было бы удобно упаковать приложение в автономный исполняемый файл.
И, наконец, хочется, чтобы конечное приложение работало быстрее, чем в среде разработки.
И вот тут настало время скомпилировать Python-код. Меня зовут Руслан, я старший разработчик компании «Цифровое проектирование». Сегодня я расскажу, как выбрать тот самый компилятор из множества доступных.
AOT/JIT
Компиляция – это сборка программы, включающая: трансляцию всех модулей программы, написанных на языке программирования высокого уровня, в эквивалентные программные модули на низкоуровневом языке, близком к машинному коду, или на машинном языке и сборку исполняемой программы. Существует два вида компиляции:- AOT-компиляция (ahead-of-time) – компиляция перед исполнением программы. Т.е. программа компилируется один раз, в результате компиляции получается исполняемый файл.
- JIT-компиляция (just-in-time) – компиляция во время исполнения программы. Т.е. программа (а точнее, блоки программы) компилируется много раз - при каждом запуске.
Бенчмарк
Так как одной из целей является ускорение, необходимо оценить, насколько быстро работает скомпилированный код. В качестве бенчмарка будем использовать pyperfomance. К сожалению, pyperfomance не подошел для Cython и Pythran, потому что не позволяет визуализировать все возможности языка. Ускорения для Cython без модификации кода получить не удалось, а Pythran не умеет в пользовательские классы. Для них воспользуемся вычислением числа пи:def approximate_pi:
step = 1.0 / n
result = 0
for i in range:
x = (i + 0.5) * step
result += 4.0 / (1.0 + x * x)
return step * result
Эксперименты будем проводить на процессоре Intel Core i7 10510U. На CPython 3.9.7 время вычисления числа пи до 100.000.000 знака заняло 5.82 секунды.
AOT-компиляция Python
PyInstallerPyInstaller упаковывает приложения Python в автономные исполняемые файлы в Windows, GNU / Linux, Mac OS X, FreeBSD, Solaris и AIX.
Устанавливается через pip:
pip install pyinstaller
После установки для создания исполняемого файла достаточно выполнить команду:
pyinstaller <имя_файла>.py
В результате будет создано:
- *.spec – файл спецификации (используется для ускорения будущих сборок приложения, связи файлов данных с приложением, для включения .dll и .so файлов, добавление в исполняемый файл параметров runtime-а Python);
- build/ – директория с метаданными для сборки исполняемого файла;
- dist/ – директория, содержащая все зависимости и исполняемый файл.
- --name – изменение имени исполняемого файла (по умолчанию, такое же, как у сценария);
- --onefile – создание только исполняемого файла (по умолчанию, папка с зависимостями и исполняемый файл);
- --hidden-import – перечисление импортов, которые PyInstaller не смог обнаружить автоматически;
- --add-data – добавление в сборку файлов данных;
- --add-binary – добавление в сборку бинарных файлов;
- --exclude-module – исключение модулей из исполняемого файла;
- --key – ключ шифрования AES256. Да, приложение не будет содержать исходного кода, но его можно декомпилировать, например, так: Pyinstaller Extractor (.exe → .pyc) и uncompile6 (.pyc → .py). Для скрытия исходного кода можно обфусцировать байт-код Python с помощью шифрования.
PyInstaller знает о многих Python-пакетах и умеет их учитывать при сборке исполняемого файла. Но не о всех. Например, фреймворк uvicorn практически весь нужно явно импортировать в файл, к которому будет применена команда pyinstaller. Полный список поддерживаемых из коробки пакетов можно посмотреть здесь.
Cython
Cython - это оптимизирующий статический компилятор как для языка программирования Python, так и для расширенного языка программирования Cython. С его помощью можно код на Python транслировать в С и затем скомпилировать в бинарник, совместимый с CPython. Компиляцию придется делать под все операционные системы и архитектуры процессора.
Ставится Cython через pip:
pip install Cython
Рассмотрим его работу на примере с вычислением числа пи:
- Немного модифицируем нашу функцию:
def approximate_pi(int n):
cdef float step
cdef float result
cdef float x
step = 1.0 / n
result = 0.0
for i in range:
x = (i + 0.5) * step
result += 4.0 / (1.0 + x * x)
return step * result
- Cython → C:
cython -2 pi_approximater.pyx -o pi_approximater.c - Компилируем С-шный код:
gcc -g -O2 -shared -o pi_approximater.so pi_approximater.c python-config --includes --ldflags -fPIC - И замеряем время на бенчмарке: 3,66 секунды.
Создадим файл build.py:
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize("bench_cython.pyx"),
)
Запустим: python build.py build_ext –-inplace
В результате будет сгенерирован .so/.dll файл.
Nuitka
Nuitka способна упаковывать приложения Python в автономные исполняемые файлы, а также транслировать Python-код в С для его последующей компиляции. Работает с разными версиями Python (2.6, 2.7, 3.3 - 3.9).
Ставится через pip:
pip install nuitka
Для генерации исполняемого файла достаточно выполнить команду:
python -m nuitka --follow-import some_program.py
Для компиляции модуля:
python -m nuitka --module some_module.py
Для компиляции пакета:
python -m nuitka --module some_package --include-package = some_package
Pythran
Pythran – статический компилятор Python, позиционирующий себя как ориентированный на научные вычисления и использующий преимущества многоядерных процессоров и блоков инструкций SIMD. Он транслирует Python-код, аннотированный описаниями интерфейса, в C++. До версии 0.9.5 (включительно) Pythran поддерживал Python 3 и Python 2.7. Последние версии поддерживают только Python 3.
Установка:
pip install pythran
Генерируем бинарный файл <имя файла>.so:
pythran <имя файла>.py
Pythran по умолчанию не умеет в пользовательские классы, поэтому при попытке их компиляции будет выброшена ошибка:
Добавим комментарий аннотации функции:Top level statements can only be assignments, strings,functions, comments, or imports
#pythran export approximate_pi(int)
def approximate_pi:
step = 1.0 / n
result = 0
for i in range:
x = (i + 0.5) * step
result += 4.0 / (1.0 + x * x)
return step * result
Скомпилируем и бенчмарк выдает 0,00007 секунды.
cx-Freeze
cx-Freeze – это набор скриптов и модулей преобразования скриптов Python в исполняемые файлы. cx_Freeze - кроссплатформенный. Он поддерживает Python 3.5.2 и выше.
Ставится с помощью pip:
pip install cx_Freeze
Для генерации исполняемого файла достаточно выполнить команду:
cxfreeze <имя файла>.py
Сборку можно настроить с помощью параметров командной строки:
- -h, --help - справка;
- -O - оптимизировать сгенерированный байт-код согласно PYTHONOPTIMIZE;
- -c, --compress - сжать байт-код в zip-файлах;
- -s, --silent - выводить только предупреждения и ошибки;
- --target-dir=DIR, --install-dir=DIR - каталог, в который следует поместить целевой файл и все зависимые файлы.
import sys
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {"packages": ["os"], "excludes": ["tkinter"]}
# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
base = "Win32GUI"
setup( name = "guifoo",
version = "0.1",
description = "My GUI application!",
options = {"build_exe": build_exe_options},
executables = [Executable("guifoo.py", base=base)])
Сборка исполняемого файла:
python setup.py build
JIT-компиляция Python
JIT-компиляция не позволяет скрывать исходники или создавать автономный исполняемый файл, но дает возможность значительно ускорить выполнение программы.PyPy
PyPy - интерпретатор языка программирования Python 2.7 и Python 3.7. Он написан на RPython и содержит:
- компилятор байт-кода, отвечающий за создание объектов кода Python из исходного кода пользовательского приложения;
- оценщик байт-кода, ответственный за интерпретацию объектов кода Python;
- стандартное объектное пространство, отвечающее за создание и управление объектами Python, видимыми приложением.
Основные особенности (сравнение с CPython):
- Скорость. При выполнении длительно выполняющихся программ, когда значительная часть времени тратится на выполнение кода Python, PyPy может значительно ускорить ваш код.
- Использование памяти. Программы Python, требующие много памяти (несколько сотен Мб или более), могут занимать меньше места, чем в CPython. Однако это не всегда так, поскольку зависит от множества деталей. Также базовый уровень потребления оперативной памяти выше, чем у CPython.
PyPy работает на Mac, Linux (не все дистрибутивы) или Windows.
Для запуска кода с помощью PyPy вместо команды python3 (как c помощью CPython) достаточно воспользоваться командой pypy3:
pypy3 something.py
Pyston
Pyston - это форк CPython 3.8.8 с дополнительной оптимизацией производительности. В настоящее время он поддерживает установку только из исходников. Или с помощью pyenv.
В Pyston поддерживаются все возможности CPython, в том числе C API для разработки расширений на языке Си. Среди основных отличий Pyston от CPython помимо общих оптимизаций выделяется использование DynASM JIT и inline-кэширования.
Заключение
Итак, мы рассмотрели 5 фреймворков AOT-компиляции Python. Для любителей аналитики, ниже приведена таблица со сравнительным анализом.PyInstaller | Cython | Nuitka | Pythran | cx-Freeze | |
Генерация автономных исполняемых файлов | + | - | + | - | + |
Компиляция python-модуля в исполняемый файл, совместимый с CPython | - | + | + | + | + |
Компиляция байт-кода Python | + | - | + | - | + |
Трансляция Python в С/C++ | - | + | + | + | - |
Кроссплатформенность | + | + | + | + | + |
Кросскомпиляция | - | - | - | - | - |
Список литературы
- http://www.pyinstaller.org/
- https://cython.org/
- https://nuitka.net/
- https://github.com/serge-sans-paille/pythran
- https://cx-freeze.readthedocs.io/en/latest/
- https://github.com/pyston/pyston
- https://www.pypy.org/
Компиляция Python
Предположим, вы разработали приложение или библиотеку на Python и уже готовитесь передать его / её заказчику. И в этот момент возникают вопросы, о которых многие даже не задумываются. Во-первых,...
habr.com