rによる繰り返し処理の並列化

27
繰繰繰繰繰繰繰繰 foreach & doParallel Windows Vista/7, 32/64bit 機機機機機機機機1 機機 PC 機機機機機機機機機機機機機機機機機機機機機機機機機機機機機機 機機 OS 機機機機機 機機機機機機機機 機機機機機機機機機機機機機機機機 繰繰繰繰 R 繰繰繰繰 2014/3/10

Upload: wada-kazumi

Post on 28-May-2015

3.571 views

Category:

Education


4 download

DESCRIPTION

Windows Vista/7, 32/64bit機で動作確認した、1台のPC上でソケットを利用してメモリ共有型でコアを並列化する方法です。foreacnとdoParallelパッケージ使用。シミュレーションなどにどうぞ。

TRANSCRIPT

Page 1: Rによる繰り返し処理の並列化

繰返しの並列処理- foreach & doParallel -

Windows Vista/7, 32/64bit 機で動作確認した、 1 台の PC 上でソケットを利用してメモリ共有型でコアを並列化する方法です。他の OS でも動くかもしれないので、試した方ぜひ結果教えてください。

統計環境 R における

2014/3/10

Page 2: Rによる繰り返し処理の並列化

foreach パッケージ• 単体で使用しても繰返し処理を高

速化する。• snow や parallel などの並列処理

パッケージと組み合わせると繰返し処理を並列化できる。

• for ループとの違い 出力は 1 つだけ

これが問題 !

2

Page 3: Rによる繰り返し処理の並列化

foreach: 出力制御のコツ

制約 : 出力は 1 つだけ

• アウトプットの構造や書き出される順番を理解し、必要があれば後から自分で配列化などを施す

• 複数の出力も順番を間違えなければ扱える

3

Page 4: Rによる繰り返し処理の並列化

例 ) 回帰パラメータ算出実験

 乱数データから回帰係数を算出するモンテカルロ実験を 3 回だけ繰返す‐ x は一様乱数で三変量‐ y は次の回帰モデルに従い、自由度3 の t 分布に従う乱数を誤差項として付与

y=10+5x1+2x2+3x3自由度の小さい t 分布は正規分布よりも裾が長いので、ちょっと荒れてる感じのデータになります。結果、そのデータから回帰パラメータを推計したとき、真の値と差異が出やすいです。 4

Page 5: Rによる繰り返し処理の並列化

2 4 6 8

40

60

80

100

x1[, 1]

y1

データのイメージ

第一説明変数  x1

目的変数 y1

5

Page 6: Rによる繰り返し処理の並列化

並列化の準備としてまず foreach の結果の

戻し方を確認します

実験 : 1A, 1B, 1C

6

Page 7: Rによる繰り返し処理の並列化

例 ) 回帰パラメータ算出実験 (1A)

rm(list=ls(all=TRUE)) # ワークスペースのクリアrequire(foreach); require(MASS) # for rt

set.seed(15)theta1 <- c(10, 5, 2, 3) # 回帰パラメータ "y=10+5x1+2x2+3x3"

ot1 <- foreach(i=1:3, .combine=c) %do% { x1 <- matrix(runif(300, min=1, max=10), nc=3, nr=100) # x の

値 x2 <- cbind(rep(1, dim(x1)[1]), x1) # 1 列目に要素が全て 1 の列を付

与 y1 <- x2 %*% theta1 + rt(100, df=3) # y は推計値+残差 lm(y1 ~ x1)$coeff}ot1is.vector(ot1)

注 ) まだパラレル化はしていません

7

Page 8: Rによる繰り返し処理の並列化

例 ) 回帰パラメータ算出実験 (1B)

rm(list=ls(all=TRUE)) # ワークスペースのクリアrequire(foreach); require(MASS) # for rtset.seed(15)theta1 <- c(10, 5, 2, 3) # 回帰パラメータ "y=10+5x1+2x2+3x3"ot2 <- foreach(i=1:3, .combine="cbind") %do% { x1 <- matrix(runif(300, min=1, max=10), nc=3, nr=100) # x の値 x2 <- cbind(rep(1, dim(x1)[1]), x1) # 1 列目に要素が全て 1 の列を

付与 y1 <- x2 %*% theta1 + rt(100, df=3) # y は推計値+残差 lm(y1 ~ x1)$coeff}ot2is.matrix(ot2)

8

Page 9: Rによる繰り返し処理の並列化

