«Python медленный» и другие мифы об уходящей эпохе

«Python медленный» и другие мифы об уходящей эпохе

12 января 2023 г.

Когда я был маленьким, программирование было простым. У моего друга был компьютер и там были Basic и Assembly. Вы можете написать свою программу на языке Basic, что проще, но ваша программа будет работать медленно, или вы можете написать что-то на ассемблере, что будет сложнее, но ваша программа будет работать значительно быстрее.

Объяснение этому тоже было простое. Basic был интерпретатором, чтобы запустить вашу программу, он должен был просматривать ваш код каждый раз, когда вы его вызываете, и интерпретировать его построчно. Если он говорит «PRINT X», интерпретатор должен найти переменную с именем «X», найти подпрограмму, которая выполняет печать, и вызвать найденную подпрограмму для найденной переменной. n n Сборка была, ну, сборкой. Он тоже каким-то образом интерпретировал вашу программу, но только один раз, когда вы запускали ассемблер. После этого ваша программа будет выполняться без интерпретации. Программы, требующие интерпретации, работают медленнее, чем программы, не требующие интерпретации. Если, конечно, это равнозначные программы. n n А в Basic и Assembly они обычно были. Basic — императивный язык, даже не слишком дружественный к структурному программированию. Даже такая базовая вещь, как "функция", является там не встроенной языковой конструкцией, а шаблоном: "GOSUB... RETURN", очень похожим на "call...ret" в ассемблере. n n Теперь перенесемся на 30 лет вперед. Языков предостаточно. Компьютеры повсюду. Программирование перестало быть простым. Мой отдел зарабатывает себе на хлеб, переписывая код исследователя, изначально написанный на Python, на C++ для повышения производительности, потому что общеизвестно, что Python интерпретируется и работает медленно, а C++ компилируется и быстро. Но каким-то образом с каждым годом эта переделка становится все труднее и труднее выиграть какое-либо выступление. Что-то меняется и меняется быстро. Общеизвестное, однако, не меняется, поэтому мы продолжаем переписывать. n n Но теперь мы вынуждены оптимизировать все как сумасшедшие, чтобы оправдать то, что мы делаем. Приходит алгоритм на Python, переписываем его эквивалентно на C++, и вдруг он становится в 3 раза медленнее. Это... не то, для чего мы здесь. Поэтому мы реконструируем алгоритм, чтобы добиться обещанного прироста производительности. И в большинстве случаев, поскольку исследователи вообще не заботятся о производительности, а с точки зрения алгоритмов они оставляют некоторые низко висящие плоды, это работает. n n Тем не менее, весь этот бизнес теперь выглядит как афера. Мы делаем код медленнее, переписывая его на C++, чтобы мы могли сделать его быстрее, реинжиниринг кода. Почему бы нам тогда не перепроектировать его непосредственно в Python? Ах! Дело в том, что мы не знаем Python. Мы немного знаем Python, достаточно, чтобы читать и понимать, но недостаточно, чтобы делать на нем сверхбыстрые программы. n n Итак, что нужно знать?

Библиотеки

Большинство библиотек Python написаны на C или Fortran. Ядро NumPy написано на C; Pandas — в Cython и C; SciPy — на Фортране, C и частично на C++. У них нет причин быть медленнее, чем то, что было написано на C++, Rust или Julia. Хотя они могут быть быстрее. n n В нашей компании мы обслуживаем как облачные сервисы, так и настольные приложения. А пользователи десктопов злятся, когда новая версия их любимого приложения перестает работать на их железе без всякой видимой причины. Таким образом, мы сохраняем наши настольные сборки целевыми. Очень старый, как до Nehalem старый. Таким образом, никто не злится, но и не получает удовольствия от SSE3.

Конечно, вычислительная библиотека, созданная для надлежащей цели, обычно будет быстрее, чем эквивалентная библиотека, созданная для обычного компьютера 15-летней давности с ограниченными суперскалярными возможностями.

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

Компиляторы

Честно говоря, весь спор о том, какой язык быстрее, смехотворен. Язык — это не компилятор и не интерпретатор, это то, чем он является — язык: набор правил, определяющих, как мы должны сообщать компьютеру, что мы хотим делать. Язык — это всего лишь набор правил, спецификация. И ничего больше.

Само различие между интерпретацией и компиляцией осталось в прошлом веке. В настоящее время существуют интерпретаторы C, такие как IGCC, PicoC или CCons, и есть компиляторы Python. Компиляторы JIT, такие как [PyPy], и классические компиляторы с компиляцией перед запуском, такие как Codon (который также поддерживает JIT, если вы хотите, чтобы была скомпилирована только часть вашего кода).

