Как стать автором
Обновить

Комментарии 37

Очень годно. Меня гложет любопытство -- а что насчёт async/await? Что насчёт интеграций с другими сишными расширениями? Могу ли я упороться задекорировать какой-нибудь asyncpg?

Учитывая GIL в стандартной реализации Питона, перевод асинхронности в настоящую был бы очень уместен. Однако, готовых решений по реализации многопоточности с интерфейсом как в Питоне, насколько я знаю, в C++ нет. Async присутствует в стандарте C++20, но я не уверен, что это то, что нужно.

Любое сишное расширение может быть интегрировано в jit-компилятор, если имеет прямой аналог в Питоне, например, модуль cmath. В более сложных случаях поддержка модулей заключается в ручном переводе в сишные эквиваленты. Именно поэтому даже такие большие проекты, как RPython и PyPy не поддерживают всего разнообразия пакетов. Если в библиотеках встречаются вставки на других ЯП, то всё опять же усложняется, так как придётся делать целую цепочку вызовов динамических библиотек.

С оптимизацией библиотеки и так неплохо справляются, когда включают в себя модули на более производительных языках. Например Numpy содержит код на Фортране, потому что так быстрее. Ускорять Numpy с помощью jit не представляется необходимым.

В моём решении не реализовано преобразование объектов и структур данных, а без этого реализовывать многопоточность было бы проблематично. Заниматься преобразованием прикладных библиотек, таких как asyncpg, имеющих свойство менять API при каждой новой значительной версии, можно заниматься на свой страх и риск никогда не закончить.

не очень знаком с питоном, но вроде std::async вполне есть в С++11 ?

С Numpy не так все очевидно. Стоит посмотреть доклад Олега Хачумова на PyCon Russia 2022. Кстати, там пакет Taichi внезапно уделал Numba, правда, только на числах Фибоначчи, остальное не сравнивалось.

А ещё в LLVM есть инфраструктура MLIR для создания промежуточных представлений (IR), и там есть диалект “async”. Учитывая, что диалекты можно смешивать в одном IR, можно попробовать решить этот вопрос на уровне компилятора. Впрочем, там и для циклов есть отдельный диалект, и много для чего ещё. Порог для входа, впрочем, повыше, чем голый питон.

Невероятно круто, если это реально сделано студентом и за две недели!

Спасибо. На самом деле, это примерное время разработки. У студентов рабочий день не нормированный, поэтому я руководствовался при подсчёте датами своих коммитов. Для предмета на зачёт больше времени не нашлось)

Отличный результат! Успехов!

Правильно ли я понимаю, что такие особенности питона, как автоувеличение размера целочисленных переменных или истинное деление не реализованы, что приведет к отличию в результатах?

И ещё вопрос - у вас везде упоминается C++, не С, а что конкретно от С++ вам пришлось использовать?

А так круто вышло.

Автоувеличение, или длинная арифметика, действительно не были реализованы, но программисты на C++, как правило, обходятся без неё. Обычно тип данных определяется задачей. Например, число дней в году рациональнее было бы кодировать типом short. Чтобы не происходило переполнения типов, необходимо правильно выбрать тип на этапе проектирования.

C++ я выбрал с заделом на будущее, так как там присутствует готовое понятие класса. Также там реализованы некоторые коллекции - прямые аналоги коллекций Питона. Конкретно в моей реализации до их использования не дошло, но в большом проекте выбор C привёл бы к тех. долгу и реализации этих фичей (классы, коллекции) вручную.

Мне кажется, по ссылке находится статический компилятор. Там используется другой подход - Ahead of time, который конкурирует с Just in time. Так как оба эти подхода до сих пор существуют, выделить однозначного лидера не получается.

Да, просто вспомнил, что там тоже сигнатуры в си генерируются из питонячьих типов. Ни в коем случае не критиковал, наоборот, шикарный PoC!

Тогда простите, неправильно вас понял)

Было бы интересно сравнить результаты на какой-нибудь реально программе, думаю на ней результаты будут далеко не такими радужными.

Согласен, что применимость jit-компиляции ограничена. Из моего исследования стало ясно, что она хорошо себя показывает на алгоритмах с глубоким перебором и вложенными циклами и на разветвлённой рекурсии. В остальных случаях применение под вопросом.

Для проверки эффективности на реальной программе проекту явно не хватает функционала, но аналогичные замеры можно провести, используя @numba.jit(nopython=False)

У меня есть и практический интерес. Я работаю над проектом ANTLR, и у нас есть проблемы с производительностью Python. Я обнаружил, что в нем не сворачиваются константы, а такая оптимизация ускоряет код до 20 раз на синтетическом примере. Однако реальный код это ускоряет не более чем на 2% (тем не менее оптимизация все равно полезная, поскольку она значительно уменьшает размер исходников и удаляет широкие строки, которые неудобно смотреть в редакторе).

Конечный пользователь, добавивший над функцией аннотацию jit, пользуется совершенно другим вариантом своего кода, ничего не подозревая.

зато, дойдя до отладки кода, он будет удивлен :)

Отладка стороннего модуля на другом языке - дело занятное :)

У меня был опыт работы с PyQt, где любая ошибка внутри самого Qt просто приводила к аварийному завершению программы без выяснения причин. Дебаг внутрь исходников не входил. Так что это проблема более глобальная, чем jit-компиляторы.

Функция print после каждой строчки в помощь

