16

私は非常に大きなリストXとベクトル化された関数を持っていますf。計算したいのですf(X)が、シングルコアで計算すると時間がかかります。私は48コアサーバーを持っています(アクセスできます)。の計算を並列化する最も簡単な方法は何f(X)ですか?以下は正しい答えではありません。

library(foreach)
library(doMC)
registerDoMC()

foreach(x=X, .combine=c) %dopar% f(x)

上記のコードは確かにの計算を並列化しますが、のすべての要素に個別にf(X)適用することによって並列化します。これはのベクトル化された性質を無視し、結果として物事を速くするのではなく遅くする可能があります。に要素ごとに適用するのではなく、適度なサイズのチャンクに分割して適用したいと思います。fXffXXf

Xでは、手動で48個の同じサイズのサブリストに分割し、それぞれに並行し適用fしてから、手動で結果をまとめる必要がありますか?または、このために設計されたパッケージはありますか?

誰かが疑問に思っている場合に備えて、私の特定のユースケースはここにあります。

4

5 に答える 5

6

これは古い質問ですが、これはグーグル経由でこれに遭遇したすべての人にとって興味深いかもしれません(私のように):パッケージのpvec関数を見てください。multicore私はそれがあなたが望むことを正確に行うと思います。

于 2011-08-11T08:55:13.600 に答える
5

これが私の実装です。これは、ベクトル化されたchunkmap関数、ベクトル化されるべき引数のリスト、およびベクトル化されるべきではない引数のリスト(つまり定数)を取り、引数に対して関数を直接呼び出すのと同じ結果を返す関数です。結果は並行して計算されます。関数、fベクトル引数v1、、、およびスカラー引数、の場合、以下は同じ結果を返す必要があります。v2v3s1s2

f(a=v1, b=v2, c=v3, d=s1, e=s2)
f(c=v3, b=v2, e=s2, a=v1, d=s1)
chunkapply(FUN=f, VECTOR.ARGS=list(a=v1, b=v2, c=v3), SCALAR.ARGS=list(d=s1, e=s2))
chunkapply(FUN=f, SCALAR.ARGS=list(e=s2, d=s1), VECTOR.ARGS=list(a=v1, c=v3, b=v2))

chunkapply関数がベクトル化されている引数とベクトル化されていない引数を知ることは不可能であるため、fいつ呼び出すかを指定するのはあなた次第です。そうしないと、間違った結果が得られます。通常、引数に名前を付けて、正しくバインドされるようにする必要があります。

library(foreach)
library(iterators)
# Use your favorite doPar backend here
library(doMC)
registerDoMC()

get.chunk.size <- function(vec.length,
                           min.chunk.size=NULL, max.chunk.size=NULL,
                           max.chunks=NULL) {
  if (is.null(max.chunks)) {
    max.chunks <- getDoParWorkers()
  }
  size <- vec.length / max.chunks
  if (!is.null(max.chunk.size)) {
    size <- min(size, max.chunk.size)
  }
  if (!is.null(min.chunk.size)) {
    size <- max(size, min.chunk.size)
  }
  num.chunks <- ceiling(vec.length / size)
  actual.size <- ceiling(vec.length / num.chunks)
  return(actual.size)
}

ichunk.vectors <- function(vectors=NULL,
                           min.chunk.size=NULL,
                           max.chunk.size=NULL,
                           max.chunks=NULL) {
  ## Calculate number of chunks
  recycle.length <- max(sapply(vectors, length))
  actual.chunk.size <- get.chunk.size(recycle.length, min.chunk.size, max.chunk.size, max.chunks)
  num.chunks <- ceiling(recycle.length / actual.chunk.size)

  ## Make the chunk iterator
  i <- 1
  it <- idiv(recycle.length, chunks=num.chunks)
  nextEl <- function() {
    n <- nextElem(it)
    ix <- seq(i, length = n)
    i <<- i + n
    vchunks <- foreach(v=vectors) %do% v[1+ (ix-1) %% length(v)]
    names(vchunks) <- names(vectors)
    vchunks
  }
  obj <- list(nextElem = nextEl)
  class(obj) <- c("ichunk", "abstractiter", "iter")
  obj
}

