Julia. С чего начать проект?…

  • Tutorial


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


Проект


Как уже было упомянуто, одноразовые скрипты или блокноты Jupyter Notebook имеют право на существование на рабочем столе одного человека, особенно, когда язык программирования используется как продвинутый калькулятор. Но этот подход совершенно не годится для разработки проектов, которые должны развиваться и эксплуатироваться годами. И, естественно, Julia, как технологическая платформа, имеет средства, предоставляющие разработчикам такую возможность.


Для начала, несколько общих моментов. В Julia для управления пакетами имеется модуль Pkg. Любая библиотека Julia — это модуль (module). Если модуль не входит в базовый комплект Julia, он оформляется как отдельный пакет. Для каждого пакета имеется файл проекта Project.toml, который содержит описание проекта и его зависимости от других пакетов. Существует второй файл — Manifest.toml, который, в отличии от Project.toml, генерируется автоматически и содержит перечень всех необходимых зависимостей с номерами версий пакетов. Формат файлов Toml — это Tom's Obvious, Minimal Language.


Правила именования пакета


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


  • Следует избегать жаргона и неоднозначных сокращений, по-разному используемых в разных областях.
  • Не следует использовать слово Julia в имени пакета.
  • Пакеты, которые предоставляют какие-то функции вместе с новыми типами, в них объявленными, следует именовать во множественном числе.
    ◦ DataFrames предоставляет тип DataFrame.
    ◦ BloomFilters предоставляет тип BloomFilter.
    ◦ В то же время, JuliaParser не предоставляет новый тип, а новая функциональность — это функция JuliaParser.parse().
  • Следует предпочитать прозрачность и понятность, используя полное имя, чем сокращение. RandomMatrices менее двусмысленное, чем RndMat или RMT.
  • Имя может соответствовать специфике предметной области. Примеры:
    ◦ Julia не имеет своих пакетов рисования графиков. Вместо этого, имеются пакеты Gadfly, PyPlot, Winston и пр., каждый из которых реализует свой собственный подход и методику использования.
    ◦ В то же время, SortingAlgorithms предоставляет полноценный интерфейс для использования алгоритмов сортировки.
  • В случаях, когда пакеты являются обёрткой над какими-то сторонними библиотеками, они сохраняют имя этой библиотеки. Примеры:
    CPLEX.jl является обёрткой над библиотекой CPLEX.
    MATLAB.jl предоставляет интерфейс для активации MATLAB из Julia.

В то же время, имя git-репозитория, обычно, имеет суффикс «.jl».


Генерация пакета


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


julia> ]

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


(v1.2) pkg> generate HelloWorld

В итоге, в текущей директории появится новая директория, соответствующая имени пакета, состав которой можем увидеть при помощи команды tree (если она установлена):


shell> cd HelloWorld

shell> tree .
.
├── Project.toml
└── src
    └── HelloWorld.jl

1 directory, 2 files

В данном случае, мы видим минимальный, но недостаточный для хорошо оформленного проекта набор файлов.Подробнее см. https://julialang.github.io/Pkg.jl/v1/creating-packages/.


Альтернативный вариант создания пакетов — генератор PkgTemplates.jl. В отличии от встроенного генератора, он позволяет сразу сформировать полный набор служебных файлов для обслуживания пакета. Единственный недостаток — он сам должен быть установлен как пакет.


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


julia> using PkgTemplates

Формируем шаблон, включающий список авторов, лицензию, требования к Julia, список плагинов для систем непрерывной интеграции (пример из документации к PkgTemplates):


julia> t = Template(;
           user="myusername", # имя в github
           license="ISC",     # тип лицензии
           authors=["Chris de Graaf", "Invenia Technical Computing Corporation"],
           dir="~/code",      # директория, куда поместить пакет
           julia_version=v"1.0", # минимальная версия Julia
           plugins=[          # плагины средств непрерывной интеграции
               TravisCI(),
               Codecov(),
               Coveralls(),
               AppVeyor(),
               GitHubPages(),
               CirrusCI(),
           ],
       )

Получаем шаблон:


Template:
  → User: myusername
  → Host: github.com
  → License: ISC (Chris de Graaf, Invenia Technical Computing Corporation 2018)
  → Package directory: ~/code
  → Minimum Julia version: v0.7
  → SSH remote: No
  → Commit Manifest.toml: No
  → Plugins:
    • AppVeyor:
      → Config file: Default
      → 0 gitignore entries
    • Codecov:
      → Config file: None
      → 3 gitignore entries: "*.jl.cov", "*.jl.*.cov", "*.jl.mem"
    • Coveralls:
      → Config file: None
      → 3 gitignore entries: "*.jl.cov", "*.jl.*.cov", "*.jl.mem"
    • GitHubPages:
      → 0 asset files
      → 2 gitignore entries: "/docs/build/", "/docs/site/"
    • TravisCI:
      → Config file: Default
      → 0 gitignore entries

Теперь, с помощью этого шаблона, можем создавать пакеты, просто указав их имя:


julia> generate(t, "MyPkg1")

B минимальном варианте шаблон может выглядеть, например так:


julia> t = Template(;
       user="rssdev10",
       authors=["rssdev10"])
Template:
  → User:  rssdev10
  → Host: github.com
  → License: MIT (rssdev10 2019)
  → Package directory: ~/.julia/dev
  → Minimum Julia version: v1.0
  → SSH remote: No
  → Add packages to main environment: Yes
  → Commit Manifest.toml: No
  → Plugins: None

