Как стать автором
Обновить
68
0
Roman Samarev @rssdev10

Пользователь

Отправить сообщение
  • Intel инвестировала в Julia Computing — https://juliacomputing.com/docs/JC_Whitepaper_110417.pdf Поэтому, в ближайшее время, они точно будут поддерживать Julia. Кроме того, у Intel есть специальные сборки Julia именно под их железо. Актуально для резидентов США.
  • Проект с Google — это TPU — https://github.com/JuliaTPU/XLA.jl
  • Машинное обучение — https://github.com/alan-turing-institute/MLJ.jl, Los Alamos National Laboratory — https://lanl-ansi.github.io/software/, https://mads.lanl.gov/
  • Flux.jl с неофициальными бенчмарками, где оказывается сильно быстрее PyTorch по большинству типовых задач. Скорее всего, из-за возможности глубокой автоматической оптимизации. Всё же, один язык для всего — это плюс.

Всё это из того, что на поверхности.


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


Касаемо Питона — ну не знаю, зачем его тащить в научные вычисления. Поддержка библиотек на одном языке всегда дешевле, чем 2-3, что приходится делать с Питоном. Надежда на то, что программисты готовы работать за еду, поскольку их очень много, иногда оправдывается. Иногда выливается в то, что искать толковых становится дороже, чем мигрировать на новую технологию.


А вообще, есть простой вопрос. Если что-то случилось, то кому платить? В случае Julia — платить Julia Computing и они всё исправят. В Питоне — не знаю. Авторы библиотек только за свои куски отвечают. Да и, вообще, не факт, что исправят, потому что процесс принятия решения в Питоне стал слишком тяжеловесным.


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

Мне очень не хватает в Julia простой компиляции в нативный код.

Есть https://github.com/JuliaLang/PackageCompiler.jl
Можно сгенерировать запускаемый бинарник. Только код должен быть оформлен в пакет и быть установлен как пакет. А бинарник будет довольно жирным. Для сервисов — годится.


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

не понмиаю, почему вы так сужаете область применения Julia? У меня она полностью вытеснила python в т.ч. в работе. Julia — это тот же python, тока:

Вы ставите меня в неудобное положение… Если бы я не испытывал симпатии к Julia, то не писал бы обзоры о ней… В то же время, вынужден отметить, что не совсем всё так хорошо.


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


Однако я бы не стал сейчас рекомендовать Julia в веб-разработку. Я стараюсь следить за этой областью, но не видел никаких более-менее надёжных бенчмарков о том, как поведёт себя HTTP.jl на высоких нагрузках. Если же сравнивать скорость разработки и удобство поддержки веб-приложений, например Ruby on Rails и https://github.com/GenieFramework/Genie.jl, то результат, явно не в пользу Genie. Безусловно, всё может измениться, если какая-то крупная компания решит поддержать её, обеспечит разработку массы типовых проектиков, проведёт популяризацию разработки именно на Julia. Но они этого не делают.


У Julia недостаточная интеграция в Java-код. Всё, что связано с "большими данными" — это Java. ETL — преимущественно Java. И если для других языков уже разработаны средства для интеграции с Java, то в случае с Julia — нет. Уже больше года назад я слепил для тестов обвязку https://github.com/rssdev10/julia4j. Бинарная часть собирается руками, потому как перевести на CMAKE и проверить это на разных операционках, руки у меня не дошли. Проблема в том, что embedded Julia не может быть запущена в изолированном контейнере. А значит, даже если мы активируем код из какого-нибудь условного Apache Beam/Flink/Ignite, у нас проблема с запуском на параллельное выполнение кода из разных узлов нашего workflow в рамках одного процесса.


