Запуск функций R на нескольких машинах

в 8:07, , рубрики: R, Блог компании Инфопульс Украина, высокая производительность, параллельное программирование, параллельные вычисления

Как мы показали в «Небольшом введении в параллельное программирование на R», одно из преимуществ R — легкость, с которой можно воспользоваться преимуществами параллельного программирования для ускорения вычислений. В этой статье мы расскажем, как перейти от запуска функций на нескольких процессорах или ядрах к запуску на нескольких машинах (с целью еще большего масштабирования и ускорения).

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

  • Подключайте более мощные параллельные библиотеки, например, Intel BLAS (доступна под Linux, OS X и Windows как часть дистрибутива Microsoft R Open). Это позволит заменить уже используемые библиотеки их параллельными версиями, благодаря чему получите ускорение (на соответствующих задачах, например, связанных с линейной алгеброй в lm()/glm()).
  • Вынесите обработку задач моделирования из R во внешнюю библиотеку для параллелизации. Это стратегия, которую используют следующие системы: методы rx от RevoScaleR (теперь Microsoft Open R), методы h2o от h2o.ai, RHadoop.
  • Используйте утилиту parallel в R, чтобы запускать функции на других экземплярах R. Эта стратегия из «Небольшого введения в параллельное программирование на R» и ряда библиотек на основе parallel. Фактически это реализация удаленного вызова процедуры через сокет или сеть.

Рассмотрим подробнее третий подход.

Фактически, третий подход представляет собой очень мелко подробленный удаленный вызов процедуры. Он зависит от передачи копий кода и данных на удаленные процессы и последующий возврат результатов. Это плохо подходит для очень маленьких задач, но отлично — для приемлемого числа средних или больших. Эта стратегия используется в библиотеке R parallel и в библиотеке Python multiprocessing (хотя с multiprocessing для Python может понадобиться ряд дополнительных библиотек, чтобы перейти от одной машины к кластерным вычислениям).

Этот метод может показаться менее эффективным и менее сложным, чем методы распределенной памяти, но полагаясь на передачу объекта, можно очень легко распространить технику от одной машины на несколько («кластерные вычисления»). Именно это мы сделаем с помощью кода на R в этой статье (переход от одной машины к кластеру приведет к многочисленным проблемам систем/сети/безопасности, и с ними придется справляться).

Вам понадобится весь код на R из предыдущей статьи. Также предполагается, что вы можете сконфигурировать ssh, или у вас есть человек, который может помочь с настройкой. Вместо запуска параллельного кластера командой “parallelCluster <- parallel::makeCluster(parallel::detectCores())” сделайте следующее.

Соберите список адресов машин, к которым вы можете применить ssh. Это сложная часть, зависит от операционной системы и может потребовать помощи, если вы раньше этого не делали. В этом примере я использую IPv4 адреса, а для Amazon EC2 — имена узлов.

В моем случае список такой:

  • Моя машина (основная): “192.168.1.235”, пользователь “johnmount”
  • Другая машина Win-Vector LLC: “192.168.1.70”, пользователь “johnmount”

Обратите внимание, мы не собираем пароли, предполагая, что установлены правильные «authorized_keys» и пары ключей в конфигурациях ".ssh" всех этих машин. Будем называть машину, с которой будет осуществляться расчет в целом, «первичной».

Обязательно стоит попробовать все эти адреса с «ssh» в терминале перед тем, как использовать их в R. Также адрес машины, выбранной «первичной», должен быть достижим с рабочих машин (т.е. нельзя использовать «localhost» или выбирать недостижимую машину «первичной»). Попробуйте вручную ssh между первичной и остальными машинами, и в обратную сторону, после чего настройки можно будет использовать в R.

Когда системные настройки позади, часть на R будет выглядеть так. Запустите ваш кластер:

primary <- '192.168.1.235'
machineAddresses <- list(
  list(host=primary,user='johnmount',
       ncore=4),
  list(host='192.168.1.70',user='johnmount',
       ncore=4)
)

spec <- lapply(machineAddresses,
               function(machine) {
                 rep(list(list(host=machine$host,
                               user=machine$user)),
                     machine$ncore)
               })
spec <- unlist(spec,recursive=FALSE)

parallelCluster <- parallel::makeCluster(type='PSOCK',
                                         master=primary,
                                         spec=spec)
