我正在测试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)

如果它确实在平衡负载,则休眠3秒的第一个作业(作业1)将在第一个节点上,其他三个作业(作业2:4)将在另一个节点上合计1.5秒。总共,系统时间应仅为3秒。

相反,我认为作业1和2分配给节点1,作业3和4分配给节点2。这导致总时间为3 + 0.5 = 3.5秒。如果我们使用parLapply()而不是parLapplyLB()运行与上面相同的代码,我们将获得大约3.5秒的相同系统时间。

我不明白或做错了什么?

最佳答案

注意:自R-3.5.0起,已修复了OP指出并在下面说明的行为/错误。如当时R的NEWS文件中所述:

* parLapplyLB and parSapplyLB have been fixed to do load balancing
  (dynamic scheduling).  This also means that results of
  computations depending on random number generators will now
  really be non-reproducible, as documented.

原始答案(现在仅与
对于像您这样的任务(就此而言,对于我需要使用 parallel 的任何任务),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

关于r - 为什么parLapplyLB实际上没有平衡负载?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38230831/

10-13 05:51