Питон медленный во многом из-за динамической типизации, так как довольно много времени уходит на определение типа переменной перед её использованием.

Да ну? А я думал из-за того, что все в Python под капотом - хэшмапы c PyObject'ами, которые надо доставать и обрабатывать отдельно

Какой-нибудь switch/case в другом языке программирования такого себе не может позволить.

Поосторожнее с выражениями, молодой человек. Существуют, как минимум, языки с ФП парадигмой. А если к более конкретным примерам, то вам стоит обратить внимание на match из Rust (то, с чем я сам имею дело на регулярной основе).

Статья интересная, но что будет, если в вашу функцию прокинуть не тот тип? Или вы не сделали (а я думаю, что нет) обертку над функцией, которая бы проверяла типы через isinstance. К сожалению, такое иногда случается

Я поправил текст статьи, под другими языками подразумевались только C++ и Java. Rust я не хотел обижать.

Про то, что все примитивы в Питоне обернуты в объект я попытался сказать в статье, но может недостаточно явно. Согласен, что это тоже замедляет работу Питона.

Проверки входных типов тоже не были реализованы, и их наличие упростило бы потенциальную отладку.

А нет ли желания погонять этот компилятор на рейтрейсере или реймаршере?

Что если сравнить производительность с PyPy?

Я помучился с установкой PyPy (matplotlib не установился из-за Pillow, который не смог найти zlib, о чём я и писал в статье, но считать было можно) и вот что получил. Никакой значимой оптимизации PyPy не дал, поэтому я не хочу перегружать графики повторяющейся с Питоном линией.

Неожиданный результат. У меня в задачах типа "числодробилка" PyPy давал выигрыш раз в 5-10 по сравнению с CPython.

Может быть, я просто что-то неправильно воспроизвёл. Но всё что я сделал, это поменял интерпретатор на PyPy и выполнил прогоны с теми начальными данными, записав результат в файл.

По небольшому опыту написания контестов, где предлагают на выбор Питон или PyPy, скажу, что зачастую PyPy может проигрывать Питону по памяти, а иногда и по времени. Наверное, это зависит от конкретной задачи.

У PyPy много времени уходит на собственно JIT-трансляцию перед выполнением полезных вычислений. Чем больше времени занимают собственно расчёты по сравнению с трансляцией, тем больший выигрыш даёт PyPy.

Вы были правы, что PyPy ускоряет вычисления. Я добавил его рассмотрение в раздел с графиками. Теперь статья будет иметь большую ценность.

Стоило запустить код вне Jupyter-блокнота, как PyPy стал работать быстрее. Однако, на числах Фибоначчи он меня так и не перегнал.

классно. но почему используешь C/C++? есть golang, компиляция которого длится несколько миллисекунд из-за простоты синтаксиса (делал тесты программы в 100 строчек). + go работает в свое виртуальной машине, безопасней относительно памяти, легче параллелится. еще идея оптимизации питона: написать нормальный фронтэнд для llvm, а затем компилировать его, как все нормальные языки (не помню, скорее всего такое уже есть). но в целом статья прикольная, интересный подход к jit)

Единственное оправдание, которое я могу придумать, насчёт неиспользования Go, это, пожалуй, его незнание)

Язык модный, стильный, молодёжный. Правда, в академической среде его пока не так активно преподают. Постараюсь выучить самостоятельно.

Насчёт полной компиляции Питона - вопрос спорный. Питон во многом популярен из-за интерпретации и динамической компиляции, свобода действий над кодом во многом отличает его от компилируемых языков. Если есть желание скомпилировать Питон, то я вижу три варианта, и все не очень:

  1. Компилировать виртуальную машину Питона целиком с конкретной программой (таким занимается библиотека pyinstaller) (это не выглядит очень эффективно)

  2. Сделать компилируемую версию Питона с поддержкой динамической типизации (сомневаюсь, что такие вещи просто пишутся)

  3. Отобрать у пользователя все динамические возможности Питона и сделать более эффективную реализацию на этих ограничениях (но тогда чем Питон будет лучше других компилируемых языков, таких как C/C++ или golang)

изучить go невероятно просто. пройти туториал на офф. сайте можно минут за 30, пару дней полазать по статейкам и книжкам - вот и весь го.

лично мне и двух дней не понадобилось, чтобы выучить

питон используется для разных целей, и варианты оптимизации сильно зависят от задач. например мне надо было оптимизировать среду для обучения нейронки, которую писали на pytorch. в подобных проектах динамическая типизация питона не играет важной роли, ею можно пренебречь. в некоторых задачах интерпретируемость питона очень полезна. кажется,идея языка, который в зависимости от целей предоставляет различные способы оптимизации и функционала, вполне имеет место быть. компилируемый питон со статической типизацией? почему бы нет..

Насчёт ML хочу заметить, что тут как раз всё очень хорошо оптимизировано и написано на достаточно низкоуровневых языках. Питон - просто удобный интерфейс.

Согласно Википедии, PyTorch является интерфейсом библиотеки Torch, которая написана на Lua, LuaJIT, C, C++ и CUDA.

Возможно, существуют более подходящие примеры для использования статически типизированного Питона.

проблема была не в торче, а в pybullet, который кроме интерфейса на питон имеет функции загрузки ROS моделей (в отличии от bullet, который фактически является физическим движком). Но да, наверняка есть примеры лучше.

У меня в голове такое не укладывается))) Супер! Молодец!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации