Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Moscow Python Meetup №86. Александр Гончаров (Reef Technologies, Senior Python Developer). Альтернативные питоны: что нового, и стоит ли оно того?

Moscow Python Meetup №86. Александр Гончаров (Reef Technologies, Senior Python Developer). Альтернативные питоны: что нового, и стоит ли оно того?

Всегда существовало множество альтернативных реализаций Python, и хотя cpython развивается, развиваются и альтернативы. В этом докладе мы пройдемся по самым популярным и современным из них, посмотрим, что они предлагают, какие проблемы решают и, наконец, для каждого из них найдем ответ на вопрос: "Стоит ли мне перейти с cpython на X прямо сейчас?"

Видео: https://youtu.be/xURoyC_qBlE

Moscow Python: http://moscowpython.ru
Курсы Learn Python: http://learn.python.ru
Moscow Python Podcast: http://podcast.python.ru
Заявки на доклады: https://bit.ly/mp-speaker

Moscow Python Meetup

November 22, 2023
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Обо мне • Александр Гончаров • Работаю в Reef Technologies

    • Пишу на Django 10+ лет • Пишу смешные и познавательные статьи
  2. Почему доклад? • Попробовали PyInstaller и погорели • Переключились на

    3.9-nogil и получили 10x ускорение • Взяли mypyc и значительно ускорили код • Сказали: Смотри, mojo! Смотри, codon! Однажды у нас на работе
  3. А я всё ещё на CPython! • Может, я отстал

    от жизни? • А какие вообще инструменты есть? • Лучший способ – попробовать самому • Делюсь тем, что узнал
  4. Дисклеймер • “Альтернативные” питоны – в широком смысле • 20

    минут – мало, пробежимся “по верхам” • Хотите больше? → Ждите статью • Сегодня без Cython, PyPy итд – они на слуху • Субъективный опыт
  5. Зачем вообще другие питоны? • Питон – классный: богатая экосистема,

    выразительный синтаксис • Но не во всём! • Каждая реализация пытается решить какой-то набор проблем - например, скорость, отсутствие честных потоков, низкоуровневых вещей итд
  6. На чём тестировать • У меня есть CPU-bound программа на

    чистом питоне • Написна в стиле “Interstellar” • Хотелось бы ускорить
  7. Зачем? • Позиционируется в первую очередь как язык для AI

    developers • Чтобы можно было писать на питоне++, и чтоб написанное было быстрым, как C++ • Type checking • Надмножество питона → экосистема питона
  8. Что ты такое? • Mojo - это не питон, он

    не похож на него • Чтобы запускать питон-код из-под Mojo, используется самый обычный cpython! • То есть на Mojo можно запускать питон-код, но это ничего не даст в плане производительности - нужно только ради экосистемы • Обещают утилиту для автоматической конвертации питон-кода в Mojo
  9. Чем отличается от питона? • Есть строгие типы (Int, F32)

    • Нет list / dict • Нет lambda • Нет генераторов
  10. Чем отличается от питона? • Нет классов (есть “строгие” struct,

    наследование не работает) • Exception → Error, но это не замена – нет stack trace; полиморфизма нет, поэтому нет подклассов Error • Есть async def, но нет async for and async with
  11. Чем отличается от питона? • def foo(arg1) - arg1 передаётся

    по значению (то есть делается копия) • fn foo(arg1) - arg1 передаётся по ссылке, + immutable • Нужен mutable аргумент? → fn foo(inout self)
  12. Как попробовать? • В пакетных менеджерах нет • Официального докер-образа

    тоже нет (есть неофиц. устаревший - 0.2) • Придётся ставить ручками
  13. Как попробовать? • Попробовал поставить на локалхост - таймаут при

    загрузке Mojo • Иконка огня символизирует, как у вас будет подгорать при установке и использовании Mojo
  14. Как попробовать? • На локальный комп вообще не ставится никак,

    зашёл по ssh на один из своих серверов и стал играться там
  15. Как попробовать? • Скорее коммитим изменения в докер образ •

    Пакуем, пересылаем его на локалхост • Распаковываем на локалхосте • Ура! Наконец-то можно запустить Mojo!
  16. Пытаюсь запустить код Интеграция с питоном работает: from python import

    Python def main(): let requests = Python.import_module('requests') let response = requests.get("https://www.google.com") print(response.status_code)
  17. Пытаюсь запустить код • Словарей нет! В Mojo пока нет

    словарей… Но зато в Mojo вы можете использовать словари из питона!
  18. Пытаюсь запустить код from python import Python from python.object import

    PythonObject fn use_dict() raises: let dictionary = Python.dict() dictionary["fruit"] = "apple" dictionary["starch"] = "potato" let keys: PythonObject = ["fruit", "starch", "protein"] let N: Int = keys.__len__().__index__() print(N, "items") for i in range(N): if Python.is_type(dictionary.get(keys[i]), Python.none()): print(keys[i], "is not in dictionary") else: print(keys[i], "is included") Это ли мой любимый питон?!
  19. Пытаюсь запустить код let arr = [1, 2, 3, 4,

    5] for el in arr: print(el) /src/test.mojo:2:11: error: 'Tuple[Int, Int, Int, Int, Int]' does not implement the '__iter__' method for el in arr: ^ /src/test.mojo:3:5: error: TODO: expressions are not yet supported at the file scope level print(el) ^ • Даже самый простой код не заработает! Питон: Питоmojo: Mojo: from python.object import PythonObject def main(): var arr: PythonObject = [1, 2, 3] for el in arr: print(el) let arr = SIMD[DType.int32, 8](1, 2, 3, 4, 5, 6, 7, 8) for i in range(8): print(arr[i])
  20. Вывод • Mojo - не питон. Нельзя взять код на

    питоне и запустить его как mojo • Можно написать код на mojo и из него запустить код на питоне, но быстрее не станет
  21. Зачем? • Чтобы быть максимально похожим на питон • Чтобы

    скомпилировать питон и быть таким же быстрым, как C/C++ • И при этом унаследовать экосистему питона • Чтобы был настоящий multithreading, удобный multiprocessing итд
  22. Что оно делает? • Codon - не питон, это скорее

    брат-близнец питона, у которого только то, что можно скомпилировать • По задумке, должен быть почти совместим с питоном, так что питон-код нужно лишь немного изменить, чтобы запустить на codon
  23. Чем отличается от питона? • Питон с лёгким шармом статической

    типизации • int - это 64-bit signed integer; но можно использовать Int[N] • Строки - не unicode, а ASCII • Словари не сохраняют порядок ключей • Нет @classmethod • Нет "динамических" фич, типа манки-патчинга классов в рантайме или гетерогенных коллекций
  24. Как попробовать? • На Linux и MacOS - наш любимый

    "curl из сети" (простите!): /bin/bash -c "$(curl -fsSL https://exaloop.io/install.sh)" • Для Arch есть AUR package
  25. Пытаюсь запустить код Есть два варианта: • Pro: писать на

    Codon и вызывать функции из python (библиотеки тоже поддерживаются) • Lite: писать на python и вызывать JIT из Codon
  26. Pro (codon + python) Codon – это питон-совместимый язык, и

    многие программы на питоне заработают на Codon в минимальными изменениями или вообще без них. Если вы знаете Python, вы уже знаете Codon на 99%. • Ура! Смотрите, сейчас senior codon developer запустит программу на питоне и получит x100 прирост производительности!
  27. Pro (codon + python) > codon run utils.py utils.py:1:6-16: error:

    no module named '__future__' collections.codon:218:25-32: error: name 'heapify' is not defined utils.py:5:6-16: error: no module named 'contextlib' Ну понятно...
  28. Pro (codon + python) import re # ok 63:1:8-15: error:

    no module named 'logging' :4:23-28: error: cannot import name 'wraps' from 'functools' :2:38-46: error: cannot import name 'UserList' from 'collections' • Stdlib можно импортировать, он переписан на codon • Но не всегда: что-то есть, чего-то нет
  29. Pro (codon + python) from python import logging logging.basicConfig(level=logging.INFO) logging.info('HELLO

    WORLD') • Чего нет - можно взять из питона • Импорты питонячьих либ вот такие: from python import XXX. Раздражает, но понятно.
  30. Pro (codon + python) • Как узнать, что можно импортировать?

    А вот как: На данный момент, чтобы получить список поддерживаемых модулей, вы можете посмотреть содержимое директории “stdlib”
  31. Pro (codon + python) from itertools import islice def iter_fibonacci():

    a, b = 0, 1 while True: yield a a, b = b, a + b if __name__ == '__main__': print(list(itertools.islice(iter_fibonacci(), 10))) • Вот так работает и превращается в бинарник. Прикольно!
  32. Pro (codon + python) @python def get_response(): import requests response

    = requests.get('https://google.com') response.raise_for_status() return response if __name__ == '__main__': print(get_response().status_code) • Если не работает никак, можно просто сделать fallback в обычный питон:
  33. Pro (codon + python) def foo(x: List[T], T: type): print(x)

    foo([1, 2]) foo(['s', 'u'], int) # fails! def foo(x, R: type) -> R: print(x) return 1 foo(4, int) # prints 4, returns 1 foo(4, str) # error: return type is str, but foo returns int! • Как быть с type annotations? Type Var? • Круто, проверка типов из коробки!
  34. Pro (codon + python) class Base: a: int def get_value(self)

    -> int: return self.a def __str__(self): return str(self.get_value()) • Наследование пока официально в бета- режиме. Но у меня всё заработало! class Child(Base): b: int def get_value(self) -> int: return self.a + self.b if __name__ == '__main__': print(Child(a=1, b=2))
  35. Pro (codon + python) @extend class defaultdict: def __missing__(self, key):

    ret = self[key] = self.default_factory(key) return ret # dict.codon:18:16-17: error: name 'K.1' is not defined Codon выводит детальные сообщения об ошибках, чтобы помочь программисту найти и разрешить любые несоответствия • Ну как сказать...
  36. Pro (codon + python) > cat codon/stdlib/internal/types/collections/dict.codon | grep -i

    "K.1" Ни-че-го! class Dict: ... _flags: Ptr[u32] _keys: Ptr[K] <--- вот тут! _vals: Ptr[V] • Google не сказал мне ничего • Такова участь новых языков - StackOverflow вам не поможет :(
  37. Lite (python + codon jit) @codon.jit def fn(...): from .codon_jit

    import JITWrapper, JITError, codon_library ImportError: /home/user/workspace/alt-pythons/pure-python/venv-3.10.13/lib/python3.10/site- packages/codon/codon_jit.cpython-310-x86_64-linux-gnu.so: undefined symbol: _ZN5codon3jit7jitInitERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE • Магический декоратор @codon.jit компилирует отдельные функции • Ставится через pip (но кодон должен быть в системе): CODON_DIR=/opt/codon-deploy pip install codon-jit • Я попытался запустить, в ответ получил что-то вроде "авада кедавра":
  38. Вывод • Не питон, но мимикрирует под него, и делает

    это хорошо • Есть отличия, но терпимо и можно понять • Переписать питон на codon вполне можно • Но можно не переписывать и вызывать питонячие функции, работает • Если вам нравится стиль питона, но с компиляцией - хороший вариант
  39. Зачем? • Компилирует питонячьи модули в программу на C, и

    с добавлением libpython выполняет код так же, как и CPython • Получается один файл, а-ля нативная программа
  40. Nuitka что-то оптимизирует? Да. • Старается подсчитать константы на этапе

    компиляции • Оптимизирует built-in lookups (например, ValueError) • Предугадывает результат при компиляции: type("string"), range(3, 9, 2), len([1, 2]) (но range(100000) не подсчитывается) • Убирает недостижимые ветки, если это видно по каким-то константам • `for x in [a, b, c]` -> `for x in (a, b, c)` Имхо, микро-оптимизации. Не deal breaker.
  41. Чем отличается от питона? • Совместима со всеми версиями питона

    • Можно использовать всё что есть в питоне • Компилируется в бинарник
  42. Пытаюсь запустить код (venv-3.8.18) > python -m timeit -r 2

    -n 30 ... 30 loops, best of 2: 4.4 sec per loop Nuitka: 4.43s/loop Nuitka + optimization flags: 3.9s/loop • Работает прям сразу! Флаги --lto=yes и -- pgo должны делать быстрее. • Потом я скормил главный модуль, который имел зависимые модули. Он сразу скомпилировался без ошибок!
  43. Вывод • Если нужно превратить ваш питон-код в бинарь -

    то это то, что доктор прописал! • Вообще не надо ничего делать, just works
  44. Чем отличается от питона? • Для pyston нет pre-compiled пакетов,

    поэтому то, что требуется собирать - придётся собирать • Не все версии поддерживаются
  45. Как попробовать? Lite-версия: • только JIT • 3.7-3.10 • на

    ~10% быстрее cpython • pip install pyston_lite_autoload и всё! • Сам запускается
  46. Как попробовать? Pro-верcия • Форк CPython 3.8.12, с блэкджеком с

    JIT и другими оптимизациями • Говорят, что на бенчмарках на 30% быстрее cpython • Ставится легко: pyenv install pyston-2.3.5
  47. Пытаюсь запустить код # def from_start(indexes: tuple[int]) -> tuple[int]: #

    E TypeError: 'type' object is not subscriptable from __future__ import annotations • Переписываем всё на python 3.8 в стиле "ой мне лень"
  48. Пытаюсь запустить код # E ImportError: cannot import name 'cache'

    from 'functools' from functools import lru_cache as cache • Переписываем всё на python 3.8 в стиле "ой мне лень"
  49. Пытаюсь запустить код # E ImportError: cannot import name 'pairwise'

    from 'itertools' pairwise = lambda items: zip(items, items[1:]) • Переписываем всё на python 3.8 в стиле "ой мне лень"
  50. Пытаюсь запустить код # E match partial: # E ^

    # E SyntaxError: invalid syntax if partial is False: • Переписываем всё на python 3.8 в стиле "ой мне лень"
  51. Пытаюсь запустить код # Value = TypeVar('Value', bound=int | float

    | Pattern | Gap) # E TypeError: unsupported operand type(s) for |: 'type' and 'type' Value = TypeVar('Value') # пофиг ваще, удаляем • Переписываем всё на python 3.8 в стиле "ой мне лень"
  52. Пытаюсь запустить код # @dataclass(slots=True) # E TypeError: dataclass() got

    an unexpected keyword argument 'slots' @dataclass # да никто и не заметит! удаляем slots=True • Переписываем всё на python 3.8 в стиле "ой мне лень"
  53. Пытаюсь запустить код (venv-3.8.18) > python -m timeit -r 2

    -n 30 ... 30 loops, best of 2: 4.4 sec per loop (venv-3.8-pyston-lite) > python -m timeit -r 2 -n 30 ... 30 loops, best of 2: 3.71 sec per loop (venv-pyston-2.3.5) > python -m timeit -r 2 -n 30 ... 30 loops, best of 2: 2.14 sec per loop • И всё работает!
  54. Вывод • Если вы привыкли к новым фишечкам 3.9 и

    выше, то даунгрейд до питона 3.8.12 бесит • На чистом питонячьем коде работает! На джанге работает! Всякие pillow и прочее - работают! • Реально быстрее • Можно поставить прям через pyenv в одну команду • Питон 3.8 - это не весело! • Но PyPy всё равно быстрее и поддерживает 3.10 :)
  55. Банальные выводы • На словах ты Лев Толстой, • ...

    а на деле как повезёт. • Чтобы понять, хайп перед вами или годнота, нужно пробовать.
  56. Выбираем инструмент под задачу Мы рассмотрели: • mojo - новый

    компилируемый язык + python • codon - компилируемый близнец питона + python • nuitka - python → bin • pyston “lite” - python + JIT • pyston “pro” - drop-in замена питона
  57. Выбираем инструмент под задачу • Не обязательно знать "в глубь",

    но вот знать "в ширь" - крайне полезно. • В нашей компании мы пользовались mypyc и python-nogil, после моего исследования мы попробуем nuitka.
  58. That’s all, folks! Пообщаться / почитать: Группа в телеге "Блог

    Погромиста", там есть мои контакты и статьи