web-dev-qa-db-ja.com

doParallel "foreach"が一貫して親環境からオブジェクトを継承します: "{のエラー:タスク1が失敗しました-"関数が見つかりません... "

Foreachに問題があり、理解できません。次のコードは、試した2台のWindowsコンピューターでは失敗しますが、同じバージョンのRとdoParallelを実行している3台のLinuxコンピューターでは成功します。

_library("doParallel")
registerDoParallel(cl=2,cores=2)

f <- function(){return(10)}
g <- function(){
    r = foreach(x = 1:4) %dopar% {
        return(x + f())
    }
    return(r)
}
g()
_

これら2つのWindowsコンピューターでは、次のエラーが返されます。

_Error in { : task 1 failed - "could not find function "f""
_

ただし、これはLinuxコンピューターでは問題なく機能し、%dopar%ではなく%do%でも問題なく機能し、通常のforループでは問題なく機能します。

変数についても同じことが言えます。 _i <- 10_を設定し、return(x + f())return(x + i)に置き換える

同じ問題を抱えている他のユーザーには、2つの回避策があります。

1).exportを使用して、必要な関数と変数を明示的にインポートします。

_r = foreach(x=1:4, .export="f") %dopar% 
_

2)すべてのグローバルオブジェクトをインポートします。

_r = foreach(x=1:4, .export=ls(.GlobalEnv)) %dopar% 
_

これらの回避策の問題は、大規模で活発に開発されているパッケージにとって最も安定していないことです。いずれにせよ、foreachはforのように動作するはずです。

これを引き起こしている原因や修正があるかどうかのアイデアはありますか?


関数が動作するコンピューターのバージョン情報:

_R version 3.2.2 (2015-08-14)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: CentOS release 6.5 (Final)

other attached packages:
[1] doParallel_1.0.10 iterators_1.0.8   foreach_1.4.3
_

関数が動作しないコンピューター:

_R version 3.2.2 (2015-08-14)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

other attached packages:
[1] doParallel_1.0.10 iterators_1.0.8   foreach_1.4.3  
_
16
sssheridan

@Tensibaiは正しいです。 WindowsでdoParallelを使用しようとすると、現在のスコープにない、使用したい関数を「エクスポート」する必要があります。私の経験では、この作品を作成する方法は、次の(編集された)例です。

format_number <- function(data) {
  # do stuff that requires stringr
}

format_date_time <- function(data) {
  # do stuff that requires stringr
}

add_direction_data <- function(data) {
  # do stuff that requires dplyr
}

parse_data <- function(data) {
  voice_start <- # vector of values
  voice_end <- # vector of values
  target_phone_numbers <- # vector of values
  parse_voice_block <- function(block_start, block_end, number) {
    # do stuff
  }

  number_of_cores <- parallel::detectCores() - 1
  clusters <- parallel::makeCluster(number_of_cores)
  doParallel::registerDoParallel(clusters)
  data_list <- foreach(i = 1:length(voice_start), .combine=list,
                       .multicombine=TRUE, 
                       .export = c("format_number", "format_date_time", "add_direction_data"), 
                       .packages = c("dplyr", "stringr")) %dopar% 
                       parse_voice_block(voice_start[i], voice_end[i], target_phone_numbers[i])
  doParallel::stopCluster(clusters)
  output <- plyr::rbind.fill(data_list)
}

最初の3つの関数は現在の環境に含まれていないため、doParallelはRの新しいインスタンスを起動するときにそれらを無視しますが、parse_voice_blockは現在のスコープ内にあるため、どこにあるかがわかります。さらに、Rの新しいインスタンスごとにどのパッケージをロードするかを指定する必要があります。Tensibaiが述べたように、これはプロセスを分岐して実行しているのではなく、Rの複数のインスタンスを起動してコマンドを同時に実行しているためです。

11
brittenb

doParallelを使用して登録すると、次のように不幸になります。

_registerDoParallel(2)
_

次に、doParallelはLinuxおよびMac OS Xではmclapplyを使用しますが、Windowsでは暗黙的に作成されたクラスターオブジェクトでclusterApplyLBを使用します。これにより、Linuxではコードが機能しますが、Windowsでは失敗します。これは、mclapplyが原因で、forkを使用するとワーカーがマスターのクローンになるためです。そのため、通常は次のコードを使用してコードをテストします。

_cl <- makePSOCKcluster(2)
registerDoParallel(cl)
_

必要なすべてのパッケージをロードし、必要なすべての関数と変数をエクスポートしていることを確認してから、registerDoParallel(2)に切り替えて、それをサポートするプラットフォームでmclapplyの利点を取得します。

doParallelmclapplyを使用する場合、_.packages_および_.export_オプションは無視されますが、移植性のために常に使用することをお勧めします。


Foreachの自動エクスポート機能は、関数内で使用するとスムーズに機能しません。これは、foreachは、自動エクスポートする内容について保守的であるためです。現在の環境で定義されている変数と関数を自動エクスポートすることはかなり安全に思えますが、Rのスコープルールは複雑であるため、それ以外では危険だと思われます。

あなたの2つの回避策は、活発に開発されているパッケージに対してあまり安定していないというあなたのコメントに同意する傾向がありますが、fgがパッケージfooで定義されている場合、次に、foreach _.package_オプションを使用して、パッケージfooをワーカーにロードする必要があります。

_g <- function(){
    r = foreach(x = 1:4, .packages='foo') %dopar% {
        return(x + f())
    }
    return(r)
}
_

その場合、fは、foreachによって暗黙的または明示的にエクスポートされていなくても、gのスコープ内になります。ただし、これには、ワーカーが実行するコードがfで定義されていないため、fooが(内部関数ではなく)fooのエクスポート関数である必要があります。なので、エクスポートされた関数にのみアクセスできます。 (「エクスポート」という用語を2つの異なる方法で使用して申し訳ありませんが、回避するのは困難です。)

自動エクスポートルールを調整する必要があるかどうか常に疑問に思っているので、私はいつもあなたのようなコメントを聞くのに興味があります。この場合、パッケージで定義されている関数によってforeachループが実行された場合、クラスターワーカーは_.packages_オプションを必要とせずにそのパッケージを自動ロードする必要があると考えています。私はそれを調べて、おそらくこれをdoParalleldoSNOWの次のリリースに追加します。

7
Steve Weston