print(parallelCluster)

## socket cluster with 8 nodes on hosts
##                   ‘192.168.1.235’, ‘192.168.1.70’

Вот и все. Теперь можно запускать ваши функции на нескольких ядрах нескольких машин. Для правильных задач ускорение будет существенным. Всегда имеет смысл действовать пошагово: сначала напишите простой «hello world» на вашем кластере, потом убедитесь, что меньшая версия ваших расчетов работает локально, и только после этого переносите работу в кластер.

Есть и другой способ работать с кластерами в R. Для начала нам понадобится версия R с предуслатновленными пакетами Rmpi и snow. Для этой цели я предлагаю билд R HSPCC, поддерживаемый Каспером Дэниэлом Хансеном. Вот инструкции по установке.

Мы рассмотрим еще два простых метода параллельной обработки данных в R. Первый, использующий пакет multicore, ограничен процессорами на одном узле. И хотя это может показаться серьезным недостатком, фактически может оказаться сильным преимуществом, поскольку взаимодействие между процессами будет на несколько порядков быстрее. Вторая опция — задействовать пакет snow, позволяюший использовать для вычислений подкластера MPI (интерфейсы передачи данных между узлами).

Использование нескольких ядер внутри узла

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

Запустите процесс на узле кластера с несколькими процессорами, набрав следующую команду:

qrsh -l mcmc -pe local 10-12

Обратите внимание, здесь мы запрашиваем 10-12 процессоров на одном узле в очереди mcmc. Количество доступных ядер можно посмотреть в переменной окружения NSLOTS, доступной в R с помощью такой команды:

as.integer(Sys.getenv("NSLOTS"))

Следующий шаг — запустить R и загрузить библиотеку multicore. Наконец, можно продолжить использованием команды mclapply вместо lapply в R (передавая количество используемых ядер как аргумент mc.cores).

Использование snow между несколькими узлами

Ниже — инструкция в виде простого примера, как в R поднять кластер MPI и как использовать кластер для простых вычислений.

В первую очередь я бы рекомендовал установить доступ без пароля на узлы кластера перед тем, как продолжать. Это сэкономит некоторое время (однако, насущно необходимым не является).

Для запуска кластера MPI с 12 узлами (ядрами), нужно набрать такое:

qrsh -V -l cegs -pe orte 12 /opt/openmpi/bin/mpirun -np 12 ~hcorrada/bioconductor/Rmpi/bstRMPISNOW

Эта команда должна запустить 12 экземпляров R, один из которых будет первичным. Обратите внимание, стартовый процесс был в очереди cegs. Затем вы можете пользоваться узлами, установленными с помощью mpirun, набрав в R такое:

cl <- getMPIcluster()

Затем можно просмотреть и воспользоваться нужными вам командами snow. Например,

clusterCall(cl, function() Sys.info()[c("nodename","machine")])

выдаст список узлов вашего кластера (если точнее, 11 запущенных рабочих узлов). Здесь находится список доступных команд.

Когда вычисления на кластере завершены, нужно закрыть узлы следующей командой:

stopCluster(cl)

Предупреждение: удостоверьтесь, что вы не отменили процессы R нажатием комбинации клавиш Ctrl+C. Это может вызвать проблемы со snow.

Пример: сравнение последовательной и параллельной обработки

> f.long<-function(n) {
+          xx<-rnorm(n)           
+          log(abs(xx))+xx^2
+ }

#Использование multicore

############

> system.time(mclapply(rep(5E6,11),f.long,mc.cores=11))

   user  system elapsed
 26.271   3.514   5.516

#Использование snow через MPI
############

> system.time(sapply(rep(5E6,11),f.long))

   user  system elapsed
 17.975   1.325  19.303

> system.time(parSapply(cl,rep(5E6,11),f.long))

   user  system elapsed
  4.224   4.113   8.338

Обратите внимание, параллельная обработка со snow дает улучшение более чем в 50% времени вычисления. Хотя можно предполагать, что улучшение должно составлять 10/11=91%, важно помнить, что процессоры не обязательно находятся на одном и том же узле, и взаимодействие между узлами может быть довольно медленным. Это взаимодействие может быть настолько медленным, что процедура на multicore без него может дать 40%-ое улучшение времени вычисления.

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

Автор: Инфопульс Украина

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js