並列計算(snowfall)
マルチコアが主流となってきているので、並列計算のパッケージである「snowfall」を紹介します。
なお、特に断りが無い場合はSocketクラスターを使用しています。
現在編集中
目次
計算速度を向上させたいが、煩わしいことはなるべく避けたい。
そのような方にとって、並列計算パッケージ「snowfall」は一つの方法かもしれません。
本記事は、以下の方を想定しています。
> 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
base | snow | snowfall | コメント |
apply | parApply | sfApply | |
lapply | parLapply | sfLapply | |
sapply | parSapply | sfSapply | |
apply(X, MARGIN=1, FUN, ...) | parRapply | 未実装(Version 1.84-4) | snowfallでは関数は定義されているが、以下のとおり。 sfRapply does not exists yet. Use Snow's parRapply instead. sfApplyで代用する。 |
apply(X, MARGIN=2, FUN, ...) | parCapply | 未実装(Version 1.84-4) | snowfallでは関数は定義されているが、以下のとおり。 sfCapply does not exists yet. Use Snow's parCapply instead. sfApplyで代用する。 |
mapply | clusterMap | 未実装(Version 1.84-4) | snowfallでは関数は定義されているが、以下のとおり。 Currently no wrapper for clusterMap snowfallの関数では代用できないため、並列化をする場合はsnowパッケージを使用する。 |
%*% | parMM | sfMM |
カテゴリーは主観で分けています。
カテゴリー | snow | snowfall | コメント |
クラスター作成 | makeCluster | sfInit | すべてsfInitで行えます。 |
makeSOCKcluster | |||
makePVMcluster | |||
makeMPIcluster | |||
makeNWScluster | |||
オブジェクトなどの引渡し | clusterExport | sfExport | 各クラスターに特定のオブジェクトをエクスポートします。 |
- | sfExportAll | 各クラスターに全てのオブジェクトをエクスポートします。 | |
- | sfRemove | 各クラスターから特定のオブジェクトを削除します。 | |
- | sfRemoveAll | 各クラスターから全てのオブジェクトを削除します。 | |
- | sfLibrary | 各クラスターにライブラリーを読み込ませます。 | |
- | sfSource | 各クラスターにソースファイルを読み込ませます。 | |
並列基本関数 | parApply | sfApply | |
parLapply | sfLapply | ||
parSapply | sfSapply | ||
parRapply | 未実装(Version 1.84-4) | ||
parCapply | 未実装(Version 1.84-4) | ||
parMM | sfMM | %*% | |
clusterMap | 未実装(Version 1.84-4) | ||
その他並列関数 | clusterApply | sfClusterApply | sfClusterApplyはクラスター数以下の要素しか受け取れない。 (使えない関数の気がします。) |
clusterApplyLB | sfClusterApplyLB | clusterApplyでは、1要素の計算が終わるまですべてのクラスターが待機していましたが これは1要素の計算が終了次第次の計算に移ります。 (Load Balanced version of sfClusterApply) | |
- | sfClusterApplySR | 1要素の計算が終了するごとにsaveを行い、予期せぬ終了(サーバーメンテナンスなど)があった場合でも、続きから計算を始めることが出来ます。 (ただし、saveを頻繁に行うので遅くなるようです。) (Saves intermediate Results) | |
clusterCall | sfClusterCall | 各クラスターに同一の命令文を実行させます。 | |
clusterEvalQ | sfClusterEval | 各クラスターに同一の未評価の命令文を実行させます。 | |
乱数 | clusterSetupRNG | sfClusterSetupRNG | 乱数の生成をします。 (set.seedのようにNULLによって乱数の生成が出来ないため、少々扱いにくいです。) |
clusterSetupRNGstream | sfClusterSetupRNGstream | ||
clusterSetupSPRNG | sfClusterSetupSPRNG | ||
クラスターの終了 | stopCluster | sfStop | |
クラスターの使用状況 | snow.time | - | クラスターの使用状況を調査出来る関数です。 |
- | printを拡張し、snow.timeで取得したクラスターの使用状況表示します。 | ||
plot | - | plotを拡張し、snow.timeで取得したクラスターの使用状況表示します。 | |
デバッグ | - | sfCat | 各クラスターにcat文を実行させます。 |
各種情報等 | clusterSplit | sfClusterSplit | helpを参照の事。 |
setDefaultClusterOptions | - | ||
getMPIcluster | - | ||
- | sfParallel | ||
- | sfIsRunning | ||
- | sfCpus | ||
- | sfNodes | ||
- | sfGetCluster | ||
- | sfType | ||
- | sfSession | ||
- | sfSocketHosts | ||
- | sfSetMaxCPUs | ||
- | sfRestore |
並列化できる割合によって、理論上の性能向上の限界が変わってきます。(アムダールの法則を参照)
並列化できるタスクが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
help(package=snow) help(package=snowfall) vignette("snowfall")