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

Возможно, речь о 0.52.0? Просто 0.50.2 кажется вообще не существует

Это любопытственно. Насколько эффективен такой jit код? Можно ли переписать математику с плюсов на питон?

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

Эффективно это работает следующим образом. Выделяются критические к производительности участки кода, разнородные численные данные преобразаются к NumPy массивам, а далее расставляются типы и все JIT-компилируется.

По сравнению с классическим подходом на чистом NumPy этот подход очень близок по производительности к нативному коду, так как: a) убирается overhead между вызовами NumPy, b) можно использовать более разнообразные структуры данных (вложенные классы с простыми типами), с) можно использовать классические вложенные циклы, что иногда более гибко чем NumPy векторизация.

При правильном проектировании и если использовать с самого начала аннотации типов все сводится только к расстановке jit в паре мест.

Система типов в питоне полна дыр и wtf'ов, так что притаскивание туда статической типизации сделает всё хуже. Сейчас duck typing используется как poor man's traits, т.е. в большинстве случаев проблему "не того" типа можно с лёгкостью обойти. Как только оно станет статическим, duck typing перестанет работать в чистом виде.


Потеря интроспекции и всякого рода манкипатчинга лишит python его crown jewel — pytest'а. У питона есть определённый дзен и статическая типизация ему противоречит.


А для статики, фашистской типизации и быстрого кода есть Rust.

Так то оно все может быть и верно. Но быстрый и параллельный код писать хочется. А быстрый код без типов довольно сложно получить.

То, что я вижу в Numba, вполне в духе дзена Python и позволяет получить производительность, близкую к нативной. По крайней мере Numba c типами гораздо ближе к красивому Python, чем те же расширения на монструозном Cython.

У Python нет цели стать системным языком типа Rust. Но вот писать быструю математику на высоком уровне на Python очень удобно. Собственно это и подчеркивает его популярность в мире. Сейчас в экосистеме Python появляются очень хорошие инструменты, позволяющие элегантным образом снять его проблемы с производительностью. Меня это не может не радовать.
Сейчас в экосистеме Python появляются очень хорошие инструменты, позволяющие элегантным образом снять его проблемы с производительностью.
Это путь костылей. Вначале создаётся простенький язык для небольших скриптов, в то время когда уже существую и другие языки для скриптов, потом на этом языке начинают делать что-то серьёзное и приделывают кучу костылей. Вот пример кода на Crystal, который тоже собирается с помощью LLVM с мощной системой вывода типов и статической типизацией. Остальные типы можно опустить, их выведет компилятор
class Counter
	def initialize()
		@value = 0
	end
 
	def get()
		ret = @value
		@value += 1
		return ret
	end
end
 
class ListLoopIterator
	def initialize(items : Array(Float64))
		@items = items
		@counter = Counter.new()
	end
 
	def get()
		idx = @counter.get() % @items.size
		return @items[idx]
	end
end
 
items = [3.14, 2.718, 0.123, -4.0]
loop_itr = ListLoopIterator.new(items)
Ну да, это можно назвать путем костылей. А еще можно назвать естественной эволюцией наиболее приспособленного к реальным нуждам инструмента. Так то у нас тоже есть не один баг в дизайне. Однако как то живем, вполне успешно при этом.

Может быть Python и создавался как один из простейших скриптовых языков. Но с рождения он получил очень много преимуществ: a) крайне понятный для чтения язык, б) в целом почти естественная поддержка массивов со слайсами, что идеально вписалось в научные вычисления, основанные на линейной алгебре, c) идеальное взаимодействие с Fortran/C/C++, d) внятная система пакетов.

В результате Python стали «идеальным клеем» для связи между численными библиотеками, позволяя их вызывать почти без оверхеда. Это позволило стать Python почти идеальным языком для вычислений, основанных на концепции потокового графа, как в Tensorflow. А это позволило для ряда задач почти решить проблему гетерогенных вычислительных систем (CPU + GPU) с автоматической параллелизацией.

Но пока в Python еще оставался один зазор. Если код не вписывался в NumPy векторизацию, или построить граф потоковых вычислений было слишком накладно, приходилось это код выполнять на медленном интерпретаторе.

И вот теперь этот зазор почти ликвидирован. Мы получили аннотацию типов и эффективную JIT комплицию. И это все вместе выглядит вполне себе красиво, почти идеально вписалось в идеологию Python.