例 ) 回帰パラメータ算出実験 (1C)rm(list=ls(all=TRUE)) # ワークスペースのクリアrequire(foreach); require(MASS) # for rtset.seed(15)theta1 <- c(10, 5, 2, 3) # 回帰パラメータ "y=10+5x1+2x2+3x3"ot3 <- foreach(i=1:3, .combine="rbind") %do% { x1 <- matrix(runif(300, min=1, max=10), nc=3, nr=100) # x の値 x2 <- cbind(rep(1, dim(x1)[1]), x1) # 1 列目に要素が全て 1 の列を

付与 y1 <- x2 %*% theta1 + rt(100, df=3) # y は推計値+残差 lm(y1 ~ x1)$coeff}ot3is.list(ot3)

9

Page 10: Rによる繰り返し処理の並列化

結果出力の違い> ot1(Intercept) x11 x12 x13 (Intercept) x11 10.899300 4.914961 1.978661 2.960806 10.562035 5.005228 x12 x13 (Intercept) x11 x12 x13 1.962990 2.939047 9.786081 5.035381 1.947302 3.102226> is.vector(ot1)[1] TRUE

> ot2 result.1 result.2 result.3(Intercept) 10.899300 10.562035 9.786081x11 4.914961 5.005228 5.035381x12 1.978661 1.962990 1.947302x13 2.960806 2.939047 3.102226> is.matrix(ot2)[1] TRUE > ot3

(Intercept) x11 x12 x13result.1 10.899300 4.914961 1.978661 2.960806result.2 10.562035 5.005228 1.962990 2.939047result.3 9.786081 5.035381 1.947302 3.102226> is.matrix(ot3)[1] TRUE

.combine=c

.combine="cbind"

.combine="rbind"

ベクトルだったり行列だったりするけど出力は一つだけ

10

Page 11: Rによる繰り返し処理の並列化

doParallel パッケージを使って

同じ処理を並列化してみます

実験 : 1A → 2

11

Page 12: Rによる繰り返し処理の並列化

例 ) 回帰パラメータ算出実験 (2)rm(list=ls(all=TRUE)) # ワークスペースのクリアs_time <- Sys.time() # 開始時間記録のための呪文require(foreach); require(MASS) # for rt

require(doParallel)type <- if (exists("mcfork", mode="function")) "FORK" else "PSOCK" cores <- getOption("mc.cores", detectCores()) cl <- makeCluster(cores, type=type) # コア数を自動検出 registerDoParallel(cl) # コア数で並列化set.seed(15)theta1 <- c(10, 5, 2, 3) # 回帰パラメータ "y=10+5x1+2x2+3x3"ot1 <- foreach(i=1:3, .combine=c) %dopar% { x1 <- matrix(runif(300, min=1, max=10), nc=3, nr=100) # x の値 x2 <- cbind(rep(1, dim(x1)[1]), x1) # 1 列目に要素が全て 1 の列を付与 y1 <- x2 %*% theta1 + rt(100, df=3) # y は推計値+残差 lm(y1 ~ x1)$coeff}stopCluster(cl) # 並列化終了のための呪文e_time <- Sys.time() # 終了時間記録のための呪文ot1

12

Page 13: Rによる繰り返し処理の並列化

コア毎に違う乱数が割り当てられるために結果は変わる

> ot1(Intercept) x11 x12 x13 (Intercept) x11 9.495911 4.987828 2.035046 3.063866 10.212789 5.040721 x12 x13 (Intercept) x11 x12 x13 1.905662 3.046415 8.409125 5.133967 2.103635 3.064619

> ot1(Intercept) x11 x12 x13 (Intercept) x11 10.899300 4.914961 1.978661 2.960806 10.562035 5.005228 x12 x13 (Intercept) x11 x12 x13 1.962990 2.939047 9.786081 5.035381 1.947302 3.102226

実験 1A の結果 ( 並列化前 )

実験 2 の結果 ( 並列化後 )

13

Page 14: Rによる繰り返し処理の並列化

で、もちろん速くなるよね?

どんな処理をどのくらいの量だけ並列化するかによります。この例もそうですが、基本的に遅くなると考えた方が良いです。  ご利用は慎重に。

> e_time - s_timeTime difference of 0.5630572 secs

実験 1A の結果 ( 並列化前 )

> e_time - s_timeTime difference of 10.46505 secs

実験 2 の結果 ( 並列化後 )

並列化前が圧倒的に速い !

実験 2 のコードの上から二行目と下から二行目の時間測定の呪文を実験 1A のコードにも書き加え

て測定しました

14

Page 15: Rによる繰り返し処理の並列化

