Как написать эффективные вложенные функции для распараллеливания?

У меня есть кадр данных с двумя переменными группировки class и group. Для каждого класса у меня есть задание на график для каждой группы. В основном у меня 2 уровня на class и 500 уровней на group.

Я использую пакет parallel для распараллеливания и функцию mclapply для итерации по уровням class и group.

Мне интересно, какой лучший способ написать мои итерации. Я думаю, что у меня есть два варианта:

  1. Запустите распараллеливание для переменной class.
  2. Запустите распараллеливание для переменной group.

У моего компьютера 3 ядра работают для сеанса R, и обычно 4-е ядро ​​​​сохраняется для моей операционной системы. Мне было интересно, что если выполнить распараллеливание для переменной class с 2 уровнями, 3-е ядро ​​никогда не будет использоваться, поэтому я подумал, что будет более эффективно обеспечить, чтобы все 3 ядра работали при распараллеливании для переменной group. Я написал несколько тестов скорости, чтобы убедиться, что это лучший способ:

library(microbenchmark)
library(parallel)

f = function(class, group, A, B) {

  mclapply(seq(class), mc.cores = A, function(z) {
    mclapply(seq(group), mc.cores = B, function(c) {
      ifelse(class == 1, 'plotA', 'plotB')
    })
  })

}

class = 2
group = 500

microbenchmark(
  up = f(class, group, 3, 1),
  nest = f(class, group, 1, 3),
  times = 50L
)

Unit: milliseconds
 expr       min        lq     mean    median       uq      max neval
   up  6.751193  7.897118 10.89985  9.769894 12.26880 26.87811    50
 nest 16.584382 18.999863 25.54437 22.293591 28.60268 63.49878    50

Результат говорит о том, что я должен использовать распараллеливание для class, а не для group переменной.

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

Условие ifelse используется, потому что предыдущий код, используемый для подготовки данных для задачи построения графика, более или менее избыточен для обоих уровней class, поэтому я подумал, что было бы более эффективным линейным кодированием написать более длинную функцию, проверяющую, какой уровень class используется, чем " разделение" этой функции на две более короткие функции.

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

Эта угроза связана с этой проблемой. Но я думаю, что мой вопрос касается обеих точек зрения:

  • Код красивый и понятный
  • Скорость работы

Спасибо


person Archymedes    schedule 07.03.2020    source источник


Ответы (1)


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

parts <- split(df, c(df$class, df$group))
mclapply(parts, some_function)

Во-вторых, распределение задач по нескольким ядрам требует больших вычислительных затрат и может свести на нет любые выгоды, которые вы получаете от распараллеливания сценария. Здесь mclapply разбивает задание на любое количество имеющихся у вас узлов и выполняет разветвление один раз. Это намного эффективнее, чем вложение двух циклов mclapply.

person Daniel Freeman    schedule 13.04.2020