Если создадим по этому шаблону пакет с именем MyPkg2:


julia> generate(t, "MyPkg2")

То можем проверить результат прямо из Julia:


julia> run(`git -C $(joinpath(t.dir, "MyPkg2")) ls-files`);
.appveyor.yml
.gitignore
.travis.yml
LICENSE
Project.toml
README.md
REQUIRE
docs/Manifest.toml
docs/Project.toml
docs/make.jl
docs/src/index.md
src/MyPkg2.jl
test/runtests.jl

Следует обратить внимание на следующие поля:


  • user="myusername", — имя для git-регистрационной записи.
  • dir — директория для размещения кода пакета. Если не задано, он будет создан в директории разработки ~/.julia/dev. Причём, в соответствии с правилами unix файловых систем, директория ~/.julia — скрытая.

После создания проекта, будет сгенерирован достаточный набор файлов и создан git-репозиторий. Более того, все сгенерированные файлы будут добавлены в этот репозиторий автоматически.


Типовое расположение файлов в проекте


Картинку с типовым расположением файлов и их содержимым позаимствуем из https://en.wikibooks.org/wiki/Introducing_Julia/Modules_and_packages, но немного расширим:


Calculus.jl/                               # Главная директория проекта Calculus
  deps/                                    # Здесь находятся файлы для сборки пакета
  docs/                                    # Здесь находятся файлы для сборки документации
  src/                                     # Здесь находятся исходники
    Calculus.jl                            # Главный файл модуля — с заглавной буквы.
      module Calculus                      # Внутри этого файла модуль с тем же именем!
        import Base.ctranspose             # А также импорт внешних зависимостей,
        export derivative, check_gradient, # декларация экспорта функций этого пакета 
        ...
        include("derivative.jl")           # подключение файлов реализации модуля
        include("check_derivative.jl")             
        include("integrate.jl")
        import .Derivative
      end                                  # конец файла Calculus.jl
    derivative.jl                          # В этом файле реализованы какие-то функции, 
      module Derivative                    #      подключаемые в файле Calculus.jl
        export derivative
        function derivative()
        ...
        end
        …
      end
    check_derivative.jl                # файл содержит логику вычисления производных, 
       function check_derivative(f::...)#  и он включается инструкцией
         ...                            # "include("check_derivative.jl")" в Calculus.jl
      end
       …
    integrate.jl                           # файл содержит логику вычисления интегралов, 
      function adaptive_simpsons_inner(f::Funct# и он также включается в Calculus.jl
        ...
      end
      ...
    symbolic.jl                            # включает символьные операции для Calculus.jl
      export processExpr, BasicVariable, ...# эти функции доступны пользователям нашего модуля
      import Base.show, ...                 # некоторые функции из модуля Base импортируем, 
      type BasicVariable <: AbstractVariable# ... чтобы добавить к ним новые методы
        ...
      end
      function process(x::Expr)
        ...
      end
      ...     
  test/                                    # Директория тестов для модуля Calculus
    runtests.jl                            # Точка запуска всех тестов
      using Calculus                       # Подключаем нам модуль Calculus... 
      using Test                           # и модуль Base.Test... 
      tests = ["finite_difference", ...    # Имя файлов-тестов храним как строки... 
      for t in tests
        include("$(t).jl")                 # ... запускаем каждый тест индивидуально 
      end
      ...
    finite_difference.jl                   # Этот тест проверяет какие-то конечные вычисления 
      @test ...                            # ... и по имени подключается в  runtests.jl
      ...

Добавим, что директория deps может содержать файлы, необходимые для корректной сборки пакета. Например, deps/build.jl — это скрипт, который автоматически запускается в момент установки пакета. Скрипт может содержать любой код для подготовки данных (скачать набор данных или выполнить предобработку) или других программ, необходимых для работы.


Следует обратить внимание на то, что в проекте может быть только один главный модуль. То есть, в примере выше — Calculus. Однако, в том же примере есть вложенный модуль Derivative, который подключается через include. Обратите внимание на это. include подключает файл как текст, а не как модуль, что происходит в случае using или import. Последние две функции не просто включают модуль, а заставляют Julia компилировать его как отдельную сущность. Кроме того, Julia будет пытаться найти этот модуль в пакетах зависимостей и выдавать предупреждение, что он остутствует в Project.toml. Поэтому, если наша задача сделать иерархический доступ к функциям, разграничив их по пространствам имён, то включаем файлы через include, а активируем модуль через точку, указывая на его локальную принадлежность. То есть:


module Calculus
  include("derivative.jl")
  import .Derivative
  ...
end

Функция derivative, которая экспортирована из модуля Derivative будет доступна нам через Calculus.Derivative.derivative()


Файл проекта Project.toml


Файл проекта является текстовым файлом. Основные его секции раскрываются в описании https://julialang.github.io/Pkg.jl/v1/toml-files/


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


Основными полями являются:


name = "Example"
uuid = "7876af07-990d-54b4-ab0e-23690620f79a"
version = "1.2.5"

name — имя пакета, выбранное согласно правилам именования. uuid — унифицированный идентификатор, который может быть сгенерирован генератором пакета или любым другим uuid генератором. version — номер версии пакета в формате трёх десятичных чисел, разделенных точками. Это соответствует формату Semantic Versioning 2.0.0. До задекларированной версии 1.0.0 возможны любые изменения в программном интерфейсе. После выпуска этой версии владелец пакета обязан соблюдать правила совместимости. Любые совместимые изменения должны быть отражены в младшем числе (справа). Несовместимые изменения должны сопровождаться изменением старшего числа. Естественно, никакого автоматического контроля за правилом версионности нет, но несоблюдение правила просто приведёт к тому, что пользователи пакета начнут массово прекращать использовать и мигрировать на тот пакет, авторы которого соблюдают это правило.


Все зависимости пакета представлены в секции [deps].


[deps]
Example = "7876af07-990d-54b4-ab0e-23690620f79a"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

Эта секция содержит список прямых зависимостей нашего пакета. Каскадные же зависимости отражаются в файле Manifest.toml, который генерируется автоматически в директории проекта. Все зависимости представлены парами имя=идентификатор. И, обычно, эта часть не заполняется руками. Для этого предусмотрены функции пакета Pkg. И, чаще всего, это делают из REPL, переключив его в режим управления пакетами — ]. Далее — операции add, rm, st и пр., но обязательно в контексте текущего пакета. Если нет, надо выполнить activate ..


Manifest.toml может быть сохранен в системе контроля версий git. Такой подход с двумя файлами позволяет жестко зафиксировать пакеты в дереве зависимостей во время тестирования программного продукта, после чего, гарантировать, что если наш пакет будет развёрнут в новом месте, то именно те же версии сторонних пакетов и будут там же повторены. Или же, наоборот, при отсутствии Manifest.toml будет предоставлена возможность использования любых доступных версий, удовлетворяющих базовым условиям.


Секция [compat] позволяет указать конкретные версии пакетов, которые нам требуются.


[deps]
Example = "7876af07-990d-54b4-ab0e-23690620f79a"

[compat]
Example = "1.2"
julia = "1.1"

Пакеты указываются по имени, ранее использованного в секции [compat]. julia указывает версию именно Julia.


При указании версий, действуют правила, перечисленные в https://julialang.github.io/Pkg.jl/dev/compatibility/. Впрочем, эти же правила указаны в Semantic Versioning.


Есть несколько правил указания версий. Например:


[compat]
Example = "1.2, 2"

означает, что подходит любая версия в диапазоне [1.2.0, 3.0.0), не включая 3.0.0. И это полностью соответствует более простому правилу:


[compat]
Example = "1.2"

Более того, простое указание номера версии является сокращенной формой "^1.2". Пример применения которой выглядит:


[compat]
PkgA = "^1.2.3" # [1.2.3, 2.0.0)
PkgB = "^1.2"   # [1.2.0, 2.0.0)
PkgC = "^1"     # [1.0.0, 2.0.0)
PkgD = "^0.2.3" # [0.2.3, 0.3.0)
PkgE = "^0.0.3" # [0.0.3, 0.0.4)
PkgF = "^0.0"   # [0.0.0, 0.1.0)
PkgG = "^0"     # [0.0.0, 1.0.0)

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


[compat]
PkgA = "~1.2.3" # [1.2.3, 1.3.0)
PkgB = "~1.2"   # [1.2.0, 1.3.0)
PkgC = "~1"     # [1.0.0, 2.0.0)
PkgD = "~0.2.3" # [0.2.3, 0.3.0)
PkgE = "~0.0.3" # [0.0.3, 0.0.4)
PkgF = "~0.0"   # [0.0.0, 0.1.0)
PkgG = "~0"     # [0.0.0, 1.0.0)

Ну и, естественно, доступно указание по знакам равенства/неравенства:


[compat]
PkgA = ">= 1.2.3" # [1.2.3,  ∞)
PkgB = "≥ 1.2.3"  # [1.2.3,  ∞)
PkgC = "= 1.2.3"  # [1.2.3, 1.2.3]
PkgD = "< 1.2.3"  # [0.0.0, 1.2.2]

Имеется возможность указать несколько вариантов зависимостей в секции [targets]. Традиционно, в Julia до версии 1.2, её использовали для того, чтобы указать зависимости для использования пакета и для запуска тестов. Для этого, дополнительные пакеты указывались в секции [extras], а в [targets] перечислялись целевые конфигурации с именами пакетов.


[extras]
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Markdown", "Test"]

Начиная с Julia 1.2 рекомендуется просто добавить отдельный файл проекта для тестов test/Project.toml.


Дополнительные зависимости


Дополнительные зависимости могут быть подключены через файл deps/build.jl, однако в структуре проекта Julia предусмотрен файл Artifacts.toml. Библиотека управления проектами Pkg.Artifacts предоставляет функции для автоматизации загрузки дополнительных зависимостей. Пример такого файла:


# Example Artifacts.toml file
[socrates]
git-tree-sha1 = "43563e7631a7eafae1f9f8d9d332e3de44ad7239"
lazy = true

    [[socrates.download]]
    url = "https://github.com/staticfloat/small_bin/raw/master/socrates.tar.gz"
    sha256 = "e65d2f13f2085f2c279830e863292312a72930fee5ba3c792b14c33ce5c5cc58"

    [[socrates.download]]
    url = "https://github.com/staticfloat/small_bin/raw/master/socrates.tar.bz2"
    sha256 = "13fc17b97be41763b02cbb80e9d048302cec3bd3d446c2ed6e8210bddcd3ac76"

[[c_simple]]
arch = "x86_64"
git-tree-sha1 = "4bdf4556050cb55b67b211d4e78009aaec378cbc"
libc = "musl"
os = "linux"

    [[c_simple.download]]
    sha256 = "411d6befd49942826ea1e59041bddf7dbb72fb871bb03165bf4e164b13ab5130"
    url = "https://github.com/JuliaBinaryWrappers/c_simple_jll.jl/releases/download/c_simple+v1.2.3+0/c_simple.v1.2.3.x86_64-linux-musl.tar.gz"

[[c_simple]]
arch = "x86_64"
git-tree-sha1 = "51264dbc770cd38aeb15f93536c29dc38c727e4c"
os = "macos"

    [[c_simple.download]]
    sha256 = "6c17d9e1dc95ba86ec7462637824afe7a25b8509cc51453f0eb86eda03ed4dc3"
    url = "https://github.com/JuliaBinaryWrappers/c_simple_jll.jl/releases/download/c_simple+v1.2.3+0/c_simple.v1.2.3.x86_64-apple-darwin14.tar.gz"

[processed_output]
git-tree-sha1 = "1c223e66f1a8e0fae1f9fcb9d3f2e3ce48a82200"

Подробнее останавливаться не будем, поскольку дальнейшее описание зависит от конкретного сценария использования. Доступны библиотечные функции artifact_hash , download, create_artifact, bind_artifact. Подробнее см. документацию https://julialang.github.io/Pkg.jl/dev/artifacts/ .


Реализация основного кода и отладка


Директорию разработки мы, естественно, явно или неявно указываем при создании пакета. Однако, при необходимости, можем её изменить. Если пакет был сгенерирован PkgTemplates с параметрами по умолчанию, ищете его в директории ~/.julia/dev. Несмотря на то, что директория скрытая, переход в неё возможен по прямой ссылке в файловом навигаторе. Для MacOS в Finder, например, это делается нажатием Command+Shift+G. Если же, пакет создан в любой другой директории, просто откройте её в текстовом редакторе. Оптимальный редактор для работы с кодом на Julia — это Atom и всё, что поддерживает плагин uber-juno. В этом случае, вы получаете текстовый редактор с автоформатированием кода, консоль REPL для интерактивного запуска кода, возможность выполнения только выделенных фрагментов кода и просмотра полученных результатов, включая отображение графики. А также, пошаговый отладчик. Хотя, надо признать, что на данный момент он достаточно медленный, поэтому актуальный режим отладки — сначала думаем что хотим проверить и ставим отладочный вывод, потом запускаем на тест выполнение.


Рекомендуется посмотреть общие шаблоны проектирования для динамических языков програмирования. А также, вышла книга "Hands-On Design Patterns with Julia 1.0. Tom Kwong" и примеры кода к ней. А при реализации программ, следует учитывать рекомендации по стилю программирования Julia Style Guide.


Из тонкостей отладки можно отметить пакет Revise.jl. Его активацию можно установить в файле .julia/config/startup.jl только для интерактивного режима, в котором и запускать REPL из редактора Atom. Revise позволяет без перезапуска сеанса REPL редактировать код функций внутри нашего пакета, а каждый запуск using/import в наших тестах, будет подключать эти обновления.


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


Тесты


Типовое расположение тестов — директория test. Файл test/runtests.jl является точкой запуска всех тестов.


Применительно к выше упомянутому примеру, типовой вид файла имеет вид:


using Calculus                      # Подключаем наш модуль Calculus для тестов...
using Test                          # и модуль Test...
tests = ["finite_difference", "..."]# В этом массиве храним имена файлов-тестов...
for t in tests
  include("$(t).jl")                # А теперь запускаем в цикле все тесты
end

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


Для проведения модульного тестирования Julia предоставляет модуль Test из состава базового комплекта библиотек. В этом модуле определён макрос @test, назначение которого — проверить верность указанного утверждения. Примеры:


julia> @test true
Test Passed

julia> @test [1, 2] + [2, 1] == [3, 3]
Test Passed
julia> @test π ≈ 3.14 atol=0.01
Test Passed

Обратите внимание на полную форму оператора приближенного сравнения .


Утверждение, проверяющее выбор исключения — @test_throws. Пример — создаём массив и обращаемся к индексу за его пределами:


julia> @test_throws BoundsError [1, 2, 3][4]
Test Passed
      Thrown: BoundsError

Полезной конструкцией является @testset. Она позволяет группировать отдельные утверждения в логически связный тест. Например:


julia> @testset "trigonometric identities" begin
           θ = 2/3*π
           @test sin(-θ) ≈ -sin(θ)
           @test cos(-θ) ≈ cos(θ)
           @test sin(2θ) ≈ 2*sin(θ)*cos(θ)
           @test cos(2θ) ≈ cos(θ)^2 - sin(θ)^2
       end;
Test Summary:            | Pass  Total
trigonometric identities |    4      4

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


julia> @testset "Foo Tests" begin
           @testset "Animals" begin
               @testset "Felines" begin
                   @test foo("cat") == 9
               end
               @testset "Canines" begin
                   @test foo("dog") == 9
               end
           end
           @testset "Arrays" begin
               @test foo(zeros(2)) == 4
               @test foo(fill(1.0, 4)) == 15
           end
       end

Arrays: Test Failed
  Expression: foo(fill(1.0, 4)) == 15
   Evaluated: 16 == 15
[...]
Test Summary: | Pass  Fail  Total
Foo Tests     |    3     1      4
  Animals     |    2            2
  Arrays      |    1     1      2
ERROR: Some tests did not pass: 3 passed, 1 failed, 0 errored, 0 broken.

@test_broken, @test_skip и некоторые другие макросы позволяют временно отключать конкретные тесты.


Профилирование


Профилирование кода возможно по разным критериям. Команда julia имеет следующие полезные ключи запуска:


 --code-coverage={none|user|all}, --code-coverage
            Count executions of source lines (omitting setting is equivalent to "user")
 --code-coverage=tracefile.info
            Append coverage information to the LCOV tracefile (filename supports format tokens).
 --track-allocation={none|user|all}, --track-allocation
            Count bytes allocated by each source line (omitting setting is equivalent to "user")

code-coverage — покрытие кода при выполнении програмы. Для каждой строки кода (если строка значима), выполняется подсчёт количество выполнений этой строки. Генерируется файл покрытия, где для каждой строки приводится сколько раз выполнена эта строка. Эти файлы имеют суффикс .cov и должны быть удалены после анализа. Именно этим способом можно легко выявить критические пути выполнения кода программы и провести оптимизацию.
Фрагмент этого файла выглядит следующим образом:


        - function vectorize(str::String)
       96   tokens = str |> tokenizer |> wordpiece
       48   text = ["[CLS]"; tokens; "[SEP]"]
       48   token_indices = vocab(text)
       48   segment_indices = [fill(1, length(tokens) + 2);]
       48   sample = (tok = token_indices, segment = segment_indices)
       48   bert_embedding = sample |> bert_model.embed
       48   collect(sum(bert_embedding, dims=2)[:])
        - end

track-allocation — измерение потребления памяти при выполнении кода. Также как и в случае анализа покрытия, генерируются файлы, имеющие то же имя, что и имя файлов модуля, но с суффиксом .mem.


Фрагмент этого файла выглядит:


        - function vectorize(str::String)
        0   tokens = str |> tokenizer |> wordpiece
  6766790   text = ["[CLS]"; tokens; "[SEP]"]
        0   token_indices = vocab(text)
    11392   segment_indices = [fill(1, length(tokens) + 2);]
     1536   sample = (tok = token_indices, segment = segment_indices)
        0   bert_embedding = sample |> bert_model.embed
   170496   collect(sum(bert_embedding, dims=2)[:])
        - end

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


Универсальный рецепт запуска профилировщика — запуск всех тестов из корневой директории проекта:


julia --project=@.  --code-coverage --track-allocation test/runtests.jl 

Еще один способ профилирования — при помощи пакета Profile.jl и макроса @profile. Возьмём пример из статьи https://julialang.org/blog/2019/09/profilers и запустим профилирование функции вычисления чисел Фибоначчи. В примере будем использовать макрос @noinline, который запрещает компилятору раскрывать код функций и прикасаться к рекурсиям. При этом, намеренно сделаем рекурсию через две функции fib и fib_r.


julia> @noinline function fib(n)
         return n > 1 ? fib_r(n - 1) + fib_r(n - 2) : 1
       end

julia> @noinline fib_r(n) = fib(n)

julia> @time fib(40)
  0.738735 seconds (3.16 k allocations: 176.626 KiB)
165580141

julia> using Profile

julia> @profile fib(40)
165580141

julia> Profile.print(format=:flat, sortedby=:count)
 Count File      Line Function
    12 int.jl      52 -
    14 int.jl      53 +
   212 boot.jl    330 eval
  5717 REPL[2]      1 fib_r
  6028 REPL[1]      2 fib

julia> count(==(0), Profile.fetch())
585

@profile fib(40) запускает измерение количества вызовов всех функций при выполнении этого кода. В распечатке выше Profile.print(format=:flat, sortedby=:count) мы можем видеть количество вызовов каждого метода. И, в данном случае, мы легко можем видеть, что fib_r и fib имеют большое количество вызовов, но рекурсия здесь не видна. Если мы изменим формат вывода, то легко увидим длинную рекурсию:


julia> Profile.print(format=:tree)
260 REPL[1]:2; fib(::Int64)
112 REPL[1]:1; fib_r(::Int64)
212 task.jl:333; REPL.var"##26#27"
 212 REPL.jl:118; macro expansion
  212 REPL.jl:86; eval_user_input
   212 boot.jl:330; eval
  ╎ 210 REPL[1]:2; fib
  ╎  210 REPL[1]:1; fib_r
  ╎   210 REPL[1]:2; fib
  ╎    210 REPL[1]:1; fib_r
  ╎     210 REPL[1]:2; fib
  ╎    ╎ 210 REPL[1]:1; fib_r
  ╎    ╎  210 REPL[1]:2; fib
  ╎    ╎   210 REPL[1]:1; fib_r
  ╎    ╎    210 REPL[1]:2; fib
  ╎    ╎     210 REPL[1]:1; fib_r
  ╎    ╎    ╎ 210 REPL[1]:2; fib
  ╎    ╎    ╎  210 REPL[1]:1; fib_r
  ╎    ╎    ╎   210 REPL[1]:2; fib
  ╎    ╎    ╎    210 REPL[1]:1; fib_r
  ╎    ╎    ╎     210 REPL[1]:2; fib
...

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



Более подробно см. https://github.com/vchuravy/PProf.jl .


Документирование


Заготовки для генератора документации находятся в директории проекта doc. О них уже упоминалось в статье https://habr.com/ru/post/439442/
Также, подробнее, можно посмотреть руководство из документации Julia.


Для генератора документации может быть использован отдельный Project.toml файл с тем, чтобы не смешивать основные и вспомогательные пакеты. Генератор документации собирает комментарии, примеры и прочие описания в формат, пригодный для дальнейшего размещения на веб-сайте или, просто, удобный для использования.


Установка пакета


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


  • Установка по имени пакета из официального реестра. Для этого пакет должен быть необходимо зарегистрировать. Полный реестр см. https://github.com/JuliaRegistries/General Процедуру регистрации см. https://github.com/JuliaRegistries/Registrator.jl
  • Установка по имени пакета из частного реестра. Необходимо создать этот реестр и зарегистрировать в нём пакет. См. https://julialang.github.io/Pkg.jl/v1/registries/
  • Установка пакета по имени git-репозитория. Единственная тонкость — надо обеспечить режим доступа по ключу и без пароля, в противном случае любое обновление пакетов будет автоматически приводить к сканированию этого репозитория, для чего Julia будет требовать пароль и стопорить процесс, если это было активировано скриптом.
  • Установка по имени локальной директории, в которой находятся файлы проекта.

В тех случаях, когда результатом является приложение или сервис, возможны альтернативные варианты. Например, приложение может быть непосредственно скопировано исходным кодом, выгружено при помощи git clone, или быть скопированным как скомпилированное в бинарные файлы приложение. Последние достигается с помощью PackageCompiler.jl. Однако то, как собирать серверные приложения, рассмотрим как-нибудь позже.


Cкрипты для обслуживания пакета


Если у пакета есть какие-то зависимости, необходимы действия по загрузке данных, предварительном их обсчёту (например посчитать векторы для каких-то массивов строк, посчитать нормы для будущего вычисления скалярных произведений), всё это должно быть размещено в директории deps, а запускаться из файла deps/build.jl. Этот файл будет активирован автоматически в процессе установки пакета. Если код пакета был просто скопирован, то, естественно, этот файл может быть активирован вручную. Но, в этом случае, лучше, если он будет активирован через специальные команды, позволяющие ещё и загрузить все зависимости нашего пакета. Для этого, обычно, создаётся файл build.jl, который размещается в корне проекта:


#!/usr/bin/env julia --project=@.

using Pkg
Pkg.activate(".")
Pkg.build() # Pkg.build(; verbose = true) for Julia 1.1 and up
Pkg.test() # (coverage=true)

Комментарии к файлу. Строка julia --project=@. указывает Julia подключить Project.toml из текущей директории. Впрочем, указанный способ — это запуск скрипта build.jl из командной оболочки, когда файл имеет атрибут executable. Но также, мы можем его запустить прямой командой julia --project=@. build.jl.


Команда Pkg.activate(".") переключает пакетный менеджер в контекст текущего пакета (по Project.toml).


Команда Pkg.build() приведёт к загрузке всех зависимостей и запуска их сборки, включая компиляцию сторонних C-библиотек, если это требуется. Именно в этот момент будет активирован и наш deps/build.jl, если он есть.


Строка Pkg.test() запускает тесты текущего пакета. И это нужно сделать, чтобы во-первых, убедиться что пакет установлен корректно, выполняются все необходимые действия. А во-вторых, откомпилировать наш пакет и все его зависимости с тем, чтобы при дальнейшем его запуске не требовалось ждать долгой компиляции. Закомментированный здесь аргумент coverage=true означает запуск тестов покрытия. Это не применяется при развёртывании пакета, но может быть использован при разработке. Но такой файл build.jl не должен дойти до этапа внедрения.


Также, в проекте могут присутствовать скрипты для систем непрерывной интеграции. Они уже были упомянуты выше, в разделе генерации проекта с помощью PkgTempletes. Поскольку подобных систем много — это и Gitlab CI, и Travis CI, стредства от GitHub и многие другие, то рассматривать их здесь не будем.


Заключение


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


Ссылки


Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 21

    +2
    Рассчитывал увидеть в начале статьи информацию о Julia, какие области применения и т.п. Вы простите, но первый раз слышу.
      +3

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


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

        +5
        не понмиаю, почему вы так сужаете область применения Julia? У меня она полностью вытеснила python в т.ч. в работе. Julia — это тот же python, тока:
        • без проблем с производительностью
        • без глобальной блокировки интерпретаторa
        • с опциональной типизацией с выводом типов
        • с, вообще говоря, зависимыми типами. Раньше в документации так и писали, но потом убрали из-за того, что в julia это не то же самое и не так круто как в idris, но когда люди читают «зависимые типы», то ждут именно чего то в стиле idris...
        • с отличными средствами интроспекции и оптимизации кода, когда вызываешь что то в стиле @code_llvm custom_code, смотришь генерируемый llvm-код, подправляешь, обычно, типы, снова смотишь… добиваясь идеального кода, чтобы julia все могла вывести статически, чтобы в рантайме не было лишних выделений памяти или диспатчеризации методов… кайф...
        • с макросами
        • с множественной диспатчеризацией


        При этом она позволяет органично использовать экосистему python с помощью github.com/JuliaPy/PyCall.jl

        @pyimport math 
        # с этого момента math - полноценный julia-модуль
        m = math
        m.sin(m.pi/4)
        


        Вообще говоря, можно подконнектиться к существующему python-процессу…

        Самое главное, что julia разрывает порочный круг, когда прототипируешь на python, но потом это начинает работать слишком медленно, переписываешь на что то более быстрое и менее интерактивное… и в этом момент получается две версии одного кода, которые надо синхронизировать и каждое изменение в прототипе может потребовать переписывать ускоренную версию. А на juliа прототипируешь, уже получая большую производительность… Но в случае чего достаточно расставить типы в критических местах(массивы, циклы) и проблема с производительностью решена.

        А поддержка синтаксиса в стиле matlab/octave/numpy — это, скорей, приятный бонус. Синтаксический сахарок. Да и, на самом деле, для питона поболее математических библиотек…
          +3
          не понмиаю, почему вы так сужаете область применения 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.

            +2
            Я уже несколько лет активно присматриваюсь к Julia. В основном я пытаюсь заменить Питон для научных расчетов на что-то другое. Но сейчас для меня будущее Julia все-таки под вопросом.

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

            Питон меня полностью устраивает как язык общего уровня. Крайне нравится идеология векторизованных вычислений через NumPy. Но еще пару лет тому назад меня расстраивали три вещи: а) не всегда можно все выразить через NumPy, да и сам NumPy местами медленный, b) GIL, и сложность с параллельными вычислениями и c) отсутствие типизации, что важно для сложных проектов. Все эти проблемы решались через Cython, но код при этом становился крайне плохо читаемым.

            Но за последние несколько лет Python практически решил все эти проблемы: a) появилась Numba, JIT компиляция на лету; теперь достаточно всего пары аннотаций для максимально производительного параллельного кода, b) в python 3.8 появилась концепция субинтерпретаторов с экспериментальным API для shared memory, т.е. проблемы GIL уже почти нет и c) очень неплохая система аннотации типов.

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

            Но вообще, я очень болею за Julia! Это важный проект для всей области научных расчетов. Мне очень не хватает в Julia простой компиляции в нативный код. Было бы это просто (например, как в nim с его проектом nimpy), я пробовал бы потихоньку на julia писать бинарные модули и использовать из Python.
              +2
              Мне очень не хватает в Julia простой компиляции в нативный код.

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


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

                +1
                Есть github.com/JuliaLang/PackageCompiler.jl
                Можно сгенерировать запускаемый бинарник. Только код должен быть оформлен в пакет и быть установлен как пакет. А бинарник будет довольно жирным.
                Да, я его как раз и пробовал. Но для меня пока и процесс компиляции, и результат оказались довольно тяжеловесными. Надеюсь, что компилятор будет постепенно совершенствоваться.
                Нам удавалось избегать использования Питона где бы то ни было. И, как раз сейчас, я сомневаюсь в его будущем и целесообразности каких-либо инвестиций в него.
                Хмм… Ну не знаю. Пока я наблюдаю как раз обратную картину взрывного роста использования Питона в научных расчетах. Почти каждая крупная компания недавно выдвинула свою инициативу развития Python. Вот некоторые примеры
                — Intel: software.intel.com/distribution-for-python, полная интеграция со своими флагманскими библиотеками для высокопроизводительных расчетов Intel Math Kernel Library, Intel Data Analytics Acceleration Library
                — NVidia: developer.nvidia.com/how-to-cuda-python, полная поддержка компиляции Python для GPU
                — Google: это TensorFlow, сейчас один из важнейших продуктов для Google,
                — Netflix: практически полностью построен на Python, medium.com/netflix-techblog/python-at-netflix-bba45dae649e, тут как раз и преимущество Python, что на нем можно писать не только научные расчеты, а строить всю инфраструктуру
                — одно из главных открытий в физике последних лет, непосредственно измеренные гравитационные волны, были рассчитаны на Python: pycbc.org,
                — много много других

                Вот еще интересная публикация о статистике использования фреймворков для машинного обучения, собранная по данным упоминания на самых популярных научных конференциях: thegradient.pub/state-of-ml-frameworks-2019-pytorch-dominates-research-tensorflow-dominates-industry. Там говорится, что по большому счету остались только два самых популярных фреймворка: TensorFlow и PyTorch, оба это Python.
                На счёт библиотек на каждый чих, большинство из них давно заброшены, имеют проблемы стабильности и совместимости с новыми версиями языка.
                Вот это все-таки не так. Все хоть как-либо значимые библиотеки уже давно собраны в очень качественные дистрибутивы с полной поддержкой последних версий Python и регулярным обновлением под три платформы: Windows, Linux, Mac. Вот наиболее популярный пакет www.anaconda.com/distribution/#download-section. Мне сложно придумать типовую математическую задачу, которая не была бы уже в том или ином виде реализована в этом дистрибутиве.
                  +3
                  • 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 и они всё исправят. В Питоне — не знаю. Авторы библиотек только за свои куски отвечают. Да и, вообще, не факт, что исправят, потому что процесс принятия решения в Питоне стал слишком тяжеловесным.


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

                    +2
                    Спасибо за ссылки! Да, это все интересные иницативы. Посмотрим, как оно все будет развиваться. В любом случае я рад, что область научных вычислений сейчас активно идет вперед.
                    +2
                    Да, я его как раз и пробовал. Но для меня пока и процесс компиляции, и результат оказались довольно тяжеловесными. Надеюсь, что компилятор будет постепенно совершенствоваться.

                    Это не сложно делается. На тестовом сервисе мы это отработали. Но в статью, пока, не готов оформить. Хотелось бы ещё мелочи с докером и HTTP.jl вычистить. А вообще, разбирались с этим тут: https://discourse.julialang.org/t/packagecompiler-sockets-web-app/28793


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

                      0
                      TF большей частью написан на C++, и его можно использовать из Julia, Python там идёт просто как интерфейс.
                        +1
                        Использовать то можно, конечно. Но дело не в гипотетической возможности использования, а в чистоте и продуктивности решения.

                        Python это «официальный» API для Tensorflow. По статистике на github.com/tensorflow/tensorflow Python составляет более 30% кодовой базы, что для совсем тонкого интерфейса многовато. Да и вообще использование low-level C/C++ модулей распространенное решение в экосистеме Python, это делается очень просто и органично. Python тут выступает очень эффективным «клеем» для интеграции low-level функций.

                        Для экосистемы Julia использование C/С++ модулей не слишком «спортивно». Julia позиционируется как язык, способный конкурировать с C/C++ на его поле за счет развитой системы типов с диспетчеризацией вызовов и JIT компиляции. Иделогия Julia как раз в том, чтобы иметь на высоком уровне выразительный язык типа MatLab для научных вычислений, но при этом иметь возможность получать произодительность нативной плафтормы. Поэтому оборачивание C/C++ TensorFlow в Julia выглядит как пренебрежение идеологией Julia.
                          +2
                          Оборачивание уже имеющегося кода на C/C++ вполне нормально вписывается в идею julia. Речь о том чтобы при написании новых проектов использовать один язык, а не переписывать все имеющиеся библиотеки на него. Причём сишный код очень удобно оборачивается, и оверхеда при вызове в обе стороны нет.
                            +1
                            Не понимаю, зачем загонять себя в какую-либо идеологию. Python от своей ушёл, и ничего, живёт.
                          +2

                          По поводу задач, которые в Питоне не решаются (быстро) — Ракаукаса можно почитать (http://www.stochasticlifestyle.com/).


                          Если кратко — нелинейные диффуры. Правую часть зачастую сложно записать в векторизованном виде, и набор солверов в SciPy довольно скудный.


                          Сейчас, конечно, Julia — больше для энтузиастов. Библиотек мало, готовые решения часто не очень-то готовые и т.д. Что там будет дальше в конкуренции с питоном — неясно, но в области научных расчетов мне как-то вот просто питон не хочется. Хочется типы, мультиметоды, более близкую к математической запись и возможность писать без отступов.

                      +1
                      можно подробней про грязный код? Пожалуй, все используемые мной питоновские библиотеки — грязные… Но, пока, проблем не было… даже очень все хорошо… Ждать, что может прилететь?

                      Коркретно по pandas, numpy, scipy я оценивал готовность pycall. Скажем, mean — «грязный» вызов из нативной части pandas, прекрасно работает:

                      @pyimport pandas
                      pd = pandas
                      s = pd.Series([1, 2, 3, 4, 5, 6])
                      s.mean() # => 3.5


                      pytorch обязательно попробую на днях прикрутить…
                        +1

                        Нашел стек. Это была машинка с CentOS 7, но что-то подобное я ловил и на MacOS. Именно проблема с некоторыми питоновскими библиотеками в их зависимостях libstdc++. Возможно, это можно исправить полной пересборкой на своей платформе, но кому же эта красота нужна....


                        Возможно, надо править пути поиска или делать ссылки, но, тоже, не слишком интересное занятие. Тот код просто вычистили от pandas.


                        Resolving package versions…
                        loaded
                        ERROR: LoadError: LoadError: LoadError: InitError: PyError ($(Expr(:escape, :(ccall(#= /home/ml/.julia/packages/PyCall/RQjD7/src/pyeval.jl:39 =# @pysym(:PyEval_EvalCode), PyPtr, (PyPtr, PyPtr, PyPtr), o, globals, locals))))) <class 'ImportError'>
                        ImportError("/usr/bin/../lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /home/ml/anaconda3/lib/python3.6/site-packages/pandas/_libs/window.cpython-36m-x86_64-linux-gnu.so)",)
                        File "/home/ml/.julia/packages/PyCall/RQjD7/src/pyeval.jl", line 2, in const Py_file_input = 257
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/init.py", line 42, in from pandas.core.api import *
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/api.py", line 10, in from pandas.core.groupby.groupby import Grouper
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/groupby/init.py", line 2, in from pandas.core.groupby.groupby import (
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/groupby/groupby.py", line 49, in from pandas.core.frame import DataFrame
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py", line 74, in from pandas.core.series import Series
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/series.py", line 3978, in Series._add_series_or_dataframe_operations()
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/generic.py", line 8891, in _add_series_or_dataframe_operations
                        from pandas.core import window as rwindow
                        File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/window.py", line 36, in import pandas._libs.window as _window
                          +1
                          Я вижу, что тут используется дистрибутив Anaconda. Большая часть таких ошибок лечится одной командой anaconda package manager:
                          $ conda install libgcc
                          
                            0
                            Тоже никаких проблем с PyCall и Pandas не испытывал — стабильно работает. Это, видимо, у вас какие-то особенности установки.
                              +1

                              Возможно, что просто никто не хотел разбираться. Ставили через https://github.com/JuliaPy/Conda.jl


                              Но в любом случае, DataFrames быстрее.


                              PS: Если PyTorch заведётся из под Julia, то это лишний аргумент проектировать продукты именно на Julia, постепенно заменяя питоновские куски.

                                +3
                                DataFrames-то пусть и быстрее, но pandas пока имеет более широкий охват более редко используемых функций, чем julia-библиотеки. Например, я пользуюсь ею для чтения fixed-width файлов (pandas.read_fwf), которого в готовом виде в julia не нашёл, и ранее для чтения некоторых сложных csv. Но с csv в последнее время ситуация в julia существенно улучшилась, и проблем у меня с этим больше не встречается.

                Only users with full accounts can post comments. Log in, please.