R parLapply не параллельно

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

Я получаю немного неловкое поведение при использовании кластеров, определенных внутри функций моего пакета, когда функция parLapply назначает работу работнику и ожидает его завершения, чтобы назначить работу следующему работнику. Или, по крайней мере, это то, что, по-видимому, происходит, наблюдая за файлом журнала "cluster.log" и списком запущенных процессов в оболочке unix.

Ниже приведена макетная версия оригинальной функции, объявленной внутри моего пакета:

.parSolver <- function( varMatrix, var1 ) {

    no_cores <- detectCores()

    #Rows in varMatrix
    rows <- 1:nrow(varMatrix[,])

    # Split rows in n parts
    n <- no_cores
    parts <- split(rows, cut(rows, n))

    # Initiate cluster
    cl <- makePSOCKcluster(no_cores, methods = FALSE, outfile = "/home/cluster.log")
    clusterEvalQ(cl, library(raster))
    clusterExport(cl, "varMatrix", envir=environment())
    clusterExport(cl, "var1", envir=environment())


    rParts <- parLapply(cl = cl, X = 1:n, fun = function(x){
        part <- rasterize(varMatrix[parts[[x]],], raster(var1), .....)
        print(x)
        return(part)
        })

    do.call(merge, rParts)
}

ЗАМЕТКИ:

  • Я использую makePSOCKcluster, потому что я хочу, чтобы код запускался как на Windows, так и на Unix-системах, хотя эта конкретная проблема проявляется только в Unix-системах.
  • Функции растрирования и растра определяются в библиотеке (растре), экспортируемой в кластер.

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

library(myPackage)

varMatrix <- (...)
var1 <- (...)
result <- parSolver(varMatrix, var1)

описанная проблема появляется.

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

Я что-то здесь упускаю? Заранее спасибо.

1 ответ

Решение

Я не думаю parLapply работает последовательно. Скорее всего, он работает неэффективно, поэтому кажется, что он работает последовательно.

У меня есть несколько предложений по его улучшению:

  • Не определяйте рабочую функцию внутри parSolver
  • Не экспортируйте все varMatrix каждому работнику
  • Создать кластер за пределами parSolver

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

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

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

# Define worker function outside of any function to avoid
# serialization problems (such as unexpected variable capture)
workerfn <- function(mat, var1) {
    library(raster)
    mat * var1
}

parSolver <- function(cl, varMatrix, var1) {
    parts <- splitIndices(nrow(varMatrix), length(cl))
    varMatrixParts <- lapply(parts, function(i) varMatrix[i,,drop=FALSE])
    rParts <- clusterApply(cl, varMatrixParts, workerfn, var1)
    do.call(rbind, rParts)
}

library(parallel)
cl <- makePSOCKcluster(3)
r <- parSolver(cl, matrix(1:20, 10, 2), 2)
print(r)

Обратите внимание, что это использует преимущества clusterApply Функция для перебора списка строк-фрагментов varMatrix так что всю матрицу не нужно отправлять всем. Это также избегает звонков clusterEvalQ а также clusterExport, упрощая код, а также делая его немного более эффективным.

Другие вопросы по тегам