Для линейной алгебры никто питоновские массивы не использует, так что наличие слайсов в языке погоды не меняет, библиотеки все решают.
Идеальное взаимодействие с СИ?! приходится всё руками делать, поддержка плюсов вообще сомневаюсь что есть, слишком уж это сложная проблема. Да, FFI работает, но с таким же успехом и таким же образом это есть и в других языках от лиспа до джавы.
Внятная система пакетов python wheels, тоесть pip, в смысле conda, ой, нет, poetry.

Для линейной алгебры никто питоновские массивы не использует, так что наличие слайсов в языке погоды не меняет, библиотеки все решают.
Встроенные слайсы, оператор '@' для матричного умножения и много всего другого. Поглядите на сравнение одинаковых операций Python c MatLab. Python гораздо красивее выглядит чем Matlab. А MatLab специально создавался для научных вычислений с матрицами. Понятно, что все делается через библиотеки. Но оцените, как органично эти библиотеки вписываются в сам язык!

Идеальное взаимодействие с СИ?! приходится всё руками делать, поддержка плюсов вообще сомневаюсь что есть, слишком уж это сложная проблема
А вы пробовали pybind11. Я уже и не знаю, что проще то может быть, пара строчек и получем бесшовную интеграцию C++ кода в Python:
#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

    m.def("add", &add, "A function which adds two numbers");
}
Как я понимаю, вы евангелист питона.

крайне понятный для чтения язык
Зависит от практики, условный лисп читается не хуже.
в целом почти естественная поддержка массивов со слайсами, что идеально вписалось в научные вычисления, основанные на линейной алгебре
Это есть и в куче других языков, например ruby, и даже если в каком-то диалекте лиспа этого нет, то добавляется элементарно. Опять же, какой сейчас год на дворе?
идеальное взаимодействие с Fortran/C/C++
Вы же говорили что это
Но с рождения он получил очень много преимуществ:
в то время как pybind появился только в 2015. Mruby появился в 2012, но если брать lua, то оба языка однозначно проиграют(1993).

В результате Python стали «идеальным клеем» для связи между численными библиотеками, позволяя их вызывать почти без оверхеда.
Здесь вообще полное отсутствие преимуществ даже перед интерпретируемыми языками. А если брать условный go или rust, то питон опять проиграет. Можно ещё для примера GNU Guix привести, когда почти вся система, начиная от скриптов инициализации и заканчивая списком установленных пакетов обслуживается Guile Schema. Отличный пример того как другой язык может быть универсальным клеем.
внятная система пакетов
Питон получил пакетный менеджер одним из первых, как следствие о многих проблемах тогда просто не знали. В результате мы имеем примерно этоimage
В современных решениях, доведённых до завершения, такой проблемы нет.

Может быть Python и создавался как один из простейших скриптовых языков.
Оно так и есть. Все более-менее мощные языки либо заброшены, либо не снискали популярности. А потом, спустя несколько лет или может десятилетие эти же возможности частично появлялись в элементарных и по этому распространённых языках, и там это рассматривают как прорыв.

А потом само распространение питона начинает играть ему на руку, так как переписывать дорого, и с чём-то проще смирится, а с что-то придерживать кучей подпорок. В результате конкурент просто не может полноценно взлететь и остаётся нишевым решением. Даже если речь идёт о создании нового проекта, то некоторые берут питон только лишь потому, что он уже есть в системе, так как на нём уже есть несколько утилит в системе. В результате питон ценится не за какие-то реальные заслуги, а просто потому, что так получилось.
Пара слов про pybind11. Если вы глянете на исходники pybind11, то станет понятно, что это во многом синтаксический сахар над тем самым исходным core C API. И то, что pybind11 стал таким простым, этот как раз заслуга исходной хорошо продуманной интеграции C. Да и в целом, на Python есть биндинги почти к любой библиотеке на C/C++/Fortran. Вряд ли это было бы возможно при плохо продуманной интеграции.

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

По поводу Python есть ряд мифов, в том числе о его низкой производительности. Я лишь хочу показать, что современный Python имеет большое количество способов практически полность преодолеть проблему низкой производительности.

