並列計算(snowfall)

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

現在編集中


目次


対象者

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

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

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

どの並列関数を使うか

clusterApplyとclusterApplyLB(snow)

sfClusterApplyLBとsfLapply

sfClusterApplySR

Tips

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

clusterMap(snow)

乱数の使用

本当に速くなりますか?

並列化できる割合によって、理論上の性能向上の限界が変わってきます。(アムダールの法則を参照)
並列化できるタスクが90%ある場合性能向上の限界は10倍、半分しかない場合は2倍となります。
ここで並列化できないタスクとは各workerとの通信などを指します。
なお、かえって遅くなる場合もあります。

クラスターの種類

クラスターコメント
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 を検索