Codon построен на LLVM, той же инфраструктуре, на которой построены Rust, Julia или Clang. Код, созданный с помощью Codon, работает, плюс-минус, с тем же уровнем производительности, что и созданный с помощью любого из них. Могут быть недостатки производительности из-за сборки мусора Python или больших собственных типов данных, но мы больше не говорим о 100x или 10x. LLVM творит чудеса. он превращает код Python в машинный код для вас.

Существуют также мифы о своевременной компиляции или JIT. Некоторые говорят, что он превосходит обычную технику компиляции перед запуском, потому что он всегда компилируется для архитектуры, имеющейся у пользователей, и, таким образом, оптимально использует ее. Некоторые говорят, что по-прежнему существуют накладные расходы на компиляцию, которые при своевременной компиляции также ложатся на пользователей. Из-за этого программы работают медленнее, так как они должны запускаться и компилироваться одновременно.

Проблема обоих мифов в том, что они оба правдивы и бесполезны. Да, JIT обычно компилирует в лучший машинный код, если вы не создаете свои двоичные файлы явно для целевой машины, что, кстати, происходит довольно регулярно при развертывании в облаке. И да, во время выполнения есть штраф за компиляцию, но он незначителен, если время выполнения измеряется месяцами, что, опять же, при развертывании в облаке не является чем-то неслыханным.

Так что есть плюсы и минусы. Что важно, Python (в частности, Codon) поддерживает режимы компиляции перед запуском и JIT, поэтому вы можете выбрать то, что больше всего соответствует вашим потребностям. Традиционные компиляторы, такие как Clang, не имеют параметра JIT.

Numba и его модель ядра

Говоря о JIT, Numba, вероятно, является самой революционной технологией в мире сверхбыстрого программирования на Python. Это компилятор, но он нацелен только на выбранные ядра, а не на всю программу. Вы, конечно, можете выбрать, что компилировать и для какой платформы. В этой настройке вы можете запускать части своего кода на ЦП, а остальные — на GPGPU.

Технически можно создавать серверные части для других специализированных устройств, таких как TPU от Google или даже фотонный ускоритель. Такого бэкенда пока нет, ребята решили выкатить собственную библиотеку. Но, что симптоматично, они также решили предоставить интерфейс для фотонного компьютера на Python, чтобы вы могли беспрепятственно взаимодействовать с Pytorch, Tensorflow или ONNX.

Так что Лайтматерии еще нет. Но Нвидиа есть. Они предоставили свой бэкэнд CUDA для Numba, и теперь вы можете писать ядра на Python и запускать их на оборудовании NVidia с максимальной эффективностью. В С++ этого нет. Однако существует диалект CU, исходящий от NVidia, который, конечно же, расширяет C++ именно в этом отношении. В Python вам не нужно расширять сам язык. Поскольку Numba работает как JIT-компилятор ядра, добавление бэкенда — это всего лишь вопрос исправления библиотеки.

Итак, модель ядра ориентирована на гетерогенные вычисления. Вы можете запускать фрагменты кода на разных устройствах, что само по себе приятно. Но есть еще одно измерение неоднородности, о котором вы, возможно, не задумывались. С моделью ядра вы можете ориентироваться на разные ядра для разных контекстов вычислений и не обязательно для аппаратных устройств. Это означает, что если вы хотите, чтобы одно ядро ​​было быстрым, но не особенно точным, вы можете собрать его с опцией «-fast-math». Но если в каком-то другом контексте вы хотите, чтобы это ядро ​​было точным, а не быстрым, вы можете пересобрать тот же самый код без компромиссов.

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

Заключение

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

Эта пользовательская база достаточно велика, чтобы привлечь как новые стартапы с революционными технологиями, такими как Lightmatter и их фотонные компьютеры, так и хорошо зарекомендовавшие себя компании с многолетним опытом в области высокопроизводительных вычислений, такие как NVidia. Все эти люди вкладывают огромные средства в то, чтобы сделать Python лучше... не в язык, конечно, а в среду. Среда, в которой писать сверхбыстрые программы лишь немногим сложнее, чем медленные одноразовые программы.

В целом они делают огромный прогресс. Python становится быстрее с каждым годом. На данный момент забудьте о фотонных компьютерах, если можете, программы, написанные на Python, часто работают наравне с программами, написанными на Julia, C++ или Rust. Но Python не останавливался на достигнутом. Он работает быстрее, чем традиционные компиляторы, и становится удобнее для пользователя.

Будьте готовы к тому, что через несколько лет компиляторы Python: PyPy, Numba или что-то совершенно новое, будут заимствовать методы у Spiral или Herbie, чтобы генерировать код настолько более эффективно, что ни один традиционный компилятор не мог приблизиться к этому. В конце концов, написать новый JIT-бэкэнд на Python гораздо проще, чем переосмыслить всю инфраструктуру LLVM.

н-н-н


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE