Почему параллельные задания не печатаются в RStudio?

Почему сценарии, распараллеленные с помощью mclapply, печатаются в кластере, а не в RStudio? Просто из любопытства спрашиваю.

mclapply(1:10, function(x) {
  print("Hello!")
  return(TRUE)
}, mc.cores = 2)
# Hello prints in slurm but not RStudio

person Daniel Freeman    schedule 10.06.2020    source источник
comment
Просто примечание, чтобы оспорить использование вами тегов. Это как-то связано с rstudio и вообще не связано с кластером. Например, он будет печатать на моем ноутбуке с kubuntu 18.04 LTS. rstudio печально известна тем, что плохо работает с некоторыми функциями из пакета parallel, хотя я не уверен в причинах этого в CS.   -  person lmo    schedule 10.06.2020
comment
Спасибо @Imo, я обновил заголовок и теги, чтобы показать, что вопрос относится к RStudio.   -  person Daniel Freeman    schedule 10.06.2020
comment
Он печатает на моей машине с помощью Rstudio.   -  person Phil    schedule 10.06.2020
comment
@Phil, это печатает привет или просто ИСТИНА?   -  person Daniel Freeman    schedule 10.06.2020
comment
i.imgur.com/XHFuR5z.png   -  person Phil    schedule 10.06.2020
comment
Это классический пример моей проблемы. Поведение вывода в stdout и stderr зависит от среды, в которой работает R (здесь консоль RStudio), базовой операционной системы и, возможно, других вещей. Кстати, этот вывод может быть ретранслирован в RStudio Terminal, но не в RStudio Console. (Я опубликую решение, не используя parallel::mclapply() в качестве ответа ниже).   -  person HenrikB    schedule 11.06.2020


Ответы (1)


Ни одна из функций в «параллельном» пакете не гарантирует правильного отображения вывода, отправленного на стандартный вывод (stdout) или стандартную ошибку (stderr) на рабочих процессах. Это верно для всех типов подходов к распараллеливанию, например. разветвленная обработка (mclapply()) или кластеры PSOCK (parLapply()). Причина этого в том, что он никогда не был предназначен для последовательной передачи вывода.

Хороший тест — посмотреть, сможете ли вы захватить вывод через capture.output(). Например, я получаю:

bfr <- utils::capture.output({
  y <- lapply(1:3, FUN = print)
})
print(bfr)
## [1] "[1] 1" "[1] 2" "[1] 3"

как и ожидалось, но когда я пытаюсь:

bfr <- utils::capture.output({
  y <- parallel::mclapply(1:3, FUN = print)
})
print(bfr)
## character(0)

вывод не захвачен. Интересно, что если я вызову его без захвата вывода в R 4.0.1 в Linux в терминале, я получу:

y <- parallel::mclapply(1:3, FUN = print)
[1] 1
[1] 3
[1] 2

Интересно, а?

Еще одно предложение, которое вы можете получить при использовании локальных кластеров PSOCK, — установить аргумент outfile = "" при создании кластера. Действительно, когда вы попробуете это в Linux в терминале, это определенно сработает:

cl <- parallel::makeCluster(2L, outfile = "")
## starting worker pid=25259 on localhost:11167 at 17:50:03.974
## starting worker pid=25258 on localhost:11167 at 17:50:03.974

y <- parallel::parLapply(cl, 1:3, fun = print)
## [1] 1
## [1] 2
## [1] 3

Но и это дает ложные надежды. Оказывается, результат, который вы видите, появляется только потому, что его отображает терминал. Это может работать или не работать в консоли RStudio. Вы можете увидеть различное поведение в Linux, macOS и MS Windows. Наиболее важной частью понимания является то, что ваш сеанс R не вообще не видит этот вывод. Если мы попытаемся захватить его, мы получим:

bfr <- utils::capture.output({
  y <- parallel::parLapply(cl, 1:3, fun = print)
})
## [1] 1
## [1] 2
## [1] 3
print(bfr)
## character(0)

Интересно, а? Но на самом деле это не удивительно, если вы понимаете внутренние детали «параллельного» пакета.


