多数の独立した統合を実行しています。これらの統合を別々のコアで同時に実行することにより、速度を上げることができます (マルチコア プロセッサが利用可能な場合)。問題は、R がデフォルトでシングル スレッド方式で計算を実行することです。ただし、マルチスレッドのサポートを可能にする利用可能なパッケージが多数あります。私は最近、関連するパッケージと機能に関するいくつかの追加情報とともに、こことここでいくつかの同様の質問に答えました。
さらに、@Mike Dunlavey が既に述べたように、基準に一致しないt
との値の統合を実行しないようにする必要があります。d
(現在、これらの値に対して不要な関数評価を実行しており、後で結果を 0 で上書きしています)。
以下に可能な改善を追加しました。G
クラスター ノードで関数を評価するには、関数を含む別のファイルを作成する必要があることに注意してください。以下のコードでは、このファイルが呼び出されると仮定していますfunctionG.R
スニペット:
library(doParallel)
F4 <- function(t,d) {
results = vector(mode="numeric",max(length=length(t),length(d))) # Zero vector
logicalVector <- ((d > 0) & (t > TMin))
relevantT <- t[logicalVector]
relevantD <- d[logicalVector] # when d is single element, NA values created
if(length(relevantT) > 1 | length(relevantD) > 1)
{
if(length(d)==1) # d is only one element instead of vector --> replicate it
relevantD <- rep(d,length(relevantT))
if(length(t)==1) # t is only one element instead of vector --> replicate it
relevantT <- rep(t,length(relevantD))
cl <- makeCluster(detectCores());
registerDoParallel(cl)
clusterEvalQ(cl,eval(parse("functionG.R")))
integrationResults <- foreach(i=1:length(relevantT),.combine="c") %dopar%
{
integrate(G,lower=0,upper=relevantT[i],relevantT[i],relevantD[i])$value;
}
stopCluster(cl)
results[logicalVector] <- integrationResults
}
else if(length(relevantT==1)) # Cluster overhead not needd
{
results[logicalVector] = integrate(G,lower=0,upper=relevantT,relevantT,relevantD)$value;
}
return(results)
}
私の CPU には、ハイパースレッディングが有効な 6 つの物理コア (x2) が含まれています。結果は次のとおりです。
> t = -5000:20000
> d = -5000:20000
>
> start = Sys.time()
> testF3 = F3(t,d)
> timeNeededF3 = Sys.time()-start
>
> start = Sys.time()
> testF4 = F4(t,d)
> timeNeededF4 = Sys.time()-start;
> timeNeededF3
Time difference of 3.452825 mins
> timeNeededF4
Time difference of 29.52558 secs
> identical(testF3,testF4)
[1] TRUE
このコードの実行中、コアは常に使用されているようです。ただし、コアの周りでデータをより効率的に事前に分割し、その後、個別のコアで型の適用関数を利用することで、このコードをさらに最適化できる可能性があります。
さらに最適化が必要な場合は、関数をさらに詳しく調べることもできintegrate
ます。設定をいじって、より厳密でない数値近似を許可することで、パフォーマンスを向上させることができます。別の方法として、独自の単純なバージョンの適応シンプソン求積法を実装し、個別のステップサイズをいじることができます。ほとんどの場合、このようにパフォーマンスが大幅に向上する可能性があります (近似でより多くのエラーを許容できる/許容できる場合)。
編集:
すべてのシナリオで機能するようにコードを更新:d
および/またはt
有効/無効な数値またはベクトル
コメント
への返信 @mawir: その通りです。は、 test が に評価される行にifelse(test, yes, no)
対応する値を返し、 に評価される行に対応する値を返します。ただし、 のベクトルを作成するには、最初に式を評価する必要があります。このコードは、これを示しています。yes
TRUE
no
test
FALSE
yes
yes
length(test)
> t = -5000:5
> d = -5000:5
>
> start = Sys.time()
> testF1 = F(t,d)
> timeNeededF1 = Sys.time()-start
> timeNeededF1
Time difference of 43.31346 secs
>
> start = Sys.time()
> testF4 = F4(t,d)
> timeNeededF4 = Sys.time()-start
> timeNeededF4
Time difference of 2.284134 secs
t
このシナリオでは、との最後の 5 つの値のみd
が関連しています。ただし、F1
関数内では、ベクトルを作成するためにon allとvalues が最初にifelse
評価されます。これが、関数の実行に非常に時間がかかる理由です。次に、条件を満たす要素を選択するか、そうでない場合は 0 を選択します。関数はこの問題を回避します。mapply
d
t
yes
F4
t
さらに、 andd
がベクトルではないシナリオで高速化が得られるという。ただし、このシナリオでは並列化は使用されません。t
通常、 /の一方または両方d
がベクトルであるシナリオで最大のスピードアップを得る必要があります。
ANOTHER EDIT、ローランドのコメントに応えて:別の関数ファイルを作成したくない場合は、潜在的に置き換えることができclusterEvalQ(cl,eval(parse("functionG.R")))
ますclusterExport(cl,"G")
。