10

我正在尝试使用 doParallel 和 foreach 包,但是使用此处CRANpage找到的指南中的引导示例会降低性能。

library(doParallel)
library(foreach)
registerDoParallel(3)
x <- iris[which(iris[,5] != "setosa"), c(1,5)]
trials <- 10000
ptime <- system.time({
  r <- foreach(icount(trials), .combine=cbind) %dopar% {
    ind <- sample(100, 100, replace=TRUE)
    result1 <- glm(x[ind,2]~x[ind,1], family=binomial(logit))
    coefficients(result1)
    }
  })[3]
ptime

此示例返回56.87.

当我将 更改dopar为只是do顺序运行而不是并行运行时,它会返回36.65.

如果我这样做registerDoParallel(6),它会将并行时间缩短到42.11但仍然比顺序慢。 仍然比顺序更糟糕registerDoParallel(8)40.31

如果我增加到trials100,000,则顺序运行需要417.163 个工人的并行运行597.31。有 6 个并行工作人员需要425.85.

我的系统是

  • 戴尔 Optiplex 990

  • Windows 7 专业版 64 位

  • 16GB 内存

  • Intel i-7-2600 3.6GHz 四核超线程

我在这里做错了吗?如果我做我能想到的最做作的事情(用 替换计算代码Sys.sleep(1)),那么我会得到与工人数量密切相关的实际减少。我想知道为什么指南中的示例会降低我的性能,而对他们来说却加快了速度?

4

1 回答 1

9

潜在的问题是在 PSOCK 集群的工作人员上doParallel执行attach每个任务,以便将导出的变量添加到包搜索路径中。这解决了各种范围界定问题,但可能会严重影响性能,尤其是在短期任务和大量导出数据的情况下。在您的示例中,这不会在 Linux 和 Mac OS X 上发生,因为它们将使用mclapply, 而不是clusterApplyLB,但是如果您显式注册 PSOCK 集群,它将在所有平台上发生。

我相信我已经想出了如何以不损害性能的不同方式解决任务范围问题,并且我正在与 Revolution Analytics 合作以将修复程序添加到 and 的下一个版本中doParalleldoSNOW它也具有相同的功能问题。

您可以通过使用任务分块来解决此问题:

ptime2 <- system.time({
  chunks <- getDoParWorkers()
  r <- foreach(n=idiv(trials, chunks=chunks), .combine='cbind') %dopar% {
    y <- lapply(seq_len(n), function(i) {
      ind <- sample(100, 100, replace=TRUE)
      result1 <- glm(x[ind,2]~x[ind,1], family=binomial(logit))
      coefficients(result1)
    })
    do.call('cbind', y)
  }
})[3]

这导致每个工作人员只有一个任务,因此每个工作人员只执行attach一次,而不是trials / 3多次。它还会导致更少但更大的套接字操作,这可以在大多数系统上更有效地执行,但在这种情况下,关键问题是attach.

于 2013-05-24T21:26:54.973 回答