В части интерфейса для Питон-кода. Да, он есть и работает, как минимум для чистого питон-кода. Но проблема в том, что большинство библиотек для Питона — грязные. Например, попытки подключить код, использующий pandas, приведут к ошибке активации его native-библиотек. Понятно, что для Julia-программы pandas не нужен ни в какой форме. У нас есть DataFrames, CSV и пр. с быстрым разбором файлов и быстрыми методами обработки таблиц, но для кода на питоне, использующего pandas это означает, что сначала нам надо его переписать… Даже не представляю, что будет, если пытаться подключить pytorch и как его после этого настраивать на GPU (если кто это проделал — оставьте комментарий). Понятно, что для Julia есть Flux, но кто же будет переписывать на него, например Flair Framework (у Zalando Research нет ни одного публичного проекта на Julia).


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


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

Если честно, то язык уже не настолько новый, чтобы каждую публикацию начинать с того, для чего он нужен. Посмотрите хаб https://habr.com/ru/hub/julia/


Основная область применения — математика и высоконагруженные вычисления.

Это проблема качества учебных материалов. Впрочем, идти в ROR не зная Ruby — это сложно… Ruby довольно простой язык, но его модель отличается от других императивных языков. И, как раз, попытки тащить предыдущий опыт на Ruby-программирование и вызывают ту самую проблему вхождения и полного непонимания что происходит. Это касается и функционального стиля Ruby, и принципа умолчания в Rails, где просто надо знать, что происходит внутри. Иначе, сплошная непредсказуемая (для новичка) магия.

Главный конкурент, Python, изначально делал ставку на околонаучное сообщество — и выиграл в перспективе

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

julia> a(x::Any, b::Float64) = nothing
a (generic function with 1 method)

julia> a(b::Float64, t::Any) = 1
a (generic function with 2 methods)

julia> methods(a)
# 2 methods for generic function "a":
[1] a(x, b::Float64) in Main at REPL[1]:1
[2] a(b::Float64, t) in Main at REPL[2]:1

julia> a(1.0, 1.0)
ERROR: MethodError: a(::Float64, ::Float64) is ambiguous. Candidates:
  a(b::Float64, t) in Main at REPL[2]:1
  a(x, b::Float64) in Main at REPL[1]:1
Possible fix, define
  a(::Float64, ::Float64)
Stacktrace:
 [1] top-level scope at none:0

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


Possible fix, define
  a(::Float64, ::Float64)

У Julia есть абстрактные и конкретные типы. Абстрактные всегда пустые, но могут наследоваться. Конкретные могут инстанцироваться и иметь поля. Для любого метода можно указать либо тип, к которому его конкретно надо примениять, либо указать, что тип абстрактный, и применять к любому из потомков.


То есть, ответ — полиморфизм статический. Однако методы для обработки наших типов можем писать где угодно. Обычно вместе с определением типа. Шаблоны C++ или Java generics — это, как раз, наиболее близкий аналог.

Конкретно в этом примере код перегружен. Вопрос о причинах — к автору. Циклы в Julia используются редко. В большинстве случаев они заменяются применением функции к итератору. Например:


function mag(p::NDimPoint)
    sqrmag = 0.0
    # т.к. размерность точно неизвестна, нужно итерировать по всем полям структуры
    # имена полей для типа T получаются вызовом fieldnames(T)
    for name in fieldnames(typeof(p))
        sqrmag += getfield(p, name)^2
    end
    return sqrt(sqrmag)
end

скорее встретится в форме:


