Кластер: запуск на нескольких компьютерах
Предпочтительный способ запустить кластер
... состоит в том, чтобы использовать параметры командной строки во время запуска julia. Он позволяет отделить программый код от кофигурации кластера и загрузить на все воркеры необходимые модули с автоматическим импортом функций в пространства имен всех воркеров.
См. julia --help или документацию на сайте.
Возможно программно запускать, настраивать и останавливать воркеры
Исследование этого способа дает приблизительное представление, о том как работает встроенный менеджер кластера.
С компьютера (comp1) можно запустить один или несколько процессов julia, работающих на другом компьютере (comp2). Это можно сделать либо: 1) указав параметры командной строки при запуске главного julia-процесса 2) либо программно - вызывая с главного процесса функции добавления новых процессов.
В любом случае: 1) компьютер comp2 должен быть доступен для выполнения команд с компьютера comp1 по ssh с беспарольным доступом (по ключам). См. документацию по ssh. 2) на обоих компьютерах по должна быть доступна одна и та же версия julia, расположенная по одному и тому же абсолютному пути. При несоблюдении этих условий, при попытке с главного процесса запустить удаленный, будет возвращена ошибка.
Рассмотрим пример программного управления:
На comp1 запускаем REPL: julia Проверим, на каком компьютере запущен этот процесс:
run(`hostname`) # - comp1 - возвращена строка с именем текущего хоста
Проверим количество запущенных процессов (локальных или удаленных):
nprocs() # 1 - возвращено число
Запустим на comp2 новый процесс:
addprocs(["comp2"]) # 2 - возвращен номер в кластере (не путать с номером процесса в системе) запущенного процесса.
nprocs() # 2 - теперь запущено два процесса
Попросим второй процесс выполнить run(hostname
) и посмотрим, что он вернет:
remotecall_fetch(2,run,`hostname`) # From worker 2: comp2
Обратите внимание: удаленному процессу (2) мы передаем отдельно имя вызываемой функции (run) и отдельно - параметр для этой функции (объект hostname
типа Cmd).
Любые дополнительные процессы кластера по умолчанию не подключают никаких дополнительных библиотек. Это нужно сделать либо программно из главного процесса либо указать в параметрах командной строки при запуске кластера.
Пусть просесс 2 дополнит пути поиска библиотек, чтобы мы могли подключить свой модуль, расположенный по пути /home/my_user_name/path_to_my_libs/src на машине comp2:
remotecall_fetch(2, push!, LOAD_PATH, "/home/my_user_name/path_to_my_libs/src" )
В ответе REPL выведет содержимое массива LOAD_PATH второго процесса.
Подключим какой-нибудь модуль: для этого в главном процессе можно выполнить команду: using
using:
В главном процессе команда using
подключит модуль my_module
и импортирует в текущее пространство имен экспортируемые модулем my_module
имена.
В воркерах, в нашем случае на просессе 2, работающем на comp2 using
только подключает модуль, не выполняя импорта. Таким образом, если в главном процессе вызвать импортированную из my_module
функию my_func
можно так: my_func(param1,param2)
, то на процессе 2 это нужно делать так: my_module.my_func(param1,param2)
, а полностью (с использованием remotecall_fech):
remotecall_fetch(2, my_module.my_func, param1, param2)
Если все сделано правильно - в ответе PERL вы увидите возвращенное с хоста comp2 функцией my_func
значение.
Note. Попытка выполнить remotecall_fetch(2, using, my_module)
приведет к ошибке, потому что using
- не функция.
Набор макросов: @spawn()
,@spawnat()
,@fetch()
,@fetchfrom()
и @everywhere()
позволяет не разлагать удаленный вызов на имя функции и ее аргументы, а использовать обычный синтаксис вызова функций.
Сейчас наиболее интересен макрос @everywhere()
, который позволяет программно выполнить на всех воркерах любое выражение (а не только сделать вызов функции) из главного процесса. Например, можно объявить функцию на всех удаленных процессах:
@everywhere myfunc1(x) = x+x
С помощью @everywhere
можно добавить путь поиска модулей и подключить модули, при этом произойдет импорт экпортируемых модулем функций в пространства имен воркеров.
@everywhere begin
push(LOAD_PATH,"/usr/local/rle/var/share3/TIKETS/juice/J/src")
using J
end
@fetchfrom выполняет выражение на указанном по номеру воркере и возвращает результат на вызвавшую машину.
Можно передать ему блок кода, внутри которого сделать нужные объявления и вызовы:
@fetchfrom 2 begin
myfunc(x) = x+x
myfunc(3)
end
В отличие от @everywhere
, объявленная функция не сохранится в пространстве имен воркера 2.