Почему parLapplyLB фактически не балансирует нагрузку?
Я проверяю parLapplyLB()
Функция, чтобы понять, что он делает, чтобы сбалансировать нагрузку. Но я не вижу никакого баланса. Например,
cl <- parallel::makeCluster(2)
system.time(
parallel::parLapplyLB(cl, 1:4, function(y) {
if (y == 1) {
Sys.sleep(3)
} else {
Sys.sleep(0.5)
}}))
## user system elapsed
## 0.004 0.009 3.511
parallel::stopCluster(cl)
Если бы он действительно балансировал нагрузку, то первое задание (задание 1), которое спит в течение 3 секунд, было бы на первом узле, а остальные три задания (задания 2:4) спали бы в течение 1,5 секунд на другом узле. В общей сложности системное время должно составлять всего 3 секунды.
Вместо этого я считаю, что задания 1 и 2 передаются узлу 1, а задания 3 и 4 - узлу 2. В результате общее время составляет 3 + 0,5 = 3,5 секунды. Если мы запустим тот же код выше с parLapply()
вместо parLapplyLB()
мы получаем одинаковое системное время около 3,5 секунд.
Что я не понимаю или делаю неправильно?
2 ответа
Для задачи, подобной вашей (и, в этом отношении, для любой задачи, для которой мне когда-либо требовалась параллель) parLapplyLB
не совсем подходящий инструмент для работы. Чтобы понять, почему нет, посмотрите, как это реализовано:
parLapplyLB
# function (cl = NULL, X, fun, ...)
# {
# cl <- defaultCluster(cl)
# do.call(c, clusterApplyLB(cl, x = splitList(X, length(cl)),
# fun = lapply, fun, ...), quote = TRUE)
# }
# <bytecode: 0x000000000f20a7e8>
# <environment: namespace:parallel>
## Have a look at what `splitList()` does:
parallel:::splitList(1:4, 2)
# [[1]]
# [1] 1 2
#
# [[2]]
# [1] 3 4
Проблема в том, что он сначала разбивает свой список заданий на подсписки одинакового размера, которые затем распределяет по узлам, каждый из которых выполняется lapply()
в данном списке. Итак, здесь ваш первый узел запускает задания на первом и втором входах, тогда как второй узел запускает задания с использованием третьего и четвертого входов.
Вместо этого используйте более универсальный clusterApplyLB()
, который работает так, как вы надеетесь:
system.time(
parallel::clusterApplyLB(cl, 1:4, function(y) {
if (y == 1) {
Sys.sleep(3)
} else {
Sys.sleep(0.5)
}}))
# user system elapsed
# 0.00 0.00 3.09
parLapplyLB
не балансирует нагрузку, потому что в ней есть семантическая ошибка. Мы нашли ошибку и предоставили исправление, смотрите здесь. Теперь дело до разработчиков, чтобы включить исправление.