Вам нужно знать определенные вещи, когда вы хотите использовать распараллеливание.Первый - это накладные расходы из-за связи и, возможно, сериализации.В качестве очень грубого примера рассмотрим следующее:
num_cores <- 2L
cl <- makeCluster(num_cores, type="FORK")
registerDoParallel(cl)
exec_time <- system.time({
a_list <- foreach(i=1L:2L) %dopar% {
system.time({
m_m <- m[i,,]
new_m_m <- calcMat(m_m)
})
}
})
В моей системе exec_time
показывает прошедшее время в 1,264 секунды, тогда как истекшее время в a_list
показывает 0,003 секунды.Таким образом, очень упрощенно, мы могли бы сказать, что 99,7% времени выполнения были накладными.Это связано с гранулярностью задачи .Различные типы задач выигрывают от различных типов детализации.В вашем случае вы можете воспользоваться кусками ваших задач грубым способом.По сути, это означает, что вы группируете количество задач таким образом, чтобы уменьшить накладные расходы на связь:
chunks <- splitIndices(N, num_cores)
str(chunks)
List of 2
$ : int [1:4096] 1 2 3 4 5 6 7 8 9 10 ...
$ : int [1:4096] 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 ...
Каждый блок имеет индексы для нескольких задач, поэтому вам необходимо соответствующим образом изменить код:
exec_time_chunking <- system.time({
a_list <- foreach(chunk=chunks, .combine=c) %dopar% {
lapply(chunk, function(i) {
m_m <- m[i,,]
calcMat(m_m)
})
}
})
Вышеизложенное завершилось в моей системе за 17,978 секунд с использованием 2 параллельных рабочих.
РЕДАКТИРОВАТЬ: в качестве примечания, я думаю, что обычно нет веских оснований для установки числа параллельных рабочих на detectCores() - 1L
,поскольку основной процесс R должен ждать завершения всех параллельных рабочих, но, возможно, у вас есть другие причины, возможно, поддержание реакции системы.