並列計算(snowfall)

マルチコアが主流となってきているので、並列計算のパッケージである「snowfall」を紹介します。
なお、特に断りが無い場合はSocketクラスターを使用しています。

現在編集中


目次


並列計算で計算速度は向上するか?

並列化できる割合によって、理論上の限外が変わってきます。
アムダールの法則を参照)
なお、かえって遅くなる場合もあります。
並列化の概要は、このページ後述の並列計算事始めを参照してください。

対象者

計算速度を向上させたいが、煩わしいことはなるべく避けたい。
そのような方にとって、並列計算パッケージ「snowfall」は一つの方法かもしれません。

本記事は、以下の方を想定しています。

レベルとしては以下の方

高速化の手段はいくつかありますので、適切な方法を用いてください。
CRAN Task View: High-Performance and Parallel Computing with R

snowfallの利点

とりあえず動かす

  1. ライブラリーの読み込み
  2. sfInitでクラスターを準備する。
  3. (必要があれば)ワーカーに変数やオブジェクトを渡す。
  4. snowfall 関数を使用して並列計算を行う。
  5. クラスターを止める
    > library(snowfall)
     要求されたパッケージ snow をロード中です 
    > sfInit(parallel=TRUE, cpus=4)
    R Version:  R version 3.2.2 (2015-08-14) 
    
    snowfall 1.84-6 initialized (using snow 0.3-13): parallel execution on 4 CPUs.
    
    > a <- 1:10
    > sfExport("a")
    > sfSapply(11:20, function(x){x + sum(a)})
     [1] 66 67 68 69 70 71 72 73 74 75
    
    #検証
    > sapply(11:20, function(x){x + sum(a)})
     [1] 66 67 68 69 70 71 72 73 74 75

代表的な関数 対応表

basesnowsnowfallコメント
applyparApplysfApply
lapplyparLapplysfLapply
sapplyparSapplysfSapply
apply(X, MARGIN=1, FUN, ...)parRapplysfRapply
未実装(Version 1.84-4)
snowfallでは関数は定義されているが、以下のとおり。
sfRapply does not exists yet. Use Snow's parRapply instead.

sfApplyで代用する。
apply(X, MARGIN=2, FUN, ...)parCapplysfCapply
未実装(Version 1.84-4)
snowfallでは関数は定義されているが、以下のとおり。
sfCapply does not exists yet. Use Snow's parCapply instead.

sfApplyで代用する。
mapplyclusterMapsfClusterMap?
未実装(Version 1.84-4)
snowfallでは関数は定義されているが、以下のとおり。
Currently no wrapper for clusterMap

snowfallの関数では代用できないため、並列化をする場合はsnowパッケージを使用する。
%*%parMMsfMM

snow snowfall 各種関数 対応表

カテゴリーは主観で分けています。

カテゴリーsnowsnowfallコメント
クラスター作成makeClustersfInitすべてsfInitで行えます。
makeSOCKcluster
makePVMcluster
makeMPIcluster
makeNWScluster
オブジェクトなどの引渡しclusterExportsfExport各クラスターに特定のオブジェクトをエクスポートします。
sfExportAll?各クラスターに全てのオブジェクトをエクスポートします。
sfRemove各クラスターから特定のオブジェクトを削除します。
sfRemoveAll?各クラスターから全てのオブジェクトを削除します。
sfLibrary各クラスターにライブラリーを読み込ませます。
sfSource各クラスターにソースファイルを読み込ませます。
並列基本関数parApplysfApply
parLapplysfLapply
parSapplysfSapply
parRapplysfRapply未実装(Version 1.84-4)
parCapplysfCapply未実装(Version 1.84-4)
parMMsfMM%*%
clusterMapsfClusterMap?未実装(Version 1.84-4)
その他並列関数clusterApplysfClusterApply?sfClusterApply?はクラスター数以下の要素しか受け取れない。
(使えない関数の気がします。)
clusterApplyLBsfClusterApplyLBclusterApplyでは、1要素の計算が終わるまですべてのクラスターが待機していましたが
これは1要素の計算が終了次第次の計算に移ります。
(Load Balanced version of sfClusterApply?)
sfClusterApplySR1要素の計算が終了するごとにsaveを行い、予期せぬ終了(サーバーメンテナンスなど)があった場合でも、続きから計算を始めることが出来ます。
(ただし、saveを頻繁に行うので遅くなるようです。)
(Saves intermediate Results)
clusterCallsfClusterCall?各クラスターに同一の命令文を実行させます。
clusterEvalQsfClusterEval?各クラスターに同一の未評価の命令文を実行させます。
乱数clusterSetupRNGsfClusterSetupRNG乱数の生成をします。
(set.seedのようにNULLによって乱数の生成が出来ないため、少々扱いにくいです。)
clusterSetupRNGstreamsfClusterSetupRNGstream
clusterSetupSPRNGsfClusterSetupSPRNG
クラスターの終了stopClustersfStop
クラスターの使用状況snow.timeクラスターの使用状況を調査出来る関数です。
printprintを拡張し、snow.timeで取得したクラスターの使用状況表示します。
plotplotを拡張し、snow.timeで取得したクラスターの使用状況表示します。
デバッグsfCat各クラスターにcat文を実行させます。
各種情報等clusterSplitsfClusterSplit?helpを参照の事。
setDefaultClusterOptions?
getMPIcluster
sfParallel
sfIsRunning?
sfCpus
sfNodes
sfGetCluster?
sfType
sfSession
sfSocketHosts?
sfSetMaxCPUs
sfRestore