(Отказ от ответственности: я автор) Единственная известная мне параллельная структура, которая правильно передает стандартный вывод (например, cat(), print(),...) и условия сообщения (например, message()) в основной сеанс R, - это будущее. Подробности можно прочитать в его 'Text и виньетка вывода сообщений, но вот пример, показывающий, что это работает:

future::plan("multicore", workers = 2) ## forked processing

bfr <- utils::capture.output({
  y <- future.apply::future_lapply(1:3, FUN = print)
})
print(bfr)
[1] "[1] 1" "[1] 2" "[1] 3"

Он работает одинаково независимо от базовой структуры параллелизма, например. с местными работниками PSOCK:

future::plan("multisession", workers = 2) ## PSOCK cluster

bfr <- utils::capture.output({
  y <- future.apply::future_lapply(1:3, FUN = print)
})
print(bfr)
[1] "[1] 1" "[1] 2" "[1] 3"

Это работает одинаково во всех операционных системах и средах, в которых вы запускаете R, включая консоль RStudio. Он также ведет себя одинаково независимо от того, какую будущую структуру уменьшения карты вы используете, например. (здесь) future.apply, furrr и foreach с doFuture.

person HenrikB    schedule 11.06.2020
comment
Большое спасибо за ваш подробный ответ @HenrikB, это дает мне пищу для размышлений. Я никогда не слышал о пакете future, но похоже, что он может быть полезен в проектах, где я хочу отслеживать прогресс. - person Daniel Freeman; 11.06.2020
comment
Re ... где я хочу отслеживать прогресс: обратите внимание, что future захватывает и буферизует вывод и выпускает как можно скорее - эмпирическое правило заключается в том, что вывод будет ретранслироваться, когда параллельный рабочий выполняет свою задачу, но не раньше. Если вам нужны практически оперативные обновления, см. progressr< /b>, предназначенный для работы с будущим фреймворком. - person HenrikB; 11.06.2020
comment
@HenrikB, могу я спросить продолжение. У меня похожая проблема, но я использую R для вызова исполняемого файла третьей стороны с помощью вызова system. При последовательном запуске он отображает стандартный вывод исполняемого файла, но для некоторых интерфейсов, таких как Rstudio и графический интерфейс R, он не печатается для параллельных заданий. Я пробовал ваши будущие пакеты, но он делает то же самое, без вывода. Есть ли способ получить это последовательно из параллельного вызова system? - person Cole Monnahan; 27.01.2021
comment
Нет, сможете ли вы просмотреть system() вывод на стандартный вывод (stdout) и стандартную ошибку (stderr), зависит от среды, в которой вы запускаете R. Она сильно различается, и ее трудно предсказать. . - person HenrikB; 27.01.2021
comment
Я пробовал ваши будущие пакеты, но он делает то же самое, без вывода. - Я не уверен, что именно вы пробовали с фреймворком {future}, но см. ответ выше - стандартный вывод, а также все сообщения и предупреждения действительно перехватываются фреймворком future и передаются в основном сеансе R, когда возвращаются результаты. - person HenrikB; 27.01.2021
comment
system2() позволяет вам захватывать stdout и stderr как строки символов в R, которые вы затем можете вывести, когда все будет сделано. В дополнение к этому, вы также можете заглянуть в пакет callr. - person HenrikB; 27.01.2021
comment
@HenrikB Я просто имею в виду, что пытался позвонить в систему. Вот репрекс, который терпит неудачу во всех параллельных пакетах, которые я пробовал (foreach/doParallel, future, snowfall, parallel). test.fn <- function(i) {Sys.sleep(.5); system('where Rterm')}. При последовательном запуске он всегда выводит на консоль. Параллельно он печатает только при использовании Rterm, а не Rgui или RStudio. Я также пробовал callr, и он не работает. Вся идея состоит в том, чтобы показать прогресс, выведенный этой конкретной программой, поэтому сохранение его до конца не очень помогает. Это может быть безнадежно, но я подумал, что проверю. Спасибо! - person Cole Monnahan; 27.01.2021