どうして並列化で遅くなるの ?

 前の処理の結果に依存する処理は並列化できない 一方、並列化のためには新たな処理 ( パッケージのロードやコア間通信など ) が必要となる 処理の依存関係のない並列化可能な部分の処理が、例えば 4 コアで 75%時間を短縮できたとしても、全体のなかでその効率化が新たに必要になる処理時間を上回る分量でなければトータルで速くはならない

リモワ ルフトハンザ

並列化前I/O 並列化不能部分 並列化可能な部分

¼ に 並列化のための処理

遅くなる犯人 !並列化後

15

Page 16: Rによる繰り返し処理の並列化

アムダールの法則

並列化可能な部分が全体の 50% ならば、どんなにコア数の多い

高価な PC で並列化しても、理論値でさえ 2 倍の速度が限界です。

P: 並列化可能部分の割合S: コアの数

1(1− 𝑃 )+𝑃 /𝑆並列化の効果 :

実測はもっ

とずっ

と効率悪いです。

16

Page 17: Rによる繰り返し処理の並列化

TO DO or NOT TO DO 自分一人で作業した方が早いか、そのへんの人を何人か連れて来て仕事を説明して作業を分けてでも手伝ってもらった方が早く終わるような作業内容なのかを見極める。

• 一つずつ順を追って処理しなければいけない仕事ならば、淡々と一人で片づけるしかない。

• 分業可能な作業であれば、作業の説明や仕分けやまとめなどの新たな手間が増えるのと、分業の効率を天秤にかけて判断する。

並列処理は、分業させるためのコード書きも大変なので、それに見合う効率化が見込めるときにだけ役立つ技なのです。

17

Page 18: Rによる繰り返し処理の並列化

具体的な判断手順1. コードを精査し、並列処理できる部分とそうでない部分を切り分けて、並列処理部分は極力少ない塊にまとめる

2. 並列処理できる部分とそうでない部分を別々に時間計測する

3. 並列処理できる部分が、全体の何パーセントになるのかを計算し、アムダールの法則のグラフで理論効率を確認

4. 実測効率は理論効率よりもはるかに 悪 いことを覚悟するそれでもやりたければ並列化コードを書く

5. 大量データを取扱う場合、この並列化はメモリ共有型なので、作業を切り分ける大きさに留意する大きく切れば早いがメモリ不足の危険があり、小さく切りすぎれば遅くなる

まずシングルコア版を作成し、そこから並列化版を作ると良いです。両者の結果が変わらないことが確認できれば安心です。 18

Page 19: Rによる繰り返し処理の並列化

行列要素の並び順 : Rと fortranは縦並び

> x0 <- 1:100

> (x1 <- matrix(x0, nc=10)) [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [1,] 1 11 21 31 41 51 61 71 81 91 [2,] 2 12 22 32 42 52 62 72 82 92 [3,] 3 13 23 33 43 53 63 73 83 93 [4,] 4 14 24 34 44 54 64 74 84 94 [5,] 5 15 25 35 45 55 65 75 85 95 [6,] 6 16 26 36 46 56 66 76 86 96 [7,] 7 17 27 37 47 57 67 77 87 97 [8,] 8 18 28 38 48 58 68 78 88 98 [9,] 9 19 29 39 49 59 69 79 89 99[10,] 10 20 30 40 50 60 70 80 90 100

> x1[21][1] 21

ちなみに C は横並びだそうです。19

Page 20: Rによる繰り返し処理の並列化

配列要素の場合は ?> x0 <- 1:1000> (x1 <- array(x0, c(10,10,10))), , 1

[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [1,] 1 11 21 31 41 51 61 71 81 91 [2,] 2 12 22 32 42 52 62 72 82 92 [3,] 3 13 23 33 43 53 63 73 83 93 [4,] 4 14 24 34 44 54 64 74 84 94 [5,] 5 15 25 35 45 55 65 75 85 95 [6,] 6 16 26 36 46 56 66 76 86 96 [7,] 7 17 27 37 47 57 67 77 87 97 [8,] 8 18 28 38 48 58 68 78 88 98 [9,] 9 19 29 39 49 59 69 79 89 99[10,] 10 20 30 40 50 60 70 80 90 100

, , 220

Page 21: Rによる繰り返し処理の並列化

なぜ並び順が重要 ?• 変数一つの出力の中に

複数の情報を詰め込む場合、どの情報がどういう順番に入っているかわかっていないと使えない

• 高速なコードのために、極力キャッシュミスが起こりにくいような処理を心がける

21

キャッシュ

ディスク

メモリ

レジスタ

CPU

低速安価大容量

高速高価

小容量

参考 : 「チューニング技法虎の巻」 https://www.hpci-office.jp/pages/407

Page 22: Rによる繰り返し処理の並列化

並列化によるシミュレーション

実験 : 2 → 3

22

Page 23: Rによる繰り返し処理の並列化

例 ) 回帰パラメータ算出実験 (3)rm(list=ls(all=TRUE)) # ワークスペースのクリアs_time <- Sys.time() # 開始時間記録のための呪文require(foreach); require(MASS) # for rt

require(doParallel)type <- if (exists("mcfork", mode="function")) "FORK" else "PSOCK" cores <- getOption("mc.cores", detectCores()) cl <- makeCluster(cores, type=type) # コア数を自動検出 registerDoParallel(cl) # コア数で並列化set.seed(15)theta1 <- c(10, 5, 2, 3) # 回帰パラメータ "y=10+5x1+2x2+3x3"ot1 <- foreach(i=1:4, .combine=c) %dopar% {    # コア数の倍数が効率良いです

cf2 <- matrix(NA, nc=2500, nr=4) # 縦に並べないとわけがわからなくなる !!!for (j in 1:2500) {

x1 <- matrix(runif(300, min=1, max=10), nc=3, nr=100) # x の値 x2 <- cbind(rep(1, dim(x1)[1]), x1) # 1 列目に要素が全て 1 の列を付与 y1 <- x2 %*% theta1 + rt(100, df=3) # y は推計値+残差 cf2[,j] <- lm(y1 ~ x1)$coeff

}cf2

}stopCluster(cl) # 並列化終了のための呪文e_time <- Sys.time() # 終了時間記録のための呪文ot1 23

