如何避免使用foreach复制对象

时间:2013-05-05 11:39:41

标签: r parallel-processing mpi

我有一个非常庞大的字符串向量,并希望使用foreachdosnow包进行并行计算。我注意到foreach会为每个进程复制向量,从而快速耗尽系统内存。我试图将矢量分解为列表对象中的较小部分,但仍然没有看到任何内存使用量减少。有没有人有这个想法?以下是一些演示代码:

library(foreach)
library(doSNOW)
library(snow)

x<-rep('some string', 200000000)
# split x into smaller pieces in a list object
splits<-getsplits(x, mode='bysize', size=1000000) 
tt<-vector('list', length(splits$start))  
for (i in 1:length(tt)) tt[[i]]<-x[splits$start[i]: splits$end[i]]

ret<-foreach(i = 1:length(splits$start), .export=c('somefun'), .combine=c)   %dopar% somefun(tt[[i]])

1 个答案:

答案 0 :(得分:4)

您正在使用的迭代风格通常适用于doMC后端,因为工作人员可以通过tt的魔力有效地共享fork。但是使用doSNOWtt将使用大量内存自动导出到工作人员,即使他们实际上只需要一小部分内存。 @Beasterfield直接在tt上进行迭代的建议解决了这个问题,但是通过使用迭代器和适当的并行后端可以实现更高的内存效率。

在这种情况下,我使用isplitVector包中的itertools函数。它将矢量分成一系列子矢量,允许它们并行处理而不会失去矢量化的好处。遗憾的是,对于doSNOW,它会将这些子向量放入列表中,以便在clusterApplyLB中调用snow函数,因为clusterApplyLB不支持迭代器。但是,doMPIdoRedis后端不会这样做。他们将使用几乎一半的内存将迭代器中的子向量发送给worker。

以下是使用doMPI的完整示例:

suppressMessages(library(doMPI))
library(itertools)
cl <- startMPIcluster()
registerDoMPI(cl)
n <- 20000000
chunkSize <- 1000000
x <- rep('some string', n)
somefun <- function(s) toupper(s)
ret <- foreach(s=isplitVector(x, chunkSize=chunkSize), .combine='c') %dopar% {
  somefun(s)
}
print(length(ret))
closeCluster(cl)
mpi.quit()

当我在配备4 GB内存的MacBook Pro上运行时

$ time mpirun -n 5 R --slave -f split.R 

大约需要16秒。

您必须小心在同一台计算机上创建的工作人员数量,但降低chunkSize的值可能会让您开始更多工作。

如果您能够使用不要求所有字符串同时存在于内存中的迭代器,则可以进一步减少内存使用量。例如,如果字符串位于名为“strings.txt”的文件中,则可以使用s=ireadLines('strings.txt', n=chunkSize)