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


15

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

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

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?


上面的程序有错误:该行应为“ for(i in 1:plength)”
Simon Byrne

你是严厉的,谢谢!
Produnis,2011年

1
这不属于StackOverflow吗?
R_Coholic 2011年

1
这确实属于StackOverflow。这里根本没有统计问题。仅是一般的编程问题。
JD Long

Answers:


11

使用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)

非常感谢你。没错,通过“应用”可以优化脚本。我只是使用我的脚本作为一个最小示例,以便通过...获得消息。非常感谢,您的答案正是我想要的!
Produnis,2011年

15

您已经获得了有关如何使用多个内核的答案,但真正的问题是编写循环的方式。切勿在循环的每次迭代中扩展结果向量/对象。如果这样做,则强制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

不在本次测试中,但这远非详尽无遗...


FWIW,Matlab在预分配和扩展向量方面存在相同的问题,并且是经典代码“ blooper”。除了你的赌注,它可能是更快地使用的结果rowSums来计算该行方式(除非我失踪有关的东西 Na或NAN)。第三种方法中的代码对每一列进行两次求和。
shabbychef 2011年

@shabbychef,您会感到惊讶(请参阅我编辑的答案)。是的款项名义上计算两次,但rowSumsrowMeans高度优化的编译的代码,以及我们得到的只是计算的金额一次,我们在解释代码做平均计算再次松动。
恢复莫妮卡-G.辛普森

@Gavin Simpson:没那么快:试试吧system.time({ for (iii in c(1:1000)) { p1max3 <- apply(p1, 1, max) p1mean3 <- rowMeans(p1) p1sum3 <- rowSums(p1) } }),类似地system.time({ for (iii in c(1:1000)) { p1max4 <- apply(p1, 1, max) p1sum4 <- rowSums(p1) p1mean4 <- p1sum4 / ncol(p1) } });不重新计算总和的版本在我的计算机上需要1.368秒;确实需要1.396。再次,远非详尽无遗,但更具说服力……
shabbychef 2011年

@shabbychef我们必须对什么是或不是引人注目;-)事实上,你更严格的模拟加强我的主要观点,即不同的想法rowMeansrowSums在高效,优化的编译的代码来实现他们将很难被击败。
恢复莫妮卡-G.辛普森

@加文·辛普森 实际上,我的示例的问题在于,大部分时间都用在应用部分中,以计算最大值。我同意您的看法,基于C的矢量化函数rowMean很难通过通用R工具来击败*apply。但是,你似乎表明,它是速度更快,总结10000号两次通过rowMeanrowSum,而不是只有一次,使用的r内置除法运算符。我知道R存在一些效率问题(例如,最近发现了花括号与括号问题),但这似乎很疯狂。
shabbychef 2011年

1

看看降雪降雪包。那些例子很多...

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

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

请帮助我修改脚本...
Produnis 2011年

2
这些只是对您隐藏了循环。@Produnis代码的真正问题是强制复制正在进行,因为结果向量在循环的每次迭代中都得到扩展。
恢复莫妮卡-G.辛普森

降雪包可以扩展Gavin的解决方案,例如说“蛋糕”。程序包具有大量的修改为多取芯的apply函数。对于apply函数,可以使用sfApply(<yourarguments as apply>)。降雪也有据可查。我应该指出,在多核处理器上执行此操作不需要额外的软件。有关sfLapply示例,请参见stackoverflow.com/questions/4164960/…
RomanLuštrik2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.