どの並列関数を使用するべきか

snow.time でクラスターの使用状況を探る

関数snow.timeを使用することによって、各クラスターの使用状況を確認できます。

> st.hoge <- snow.time(hoge)
> st.hoge
elapsed    send receive  node 1  node 2  node 3  node 4 
  21.75    0.00    0.02   13.77   12.24   14.50   12.00 

snow.timeで得られた結果は、plotで詳細に状況を確認できます。

> plot(st.hoge)

applyとclusterApply(snow)

applyでの実行時間が53.03秒に対して、並列計算を行ったclusterApplyは21.76秒!
何と2倍以上の高速化が実現!素晴らしい!
&ref(): File not found: "fig01_apply.jpg" at page "並列計算(snowfall)";&ref(): File not found: "fig01_clusterApply.jpg" at page "並列計算(snowfall)";

clusterApply(snow)とclusterApplyLB(snow)

clusterApplyは、各クラスターに1個引数を渡し、すべてのクラスターの計算終了を待って次の引数をクラスターに渡します。
そのため、実行時間に差がある場合は待機状態になるクラスターが発生してしまいます。
clusterApplyは確かに速くなっているが、クラスターの待機時間があるのでこれを減らすようにしたい。
そこで出てきたのが、clusterApplyLBです。
clusterApplyLBは、計算が終了したら次の引数を引き渡すので、待機状態になるクラスターが(ほとんど)ありません!
&ref(): File not found: "fig01_clusterApply.jpg" at page "並列計算(snowfall)";&ref(): File not found: "fig02_clusterApplyLB.jpg" at page "並列計算(snowfall)";

引数にクラスターを指定しないsnowfall

snowでは全ての並列関数にクラスターを指定しなくてはなりません。

clusterApply(cl, x, Sys.sleep)

sfClusterApplyLBとsfLapply

並列関数の異端児 sfClusterApplySR

結局どの関数を使えばいいの?

未実装関数について

sfRapply

sfCapply

sfClusterMap?

Tips

通常処理と並列処理の切り替え

R起動時のsnowfallの設定

乱数の使用

クラスターの種類

クラスターコメント
SOCKデフォルト。
追加パッケージ不要。ワークステーションやPCおよびノートPCに適する。
MPIパッケージRmpiが必要。
PVMパッケージrpvmが必要。
NWS追加パッケージ不要。

並列計算を中断した場合

並列計算を中断した場合は、必ずworkerをsfStop()で一度終了させてください。
終了しないと、意図としない結果が返ってきます。

> #普通に実行
> sfSapply((1:200)/1000, function(x){Sys.sleep(x); x})
  [1] 0.001 0.002 0.003 0.004 0.005 0.006 0.007 0.008 0.009 0.010 0.011 0.012 0.013 0.014 0.015 0.016 0.017
 [18] 0.018 0.019 0.020 0.021 0.022 0.023 0.024 0.025 0.026 0.027 0.028 0.029 0.030 0.031 0.032 0.033 0.034
 [35] 0.035 0.036 0.037 0.038 0.039 0.040 0.041 0.042 0.043 0.044 0.045 0.046 0.047 0.048 0.049 0.050 0.051
 [52] 0.052 0.053 0.054 0.055 0.056 0.057 0.058 0.059 0.060 0.061 0.062 0.063 0.064 0.065 0.066 0.067 0.068
 [69] 0.069 0.070 0.071 0.072 0.073 0.074 0.075 0.076 0.077 0.078 0.079 0.080 0.081 0.082 0.083 0.084 0.085
 [86] 0.086 0.087 0.088 0.089 0.090 0.091 0.092 0.093 0.094 0.095 0.096 0.097 0.098 0.099 0.100 0.101 0.102