А что касается более мощных языков. Мне они крайне интересны. Но все, что я хочу, чтобы люди не совершали ошибок, при которых мы переписали всё на $КРУТОЙЯЗЫК, но стартап всё равно не взлетел.
Но все, что я хочу, чтобы люди не совершали ошибок, при которых мы переписали всё на $КРУТОЙЯЗЫК, но стартап всё равно не взлетел.
Вот даже интересно, если бы они ничего не переписывали, были бы у них клиенты? Я более чем уверен, что ничего бы не изменилось.

вы знаете, я пишу на питоне за деньги и на rust — за идею (изучаю). Пока что у меня нет ощущения, что хоть что-то в районе вычислений на Rust пишется хуже или медленее, чем на python. Да, статическая типизация заставляет добиться, чтобы программа была консистентной по типам до компиляции (питон — в рантайме), но если вы притаскиваете статическую типизацию в питон, то там тоже будут требовать "до компиляции" и будет то же самое.


Но трейтов нет, нормальных generic'ов нет, типов высших порядков нет, половина сахара из питона не работает, а офигенных фишек раста (тот же exhaustive pattern matching или let-выражения для извлечения из enum'ов) тоже нет.


Получается "паскаль с пробелами". И с GC-памятью, что убивает производительность (в сравнении с rust).

Получается «паскаль с пробелами».
Даже паскаль не получается, так как паскаль проверяет программу от начала и до конца, а тут проверка будет вкраплениями

В системе типов есть Protocols, позволяя совмещать дактайпинг и статическую типизацию

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

Чем это лучше любого другого компилируемого статически типизированного языка?

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

А вот если рассматривать в контексте решения практических задач, особено связанных с научными вычислениями, то мы ликвидировали существенный провал в производительности. При этом вполне элегантно, красиво и с учетом дзен Python. Код остался таким же легким для чтения и для понимания.

Теперь можно построить буквально парой строчек + парой аннотаций крайне производительный код, который перемолет огромные массивы данных крайне быстро, почти на 100% эффективно используя доступные ресурсы.

Для научных вычислений есть специализированнные ЯП. Пайтон в 99% случаев рассматривпется и использунтся для веб и около веб разработки.
Заявления про "крайне производительный код" и "пару строк"… Очень сильно пугают. Не в плане того, что такое возможно, а в плане масштаба итоговых костылей и багов

Вот тут не соглашусь. По большому счету, весь современный Machine Learning это TensorFlow + PyTorch. А это все Python.

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

А связано это с тем фактом, что современное hardware для научных вычислений довольно таки гетерогенное: многоядерные CPU, кластеры машин, GPU, TPU. Вручную параллелить алгоритмы на весь этот зоопарк слишком тяжело.

И несколько лет назад акцент в научных вычислениях сместился к идеологии потоковых вычислительных графов. Лучше всего это описано в документации к TensorFlow. Каждый алгоритм мы представляем в виде графа вычислений. Каждый узел графа это какая-то операция, дифференцирование, интегрирование, свертка и т.п. Далее этот граф компилируется под каждое специализированное hardwarе, и через него пропускается сплошной поток данных.

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

На чём основано это заявление:


Пайтон в 99% случаев рассматривпется и использунтся для веб и около веб разработки.

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

Совмещение статически типизированного компилируемого кода и динамической типизируемого интерпретируемого в рамках одного языка довольно очевидный путь дальнейшей эволюции ЯП. Собственно, сам пайтон часто используется как скриптовая обёртка для склеивания статических библиотек. Однако очень много времени отнимает написание обёрток. Тут же мы выкидаем львиную часть работы, позволяя пользоваться преимуществами оптимизированного скомпилированного кода на языке динамической части.


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

> Можно, конечно, спросить, а зачем вообще нужна динамическая часть
Вот нужен ли в питоне статически компилируемый код для меня под сомнением пока, а динамическая часть это и есть главная изюминка питона. ИМХО.

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

Но паттерны проектирования получаются, ИМХО значительно сложнее.
Главная фишка питона, кроме синтаксиса, ИМХО — простые паттерны, скорость разработки, интроспекция.
Уже есть Cython и что-то не взлетает.

Не уверен, все таки данная фича довольно узкоспециальна и необходима скорее авторам библиотек, которые понимают, что и зачем они делают, плюс к тому замена внешнего компилируемого кода внутренним — это скорее упрощение, чем усложнение. Тенденций же к тому, что практика распространится на весь софт не видно. Кроме того, я все же высказываюсь не относительно пайтона, а в целом по части эволюции ЯП. Даже если в пайтоне этот подход не приживётся, в ближайшие 10-15 лет мы не раз будем наблюдать поползновения в сторону интеграции компилируемого и интерпретируемого кода.