Page 24: Rによる繰り返し処理の並列化

比較用のシングルコア版コード (1A’)rm(list=ls(all=TRUE))# ワークスペースのクリアs_time <- Sys.time() # 開始時間記録のための呪文require(foreach); require(MASS) # for rtset.seed(15)theta1 <- c(10, 5, 2, 3) # 回帰パラメータ "y=10+5x1+2x2+3x3"ot1 <- foreach(i=1:4, .combine=c) %do% {

cf2 <- matrix(NA, nc=2500, nr=4) # 縦に並べないとわけがわからなくなる !!!for (j in 1:2500) {

x1 <- matrix(runif(300, min=1, max=10), nc=3, nr=100) # x の値 x2 <- cbind(rep(1, dim(x1)[1]), x1) # 1 列目に要素が全て 1 の列を付与 y1 <- x2 %*% theta1 + rt(100, df=3) # y は推計値+残差 cf2[,j] <- lm(y1 ~ x1)$coeff

}cf2

}e_time <- Sys.time() # 終了時間記録のための呪文e_time - s_time

24

Page 25: Rによる繰り返し処理の並列化

並列化で嬉しくなるのは ?• 400回 シングル Time difference of 2.374 secs 並列化  Time difference of 8.911 secs

• 4000回 シングル Time difference of 7.158 secs 並列化  Time difference of 11.222 secs

• 10000回 シングル Time difference of 15.31853 secs 並列化  Time difference of 15.63356 secs

• 40000回 シングル Time difference of 55.244 secs 並列化  Time difference of 36.901 secs

• 100000回 シングル Time difference of 2.264043 mins 並列化  Time difference of 1.329983 mins

25Windows VISTA, 32bit, R3.0.0, コア数 2 の条件でテストした結果

Page 26: Rによる繰り返し処理の並列化

結論 : シミュレーションに使える !

length(ot1)[1] 40000> ot1x <- matrix(ot1, nr=4) # 整形> dim(ot1x)[1] 4 10000> apply(ot1x, 1, mean)[1] 10.008551 4.999126 1.999420 3.000582> apply(ot1x, 1, median)[1] 10.010047 4.998599 1.999008 3.000921# モデルの理論値は 10, 5, 2, 3

26

データの取り出し方

Page 27: Rによる繰り返し処理の並列化

おわり27

参考資料Revolution Analytics. “doParallel: Foreach parallel adaptor for the parallel package,” R package version 1.0.1. 2012, available at http://CRAN.R-project.org/package=doParallel.

Revolution Analytics, “foreach: Foreach looping construct for R,” R package version 1.4.0. 2012, available at http://CRAN.R-project.org/package=foreach.

S. Weston and R. Calaway, “Getting started with doParallel and foreach,” 2010,  available at http://cran.r-project.org/web/packages/doParallel/vignettes/gettingstartedParallel.pdf.

S. Weston, “Using the foreach packages,” 2009, available at http://www.rochester.edu/college/psc/thestarlab/help/UsingTheForeachPackage.pdf.