mag(p::NDimPoint) = 
    sqrt(sum(name -> getfield(p, name)^2, fieldnames(typeof(p)))

Или


mag(p::NDimPoint) = 
    sum(name -> getfield(p, name)^2, fieldnames(typeof(p)) |>
    sqrt
Вы не пробовали какие-то замеры делать по производительности именно веб-сервисов?

Не пробовал. О чём честно написал в заключении.

Чтобы результаты были корректные, мало запускать только тесты на Julia. Надо в одинаковых условиях сравнивать с эталонными реализациями.

Идеальный вариант — встраиваться в существующие бенчмарки и прогонять на них.
Если структура декларируется как structure, то она по-умолчанию неизменяема. Если mutable structure, то изменяема. Система типов у Julia несколько отличается от привычных императивных языков. Но довольно простая.

Ну и на счёт возвращаемых значений. В норме функция именно возвращает новое значение, а не модифицирует входные аргументы. Такой подход и в Ruby, кстати.

А вариант функции с модификацией, когда надо изменить именно тот массив, который передан на вход, автоматически означает, что имя функции будет с суффиксом `!`.
list = [1 2 3]
for item in list
item = item + 1
end


У Julia всё просто. Воспринимаем переменную как ссылку на объект, но помним, что большинство операций не изменяют исходный объект (чистые функции и всё такое....). Соответственно, этот пример — бесполезен. item — локальная переменная. item + 1 не изменяет объект, на который ссылалась item.
julia> list = [1 2 3]
1×3 Array{Int64,2}:
 1  2  3

julia> for item in list
         item = item + 1
       end

julia> list
1×3 Array{Int64,2}:
 1  2  3


А вот такой пример приведёт к модификации (метод с суффиксом `!` является модифицирующим):
julia> list = [[1],[2],[3]]
3-element Array{Array{Int64,1},1}:
 [1]
 [2]
 [3]

julia> for item in list
         push!(item, 1)
       end

julia> list
3-element Array{Array{Int64,1},1}:
 [1, 1]
 [2, 1]
 [3, 1]


И такой пример также приведёт к модификации по той же причине. item ссылается на автономные объекты-массивы, каждый из которых может быть изменён:
julia> list = [[1],[2],[3]]
3-element Array{Array{Int64,1},1}:
 [1]
 [2]
 [3]

julia> for item in list
            item .+= 1
       end

julia> list
3-element Array{Array{Int64,1},1}:
 [2]
 [3]
 [4]


Касаемо оптимизаций Julia на граничных значениях циклов — не отвечу. Скорее всего, что-то делает. В сущности, у неё есть возможность посмотреть генерируемый LLVM-код. По нему можно проверить.
Итерация в for происходит по ссылке или по значению?

Я не совсем понимаю вопрос. Даже в C++ — итератор — отдельный класс. В Julia нет таких языковых понятий как ссылка и значение. Любое присвоение следует воспринимать как копирование ссылки (это не так на уровне машинного кода, но справедливо для модели языка). C-шных операторов &a, *a здесь тоже нет.

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

Опять не понимаю вопрос. Если вопрос о том, выполняется ли реально внутренняя проверка, когда мы пишем a[i], чтобы i не вылетел за пределы, то ответ — не знаю. Скорее всего, не проверяется. Иначе на это придётся тратить процессорное время. Но при работе с итератором всегда идёт вычисление следующего элемента с проверкой границы.

Собственно, в примерах выше, я всего лишь продемонстрировал как избегать использование прямого обращения по индексу, чтобы не ошибиться с границами индексов. Это, конечно, ещё не Руби с его Enumerable. Но уже далеко не C++, с его довольно ограниченными итераторами.
using BenchmarkTools

y = 1 : 0.5 : 10000000
x = zeros(length(y))
b = @benchmark begin
  x .= y .+ y      # in-place addition
  x .= 2 .* y      # in-place multiplication
end

#BenchmarkTools.Trial: 
#  memory estimate:  192 bytes
#  allocs estimate:  3
#  --------------
#  minimum time:     117.716 ms (0.00% GC)
#  median time:      127.008 ms (0.00% GC)
#  mean time:        133.613 ms (0.00% GC)
#  maximum time:     194.349 ms (0.00% GC)
#  --------------
#  samples:          38
#  evals/sample:     1

y = [1 : 0.5 : 10000000...]
x = zeros(length(y))
b2 = @benchmark begin
  x .= y .+ y      # in-place addition
  x .= 2 .* y      # in-place multiplication
end

#BenchmarkTools.Trial: 
#  memory estimate:  96 bytes
#  allocs estimate:  4
#  --------------
#  minimum time:     59.379 ms (0.00% GC)
#  median time:      62.218 ms (0.00% GC)
#  mean time:        66.322 ms (0.00% GC)
#  maximum time:     132.069 ms (0.00% GC)
#  --------------
#  samples:          76
#  evals/sample:     1
julia> x = 1 : 0.5 : 10
1.0:0.5:10.0

julia> typeof(x)
StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}

julia> x = [1 : 0.5 : 10...]
19-element Array{Float64,1}:
  1.0
  1.5
  2.0
  2.5

Не везде это будет работать, где может быть использован массив.


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

Если нужен Spark и уже знаете Java — разумнее всего сделать полшага вперед и перейти на Scala. Если конечно это прагматический вопрос, а не религиозный.


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

Для нынешнего Java-кода я могу быть уверен, что найду программистов и через 10 лет. Для Scala — не факт, что и через 5 лет такие найдутся. Тем более, что то, что пишут на Java для Spark, по факту, синтаксически не сильно отличается от Scala.
Ну если проект на Spark, то тут даже думать не надо. Любой вариант кроме Scala требует экстраординарных обоснований.


Запустите опрос о том, кто и на чём на Spark пишет.

Да и, вопрос, если BigData, то Spark ли… И стоит для Ignite или Flink использовать что-то кроме Java?..
В настоящее время Scala практически не развивается. Именно потому, что Java за последние 10 лет довольно сильно изменилась в лучшую сторону, и возникает вопрос, зачем нужна Scala. Да и остались ли, хоть какие-то API у Spark (как, впрочем, и у любого другого BigData-инструмента), которые не были бы на 100% совместимы с Java?

PS: Я бы не решился сейчас начинать новый проект на Scala. Опыт её использования имеется. Очень дорого обходится в ней контроль за кодом разработчиков. Существенно проще написать write-only код, по сравнению с Java. + чисто скальные проблемы по интеграции разных библиотек, собранных в разных Скалах.
Здесь бы было интересно сравнить также vanilla Java-код. И получить ответ на вопрос, стоит ли мучаться с поиском программистов под Scala ради экономии 10% объема кода.

У программистов на императивных языках программирования есть привычка использовать избыточные конструкции. Однако в Julia можно многие из них не использовать. Например индексы массива. Очень часто они вообще не нужны. Либо индексы могут быть вычислены автоматически (без необходимости вспоминать, с нуля они начинаются или с единицы). Например, традиционный вариант цикла:


let res = 0
  x = [1:0.5:10...] # массив от 1 до 10 с шагом 0.5
  for i = 1 : length(x)
    res = res + 1 / x[i]
  end
  println(res) # 5.195479314287365
end

Однако в коде видно, что индекс мы не используем по прямому назначению. Значит код может быть переписан как:


let res = 0
  x = [1:0.5:10...]
  for item in x
    res = res + 1 / item
  end
  println(res) # 5.195479314287365
end

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


  x = [1:0.5:10...] 
  res = mapreduce(item -> 1/item, +, x)
  println(res) # 5.195479314287365

Имя метода здесь — mapreduce, значит аргументы им соответствют. Функция item -> 1/item относится к map. Функция + — к reduce.


Для случая простой свёртки без map, можем просто использовать reduce.


Если же действительно нужен индекс, то можно заставить Julia взять его самостоятельно.


let res = 0
  x = [1:0.5:10...]
  for i in eachindex(x)
    res = res + i / x[i]
  end
  println(res) # 32.80452068571264
end

Но в примере видим, что нас интересовал индекс и элемент, а не индекс для того, чтобы взять элемент. Значит можем заменить код:


let res = 0
  x = [1:0.5:10...]
  for (i, item) in enumerate(x)
    res = res + i / item
  end
  println(res) # 32.80452068571264
end

Но и в этом случае нас не просили писать цикл, а просили вычилить результат по каждому индексу и элементу. Значит, можем заменить на:


  x = [1:0.5:10...]
  res = mapreduce(((i, item),) -> i/item,  +,  enumerate(x))
  println(res) # 32.80452068571264

В последнем примере конструкция ((i, item),) указывает, что надо разобрать первый аргумент из tuple.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность