7

foreach非常に巨大な文字列ベクトルがあり、dosnowパッケージを使用して並列計算を実行したいと考えています。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]])
4

1 に答える 1

5

使用している反復のスタイルは、通常、バックエンドでうまく機能します。doMCこれは、ワーカーがttの魔法によって効果的に共有できるためですfork。しかしdoSNOWttを使用すると、実際にはほんの一部しか必要としない場合でも、大量のメモリを使用してワーカーに自動エクスポートされます。@Beasterfield による直接反復処理の提案ttはその問題を解決しますが、反復子と適切な並列バックエンドを使用することで、メモリ効率をさらに高めることができます。

このような場合、パッケージのisplitVector関数を使用します。itertoolsベクトルを一連のサブベクトルに分割し、ベクトル化の利点を失うことなく並列処理できるようにします。残念ながら では、イテレータをサポートしていないため、関数doSNOWを呼び出すためにこれらのサブベクトルをリストに入れます。ただし、バックエンドとバックエンドはそれを行いません。サブベクトルをイテレータから直接ワーカーに送信し、使用するメモリをほぼ半分にします。clusterApplyLBsnowclusterApplyLBdoMPIdoRedis

を使用した完全な例を次に示し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).

于 2013-05-05T14:41:27.163 に答える