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
, упрощая код, а также делая его немного более эффективным.