3

これは私が理解するのが難しいと思うものです:

cl = makeCluster(rep("localhost", 8), "SOCK")

# This will not work, error: dat not found in the nodes
pmult = function(cl, a, x)
{
    mult = function(s) s*x
    parLapply(cl, a, mult)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

# This will work
pmult = function(cl, a, x)
{
    x
    mult = function(s) s*x
    parLapply(cl, a, mult)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

# This will work
pmult = function(cl, a, x)
{
    mult = function(s, x) s*x
    parLapply(cl, a, mult, x)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

引数の遅延評価のため、最初の関数は機能しません。しかし、遅延評価とは何ですか? mult() が実行されるとき、x を評価する必要はありませんか? 2 番目の方法は、x を強制的に評価するため機能します。ここで、最も奇妙なことが 3 番目の関数で発生します。mult() が追加の引数として x を受け取るようにするだけで、突然すべてが機能します!

もう 1 つのことは、parLapply() を呼び出す関数内ですべての変数と関数を定義したくない場合はどうすればよいでしょうか? 以下は間違いなく機能しません。

pmult = function(cl)
{
    source("a_x_mult.r")
    parLapply(cl, a, mult, x)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

これらすべての変数と関数を引数として渡すことができます。

f1 = function(i)
{
    return(rnorm(i))
}

f2 = function(y)
{
    return(f1(y)^2)
}

f3 = function(v)
{
    return(v- floor(v) + 100)
}

test = function(cl, f1, f2, f3)
{
    x = f2(15)
    parLapply(cl, x, f3)
}

test(cl, f1, f2, f3)

または、clusterExport() を使用することもできますが、エクスポートするオブジェクトが多数ある場合は面倒です。より良い方法はありますか?


4

1 に答える 1

5

これを理解するには、すべての機能に関連する環境があり、その環境が何であるかは、機能がどのように作成されたかによって異なることを認識する必要があります。スクリプトで単純に作成された関数はグローバル環境に関連付けられますが、別の関数によって作成された関数は、作成した関数のローカル環境に関連付けられます。あなたの例では、pmultを作成するmultため、関連付けられた環境multには正式な引数cla、およびが含まれますx

最初のケースの問題は、parLapplyが について何も知らないことです。これは、 byxの環境の一部としてシリアル化された未評価の仮引数にすぎません。はシリアル化されてクラスター ワーカーに送信されるときに評価されないため、そのコンテキストでは使用できないため、ワーカーの実行時にエラーが発生します。つまり、 が評価されるまでには手遅れです。multparLapplyxmultmultdatmultx

2 番目のケースは、 がシリアル化xされる前に評価されるため機能します。multそのため、 の実際の値はxの環境とともにシリアル化されますmult。クロージャについては知っていても、引数の遅延評価については知らなかったとしたら、期待どおりの結果が得られます。

3 番目のケースは、parLapplyハンドルxを持っているため機能します。裏技は一切行っておりません。

aこれらすべての場合において、は ( によってparLapply) 評価され、 の環境とともにシリアル化されることに注意してくださいmultparLapplyまた、チャンクに分割aし、それらのチャンクを各ワーカーに送信するため、aの環境でののコピーmultはまったく不要です。エラーは発生しませんが、multすべてのタスク オブジェクトのワーカーに送信されるため、パフォーマンスが低下する可能性があります。parLapply幸いなことに、ワーカーごとに 1 つのタスクしかないため、これは ではそれほど問題になりません。clusterApplyの場合、またはclusterApplyLBタスクの数が の長さに等しい場合は、はるかに悪い問題になりaます。

私の本の「雪」の章では、関数と環境に関連する多くの問題について話しています。いくつかの微妙な問題が関係しており、時にはそれが起こったことに気づかずに、火傷を負いやすい.

2 番目の質問については、関数をワーカーにエクスポートするためのさまざまな戦略がありますがsourceclusterExport. sourceには、解析された式が評価される場所を制御するlocal引数があり、スクリプトへの絶対パスを指定する必要がある場合があることに注意してください。最後に、リモート クラスター ワーカーを使用している場合、分散ファイル システムがない場合は、スクリプトをワーカーに scp する必要がある場合があります。

以下は、グローバル環境内のすべての関数をクラスター ワーカーにエクスポートする簡単な方法です。

ex <- Filter(function(x) is.function(get(x, .GlobalEnv)), ls(.GlobalEnv))
clusterExport(cl, ex)
于 2013-05-31T18:35:13.783 に答える