6

Windows 7 と Linux (SUSE Server 11 (x86_64)) の両方で R 3.0.1 を使用しています。次のコード例では、Windows ではエラーが発生しますが、Linux では発生しません。リストされているツールボックスはすべて、両方のマシンで最新のものです。Windows エラーは次のとおりです。

Error in { : task 1 failed - "NULL value passed as symbol address"

を変更する%dopar% to %do%と、Windows コードはエラーなしで実行されます。私の最初の推測では、これは Windows の構成の問題に関連していて、Rcpp と R を再インストールしようとしましたが、役に立ちませんでした。エラーはスコープに関連しているようです.f1内で関数cFuncを定義してコンパイルすると%dopar%動作しますが、予想通り、タスクごとにコンパイラを1回呼び出すため、非常に遅くなります。

エラーが発生する理由や修正方法についての提案について、誰かが洞察を持っていますか?

library(inline)
sigFunc <- signature(x="numeric", size_x="numeric")
code <- ' double tot =0;
for(int k = 0; k < INTEGER(size_x)[0]; k++){
tot += REAL(x)[k];
};
return ScalarReal(tot);
' 
cFunc <- cxxfunction(sigFunc, code)

f1 <- function(){
x <- rnorm(100)
a <- cFunc(x=x, size_x=as.integer(length(x)))
return(a)
}

library(foreach)
library(doParallel)
registerDoParallel()
# this produces an error in Windows but not in Linux
res <- foreach(counter=(1:100)) %dopar% {f1()}
# this works for both Windows and Linux
res <- foreach(counter=(1:100)) %do% {f1()}

# The following is not a practical solution, but I can compile cFunc inside f1 and then     this works in Windows but it is very slow
f1 <- function(){
library(inline)
sigFunc <- signature(x="numeric", size_x="numeric")

code <- ' double tot =0;
for(int k = 0; k < INTEGER(size_x)[0]; k++){
tot += REAL(x)[k];
};
return ScalarReal(tot);
' 
cFunc <- cxxfunction(sigFunc, code)
x <- rnorm(100)
a <- cFunc(x=x, size_x=as.integer(length(x)))
return(a)
}
# this now works in Windows but is very slow
res <- foreach(counter=(1:100)) %dopar% {f1()}

ありがとう!グスタボ

4

2 に答える 2

6

「NULL 値がシンボル アドレスとして渡されました」というエラー メッセージは異常であり、関数がワーカーにエクスポートされていないことが原因ではありません。cFuncシリアル化され、ワー​​カーに送信され、シリアル化が解除された後、関数は機能しません。また、保存されたワークスペースからロードされた場合も機能せず、同じエラー メッセージが表示されます。それはあまり驚くことではありませんinline。パッケージの動作が文書化されている可能性があります。

あなたが示したようcFuncに、ワーカーで作成することで問題を回避できます。これを効率的に行うには、各ワーカーで 1 回だけ実行する必要があります。doParallelバックエンドでこれを行うには、ワーカーの初期化関数を定義し、関数を使用して各ワーカーでそれを実行しclusterCallます。

worker.init <- function() {
  library(inline)
  sigFunc <- signature(x="numeric", size_x="numeric")
  code <- ' double tot =0;
  for(int k = 0; k < INTEGER(size_x)[0]; k++){
  tot += REAL(x)[k];
  };
  return ScalarReal(tot);
  '
  assign('cFunc', cxxfunction(sigFunc, code), .GlobalEnv)
  NULL
}

f1 <- function(){
  x <- rnorm(100)
  a <- cFunc(x=x, size_x=as.integer(length(x)))
  return(a)
}

library(foreach)
library(doParallel)
cl <- makePSOCKcluster(3)
clusterCall(cl, worker.init)
registerDoParallel(cl)
res <- foreach(counter=1:100) %dopar% f1()

を呼び出すには、PSOCK クラスター オブジェクトを明示的に作成する必要があることに注意してくださいclusterCall

あなたの例がLinuxで機能した理由は、引数なしでmclapply呼び出すと関数が使用されるregisterDoParallelのに対し、Windowsではクラスターオブジェクトが作成され、clusterApplyLB関数が使用されるためです。を使用する場合、関数と変数はシリアル化されず、ワーカーに送信されないmclapplyため、エラーは発生しません。

doParallelを使用せずにワーカーを初期化するためのサポートが含まれていればいいのですclusterCallが、まだそうではありません。

于 2013-08-15T02:58:27.200 に答える
4

私が考えることができる最も簡単な「回避策」は

1) 別のソース ファイルにコードを記述しますcFunc.c

2) でコンパイルしますR CMD SHLIB

3)通話dyn.load内で機能しますforeach

例えば、

cFunc.c
=======

#include <R.h>
#include <Rinternals.h>

SEXP cFunc( SEXP x, SEXP size_x ) {

  double tot = 0;
  for (int k=0; k < INTEGER(size_x)[0]; ++k ) {
    tot += REAL(x)[k];
  }
  return ScalarReal(tot);

}

library(foreach)
library(doParallel)
registerDoParallel()
x <- as.numeric(1:100)
size_x <- as.integer(length(x))
res <- foreach(counter=(1:100)) %dopar% { 
  dyn.load("cFunc.dll")
  .Call("cFunc", x, size_x) 
}

別の方法 (そしておそらくより良い方法) として、この関数をエクスポートして使用できる実際のパッケージを構築することを検討してください。

于 2013-08-15T02:54:32.547 に答える