如何优化我的 R 脚本以使用“多核”

机器算法验证 r
2022-02-07 02:04:40

我在具有 4 个 CPU 的 Ubuntu-Lucid PC 上使用 GNU R。为了使用所有 4 个 CPU,我安装了“r-cran-multicore”包。由于该软件包的手册缺少我理解的实际示例,因此我需要有关如何优化我的脚本以利用所有 4 个 CPU 的建议。

我的数据集是一个具有 50,000 行和 1600 列的 data.frame(称为 P1)。对于每一行,我想计算最大值、总和和平均值。我的脚本如下所示:

p1max <- 0
p1mean <- 0
p1sum <-0
plength <- length(P1[,1])
for(i in 1:plength){
   p1max <- c(p1max, max(P1[i,]))
   p1mean <- c(p1mean, mean(P1[i,]))
   p1sum <- c(p1sum, sum(P1[i,]))
}

谁能告诉我如何修改和运行脚本以使用所有 4 个 CPU?

3个回答

对于如何使用多个内核,您已经得到了答案,但真正的问题在于您编写循环的方式。切勿在循环的每次迭代中扩展结果向量/对象如果你这样做,你会强制 R 复制你的结果向量/对象并扩展它,这一切都需要时间。相反,在开始循环之前预先分配足够的存储空间并在进行时填写。这是一个例子:

set.seed(1)
p1 <- matrix(rnorm(10000), ncol=100)
system.time({
p1max <- p1mean <- p1sum <- numeric(length = 100)
for(i in seq_along(p1max)){
   p1max[i] <- max(p1[i,])
   p1mean[i] <- mean(p1[i,])
   p1sum[i ]<- sum(p1[i,])
}
})

   user  system elapsed 
  0.005   0.000   0.005

或者您可以通过以下方式执行这些操作apply()

system.time({
p1max2 <- apply(p1, 1, max)
p1mean2 <- apply(p1, 1, mean)
p1sum2 <- apply(p1, 1, sum)
})
   user  system elapsed 
  0.007   0.000   0.006 

但请注意,这并不比正确执行循环更快,有时甚至更慢。

但是,请始终注意矢量化代码。您可以使用比循环或版本更快的方式进行行求和rowSums()和均值:rowMeans()apply

system.time({
p1max3 <- apply(p1, 1, max)
p1mean3 <- rowMeans(p1)
p1sum3 <- rowSums(p1)
})

   user  system elapsed 
  0.001   0.000   0.002 

如果我是一个赌徒,我会在我提到的第三种方法上赚钱,foreach()或者在矩阵的速度测试中使用其他多核选项,因为它们必须大大加快速度,以证明设置分离出不同 CPU 内核的独立进程。

更新:根据@shabbychef 的评论,一次求和并在平均值计算中重用是否更快?

system.time({
    p1max4 <- apply(p1, 1, max)
    p1sum4 <- rowSums(p1)
    p1mean4 <- p1sum4 / ncol(p1)
    })

   user  system elapsed 
  0.002   0.000   0.002

不在此测试运行中,但这远非详尽无遗...

使用foreachdoMC详细的解释可以在这里找到你的脚本会改变很少,行

for(i in 1:plength){

应该改为

foreach(i=1:plength) %dopar% { 

使用这些包的任何多任务脚本的先决条件是

library(foreach)
library(doMC)
registerDoMC()

注意事项。根据文档,您不能在 GUI 中使用它。

至于你的问题,你真的需要多任务处理吗?您的 data.frame 需要大约 1.2GB 的 RAM,所以它应该适合您的内存。所以你可以简单地使用apply:

p1smry <- apply(P1,1,summary)

结果将是一个包含每行摘要的矩阵。

您还可以使用包 multicore 中的函数mclapply 。那么您的脚本可能如下所示:

loopfun <- function(i) {
     summary(P1[i,])
}

res <- mclapply(1:nrow(P1),loopfun)

这将返回列表,其中第 i 个元素将是第 i 行的摘要。您可以使用sapply将其转换为矩阵

mres <- sapply(res,function(x)x)

看看降雪降雪包。有很多例子......

如果您想加速特定代码而不是学习 R 和并行性,您应该这样做

P1 = matrix(rnorm(1000), ncol=10, nrow=10
apply(P1, 1, max)
apply(P1, 1, mean)
apply(P1, 1, sum)