chunkapply <- function(FUN, VECTOR.ARGS, SCALAR.ARGS=list(), MERGE=TRUE, ...) {
  ## Check that the arguments make sense
  stopifnot(is.list(VECTOR.ARGS))
  stopifnot(length(VECTOR.ARGS) >= 1)
  stopifnot(is.list(SCALAR.ARGS))
  ## Choose appropriate combine function
  if (MERGE) {
    combine.fun <- append
  } else {
    combine.fun <- foreach:::defcombine
  }
  ## Chunk and apply, and maybe merge
  foreach(vchunk=ichunk.vectors(vectors=VECTOR.ARGS, ...),
          .combine=combine.fun,
          .options.multicore = mcoptions) %dopar%
  {
    do.call(FUN, args=append(vchunk, SCALAR.ARGS))
  }
}

## Only do chunkapply if it will run in parallel
maybe.chunkapply <- function(FUN, VECTOR.ARGS, SCALAR.ARGS=list(), ...) {
  if (getDoParWorkers() > 1) {
    chunkapply(FUN, VECTOR.ARGS, SCALAR.ARGS, ...)
  } else {
    do.call(FUN, append(VECTOR.ARGS, SCALAR.ARGS))
  }
}

chunkapply(f,list(x))と同じ結果が得られることを示すいくつかの例を次に示しf(x)ます。チャンキングアルゴリズムが実際に使用されるように、max.chunk.sizeを非常に小さく設定しました。

> # Generate all even integers from 2 to 100 inclusive
> identical(chunkapply(function(x,y) x*y, list(1:50), list(2), max.chunk.size=10), 1:50 * 2)
[1] TRUE

> ## Sample from a standard normal distribution, then discard values greater than 1
> a <- rnorm(n=100)
> cutoff <- 1
> identical(chunkapply(function(x,limit) x[x<=limit], list(x=a), list(limit=cutoff), max.chunk.size=10), a[a<cutoff])
[1] TRUE

「chunkapply」よりも良い名前を持っている人がいたら、それを提案してください。

編集:

別の答えが指摘してpvecいるように、私が書いたものと非常によく似た機能を持つマルチコアパッケージで呼び出される関数があります。単純なケースの場合、あなたはそれを私たちにすべきであり、あなたはそれに対するJonasRauchの答えに投票するべきです。ただし、私の関数はもう少し一般的であるため、次のいずれかに該当する場合は、代わりに私の関数の使用を検討することをお勧めします。

  • マルチコア以外の並列バックエンド(MPIなど)を使用する必要があります。私の関数はforeachを使用しているため、foreachのバックエンドを提供する任意の並列化フレームワークを使用できます。
  • 複数のベクトル化された引数を渡す必要があります。単一の引数に対してのみベクトル化されるため、たとえば、を使用pvecして並列ベクトル化加算を簡単に実装することはできません。pvec私の関数では、任意の引数を指定できます。
于 2011-04-13T17:31:08.897 に答える
3

itertoolsパッケージは、この種の問題に対処するために設計されました。この場合、私は使用しますisplitVector

n <- getDoParWorkers()
foreach(x=isplitVector(X, chunks=n), .combine='c') %dopar% f(x)

この例でpvecは、間違いなく高速でシンプルですが、たとえば、WindowsでdoParallelパッケージを使用して使用できます。

于 2013-03-14T16:23:29.090 に答える
0

Map-Reduceはあなたが探しているものかもしれません。Rに移植されまし

于 2011-04-06T19:52:37.903 に答える
0

このようなものはどうですか?Rは、使用可能なすべてのメモリを利用し、使用可能なmulticoreすべてのコアを並列化します。

library(multicore)
result = mclapply(X, function,mc.preschedule=FALSE, mc.set.seed=FALSE)
于 2011-04-06T20:12:32.820 に答える