[103] 0.103 0.104 0.105 0.106 0.107 0.108 0.109 0.110 0.111 0.112 0.113 0.114 0.115 0.116 0.117 0.118 0.119
[120] 0.120 0.121 0.122 0.123 0.124 0.125 0.126 0.127 0.128 0.129 0.130 0.131 0.132 0.133 0.134 0.135 0.136
[137] 0.137 0.138 0.139 0.140 0.141 0.142 0.143 0.144 0.145 0.146 0.147 0.148 0.149 0.150 0.151 0.152 0.153
[154] 0.154 0.155 0.156 0.157 0.158 0.159 0.160 0.161 0.162 0.163 0.164 0.165 0.166 0.167 0.168 0.169 0.170
[171] 0.171 0.172 0.173 0.174 0.175 0.176 0.177 0.178 0.179 0.180 0.181 0.182 0.183 0.184 0.185 0.186 0.187
[188] 0.188 0.189 0.190 0.191 0.192 0.193 0.194 0.195 0.196 0.197 0.198 0.199 0.200

> #同じ作業を中断
> sfSapply((1:200)/1000, function(x){Sys.sleep(x); x})

> #変数を変えて20個のみ実行したが、前の結果が残っている?
> sfSapply((1:20)/100, function(x){Sys.sleep(x); x})
  [1] 0.010 0.020 0.030 0.026 0.027 0.028 0.029 0.030 0.031 0.032 0.033 0.034 0.035 0.036 0.037 0.038 0.039
 [18] 0.040 0.041 0.042 0.043 0.044 0.045 0.046 0.047 0.048 0.049 0.050 0.051 0.052 0.053 0.054 0.055 0.056
 [35] 0.057 0.058 0.059 0.060 0.061 0.062 0.063 0.064 0.065 0.066 0.067 0.068 0.069 0.070 0.071 0.072 0.073
 [52] 0.074 0.075 0.076 0.077 0.078 0.079 0.080 0.081 0.082 0.083 0.084 0.085 0.086 0.087 0.088 0.089 0.090
 [69] 0.091 0.092 0.093 0.094 0.095 0.096 0.097 0.098 0.099 0.100 0.101 0.102 0.103 0.104 0.105 0.106 0.107
 [86] 0.108 0.109 0.110 0.111 0.112 0.113 0.114 0.115 0.116 0.117 0.118 0.119 0.120 0.121 0.122 0.123 0.124
[103] 0.125 0.126 0.127 0.128 0.129 0.130 0.131 0.132 0.133 0.134 0.135 0.136 0.137 0.138 0.139 0.140 0.141
[120] 0.142 0.143 0.144 0.145 0.146 0.147 0.148 0.149 0.150 0.151 0.152 0.153 0.154 0.155 0.156 0.157 0.158
[137] 0.159 0.160 0.161 0.162 0.163 0.164 0.165 0.166 0.167 0.168 0.169 0.170 0.171 0.172 0.173 0.174 0.175
[154] 0.176 0.177 0.178 0.179 0.180 0.181 0.182 0.183 0.184 0.185 0.186 0.187 0.188 0.189 0.190 0.191 0.192
[171] 0.193 0.194 0.195 0.196 0.197 0.198 0.199 0.200

> #残っているので再度実行して、うまくいっているように思われるが
> sfSapply((1:20)/100, function(x){Sys.sleep(x); x})
 [1] 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20

> #やっぱり返る結果は意図としない結果。
> sfSapply((1:200)/1000, function(x){Sys.sleep(x); x})
 [1] 0.001 0.002 0.003 0.004 0.005 0.006 0.007 0.008 0.009 0.010 0.011 0.012 0.013 0.014 0.015 0.016 0.017
[18] 0.018 0.019 0.020 0.021 0.022 0.023 0.024 0.025 0.040 0.050 0.060 0.070 0.080 0.090 0.100 0.110 0.120
[35] 0.130 0.140 0.150 0.160 0.170 0.180 0.190 0.200

コア数の調査

以下のコードで取得できます。
なお、ハイパースレッディングを備えたCPUではスレッド数が取得されます。

> #parallelのdetectCoresを使用して取得します。
> library(parallel)
> detectCores()
[1] 4

並列計算事始め

参考文献・文書など

コメント



トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Google
WWW を検索 OKADAJP.ORG を検索