Проблема Cython как мне кажется, в отсутствии прозрачности, а потому недостаточной веры в его эффективность, если таковая вообще есть. Всё-таки цель требует явных выразительных средств. Разработчик должен понимать, что его код действительно эффективно скомпилируется. Cython в этом плане, увы, тёмная лошадка.

Я писал на Cython. Он дает скорость. Вполне все прозрачно. И синтаксис питоновский. Только оказывается, по сравнению с питоном, что гибкость пропадает, а скорость в 90% случаев не нужна и скорости питона вполне хватает.
Просто, когда создавали компилируемые ЯП, на первом месте были вопросы по снижению требований к железу + надежность. Скорость разработки была по умолчанию вполне приемлема, так как ПО создавали только крупные конторы. Теперь же, когда существуют стартапы из двух голодных студентов, требования к ЯП смещаются в сторону стоимости разработки. Ну а у динамических ЯП это преимущество было из коробки (для небольших объемов кода).

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

Есть проблемы:
— Интроспекция
— Манки патчинг
— Размещение разнородных объектов в массивах
— REPL
Лично меня вполне устраивает (устраивала?) экосистема, частью которой являлся питон. Это скриптовый динамический язык со строгой типизацией, легким синтаксисом и жесткими требованиями к форматированию. Для быстрой математики можно применять Си или С++ или спецбиблиотеки.
Интеграция питона с Си очень легкая.
К сожалению почти все части экосистемы движутся в сторону ухудшения. Особенно С++ — суперусложненный. Туда же движется и питон, Гвидо долго стоял на страже, но и его додолбили. Только Си более менее сохраняет свою более менее простоту.
Я за то, что лучше бы динамический питон остался бы динамическим, а когда нужно, испольовать связку с Си.
Размещение разнородных объектов в массивах
Для этого есть как минимум два решения — интерфейсы и АДТ, выбирайте по вкусу.
REPL
Для статических языков тоже делают. В каких-то случаях это будет официальный интерпретатор, в каких-то — любительский, и возможно устаревший.
Только Си более менее сохраняет свою более менее простоту.
Простота изучения/реализации и простота использования — разные вещи.
Для этого есть как минимум два решения — интерфейсы и АДТ, выбирайте по вкусу.

Т.е. предлагается из питона сделать джаву? Так она уже есть.
Переизобретают джаву как раз те, кто добавляет в питон необходимость почти везде указывать типы. А не в джаве есть вывод типов, в той или иной степени, возможно он даже в инструменте из данной статьи будет.Выше я приводил пример кода, в котором тип задаётся только один раз, против 5 в из статьи.

Соответственно другие вещи, там будут тоже короче. Вот ещё один пример.
class A
  def initialize()
    @v = 0
  end
  
  def set(v)
    @v = v
  end
  
  def get()
    @v
  end
end

class B
  def initialize()
    @v = ""
  end
  
  def set(v)
    @v = v
  end
  
  def get()
    @v
  end
end

a = A.new()
a.set(5)
b = B.new()
b.set("5")

[ a, b ].map { |i| puts i.get() }
def __init__(self, items: List[float]): — для примера так пойдет, но если items у вас сложный тип

items: nb.types.ListType(nb.types.DictType(nb.types.unicode_type, nb.types.float64))

и используется как аргумент в нескольких методах, классах, модулях — потребуется отдельный файл для определения типов (аналог *.h фалов в Си)

И еще в Numba не хватает типа struct… а так года через 2 на numba/python можно будет писать полноценный сишный код, работающий с той же скоростью.
Есть такая штука nuitka.
Человек делает как хобби проект компилятор Python в С, который бинарно совместим с CPython. Я пару лет назад скомпилировал нашу IT-систему (python, django, etc) Нюткой, оно реально работало. Но тогда компиляция заняла 40 минут, поэтому от дальнейших тестов отказался.
Кай пишет, что достиг больших успехов в статическом анализе кода на Python без всяких аннотаций. А, следовательно, скомпилированный код начинает по производительности всё сильнее обгонять CPython.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.