• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
R高速化
 

R高速化

on

  • 2,997 views

Rのループ(for)がなぜ遅いか? ...

Rのループ(for)がなぜ遅いか?
並列化によってどれくらい早くなるか?
(どういった場合に早くなるか)
を調べて勉強会で発表した資料になります。

Statistics

Views

Total Views
2,997
Views on SlideShare
1,225
Embed Views
1,772

Actions

Likes
1
Downloads
0
Comments
0

13 Embeds 1,772

http://kansaizeror.blogspot.jp 1723
http://kansaizeror.blogspot.com 32
http://kansaizeror.blogspot.tw 4
http://kansaizeror.blogspot.de 2
http://kansaizeror.blogspot.kr 2
http://kansaizeror.blogspot.fr 2
http://kansaizeror.blogspot.hu 1
http://webcache.googleusercontent.com 1
http://kansaizeror.blogspot.sg 1
http://kansaizeror.blogspot.fi 1
http://kansaizeror.blogspot.hk 1
http://kansaizeror.blogspot.co.uk 1
http://kansaizeror.blogspot.ca 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    R高速化 R高速化 Presentation Transcript

    • Rやってみた〜高速化編〜
    • 自己紹介 約40年前 大阪に誕生 約20年前 大学にてニューラルネットワーク&最適化 約15年前 開発会社にて各種開発を5年くらい 約10年前 現在(コンピュータ業界の)インフラ屋 趣味 3DCG プログラミング(主にAndroid) 統計学はド素人、Rは素人
    • 前回、Rを触ってみて結構面白かったおやすみおはよう個人的におっぱいグラフからの推測• 7時頃に起きる人が多い• 0時頃に寝る人が多い(寝る時間も時間に左右される)→時計に縛られる日本人・煩悩は夜に宿る ← new!
    • まじめにテーマを考える 前回、前々回も出ていたモテモテのテーマ、それは…高速化!• 前半…“遅い”の詳細• 後半…高速化
    • R業界の嫌われ者といえば「for」 「forを使うな…」 「forは遅い」 「forは…」どのページを見てもforの悪口だらけなぜ遅い?
    • エクセレントコード ウンコードそもそもなぜfor文が遅いか? 「ベクトルa」の平方根を「ベクトルb」に代入OSRfor(i in 1:10^6) b[i]<-sqrt(a[i])関数RインタプリタOSRb<-sqrt(a)関数Rインタプリタユーザ システム 経過0.001 0.001 0.001ユーザ システム 経過16.440 7.205 23.681という“推測”
    • 推測を確認する Rでは処理時間の詳細(プロファイル)を調べる「Rprof()」を用いるエクセレントコード ウンコード> a <- c(1:10^7)> Rprof()> for( i in 1:1) b <- sqrt(a)> Rprof(NULL)> summaryRprof()$by.totaltotal.time total.pct self.time self.pct"sqrt" 0.18 100 0.18 100> a <- c(1:10^5)> b <- c()> Rprof()> for( i in 1:length(a)) b[i] <- sqrt(a[i])> Rprof(NULL)> summaryRprof()$by.total[1] total.time total.pct self.time self.pct<0 行> (または長さ 0 の row.names)R上では原因を観測できないバグ?→0.02秒間隔では検出不可(Rprof.out)
    • (ドつぼにはまっているが)推測を「本腰入れて」確認する これ以上詳細を調査しようとすると1. Rから詳細なログを出力して動きを追う2. Rより下のレイヤから動きを追うInstruments(プロファイルツール)
    • 想像を本腰入れて確認する〜エクセレントコード〜a <- c(1:10^7)for( i in 1:1) b <- sqrt(a)ポイントは、平方根を取る目的の処理が84%(262ms)の時間を使っているところ
    •  arithmetic.c想像を本腰入れて確認するdo_math1の正体SEXP attribute_hidden do_math1(SEXP call, SEXP op, SEXP args, SEXP env){SEXP s;checkArity(op, args);check1arg(args, call, "x");if (DispatchGroup("Math", call, op, args, env, &s))return s;if (isComplex(CAR(args)))return complex_math1(call, op, args, env);#define MATH1(x) math1(CAR(args), x, call);switch (PRIMVAL(op)) {case 1: return MATH1(floor);case 2: return MATH1(ceil);case 3: return MATH1(sqrt);case 4: return MATH1(sign);math関数呼び出し/* Mathematical Functions of One Argument */static SEXP math1(SEXP sa, double(*f)(double), SEXP lcall){SEXP sy;double *y, *a;R_xlen_t i, n;int naflag;if (!isNumeric(sa))errorcall(lcall, R_MSG_NONNUM_MATH);n = xlength(sa);/* coercion can lose the object bit */PROTECT(sa = coerceVector(sa, REALSXP));PROTECT(sy = allocVector(REALSXP, n));a = REAL(sa);y = REAL(sy);naflag = 0;for (i = 0; i < n; i++) {if (ISNAN(a[i]))y[i] = a[i];else {y[i] = f(a[i]);if (ISNAN(y[i])) naflag = 1;}}ここでSQRT処理
    • 想像を本腰入れて確認する〜ウンコード〜(2時間でギブアップ)a <- c(1:10^7)b <- c()for( i in 1:10^7) b[i] <- sqrt(a[i])このVectorAssignが99%の時間を食っている平方根を取る処理が0.1%未満!
    • 想像を本腰入れて確認する〜ウンコード〜a <- c(1:10^7)b <- numeric(10^7)for( i in 1:10^7) b[i] <- sqrt(a[i])7,321,955ms(以上)から31,276msへ改善!事前に領域確保平方根を取る目的の処理が3,886ms(以上) から 2,159msに改善
    • for文は遅いのまとめ For文で処理をまわすなら迷わず領域を事前確保するべき(2時間以上 → 30秒) 領域を事前準備した場合全体では約100倍の差メインの処理(sqrt)の時間は約8倍の差→内部の処理が時間差のほとんどを占める全体(ms) math1 その他 備考エクセレントコード 305 262 48ウンコード 31,276 2,159 29,117 10^7回ループ10^7回のループで10^2の処理時間は許容範囲
    • ここから今回のメインテーマ
    • ベクトル化できない場合そんなときは… アルゴリズム、プログラムの工夫 並列化(複数スレッドを使用する) 並列化(複数マシンを使用する) GPU並列化 変態向け賢い人向け玄人向けマニア向け色々ありそうなのでパス対応関数が少ない
    • 並列化(環境と動作イメージ) 今回は4ノードを準備Switchssh① ノードでRを起動する。② クラスタノードを指定する。③ 並列化処理を実行RRRRRRRRRRRRRR RR④ 各ノードで指定された数だけRプロセスを起動して処理⑤ 結果を元のRに集約・Xeon(R) CPU E31220(4コア、3.10GHz)・メモリ 16GB
    • 並列化(Rでの書式)① ノードでRを起動する。② クラスタノードを指定する。③ 並列化処理を実行④ 各ノードで指定された数だけRプロセスを起動して処理⑤ 結果を元のRに集約① ターミナルにて「r」を実行② cl <- makeCluster(c(“サーバ名”,〜略〜), type = “SOCK”)registerDoSNOW(cl)③ foreach(i = 1:1000) %dopar% { sum(rnorm(10000)) }④ ※ライブラリがやります⑤ ※ライブラリがやります
    • 並列化(使用するパッケージ)以下のパッケージを使うことで別ノードを使用した並列化を容易に実現(すべてのノードにパッケージを事前にインストール) snow ・・・ Simple Network of Workstations foreach ・・・ for文を並列化する Iterators ・・・ 展開 doSNOW ・・・ snowの簡易化(registerDoSNOW関数のみ)単一ノード(スレッド)と複数ノードを同じ様に扱うことができる
    • 並列化評価気になる2つのポイント 並列化は本当に早くなるの? ネットワークはボトルネックにならないの?SwitchsshRRRRRRRRRRRRRR RRボトルネック?転送データ 小/大処理時間 短/長
    • 並列化(転送データ 小、処理時間 短) 乱数ベクトルの合計を求めるsystem.time(for( i in 1:10^3 ) sum(rnorm(10^5)))ユーザ システム 経過8.486 0.000 8.483単一プロセスcl <- makeCluster(c("localhost”…), type = "SOCK")registerDoSNOW(cl)system.time(foreach(i = 1:10^3) %dopar%{sum(rnorm(10^5)) })stopCluster(cl)並列化02468101214161 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16時間(秒)並列度並列度と処理時間(5回平均)処理時間7.1倍の高速化!
    • 並列化(転送データ 小、処理時間 長) 乱数ベクトルの合計を求めるsystem.time(for( i in 1:10^2 ) sum(rnorm(10^7)))ユーザ システム 経過84.372 0.576 85.008単一プロセスcl <- makeCluster(c("localhost”…), type = "SOCK")registerDoSNOW(cl)system.time(foreach(i = 1:10^2) %dopar%{sum(rnorm(10^7)) })stopCluster(cl)並列化10^3から変更10^5から変更約10倍01020304050607080901 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16時間(秒)並列度並列度と処理時間(5回平均)処理時間13.2倍の高速化!
    • 「転送データ 大」の準備 大きなデータの代表、画像データを処理しよう!画像処理業界でおなじみの「Lena」たん自主規制(続きはWebで!)Width:1084Height:2318
    • Rで画像処理ってどういうこと? 色を減らす「減色処理」に使える。クラスター分析じゃない?RGB空間を色の数で分割元画像 減色画像
    • 並列化(転送データ 大、処理時間 短)対象:LENAフル画像処理:ランダムな点を返す自主規制!Width:1084Height:2318system.time(for(i in 1:10^3)myimage[floor(runif(1,1,MAX_VALUE))])ユーザ システム 経過0.02 0.00 0.03単一プロセスcl <- makeCluster(c("localhost”…), type = "SOCK")registerDoSNOW(cl)system.time(foreach(i = 1:num,.export=c(“myimage”,“MAX_VALUE”))%dopar% myimage[floor(runif(1,1,MAX_VALUE))])stopCluster(cl)並列化character vector ofvariables to export.024681012141 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16時間(秒)並列度並列度と処理時間(5回平均)処理時間
    • 0100020003000400050006000700080001 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16時間(秒)並列度並列度と処理時間処理時間並列化(転送データ 大、処理時間 長)対象:LENAフル画像処理:k-meansで減色自主規制!Width:1084Height:2318for(i in 1:16)kmeans(myimage2,centers=255,iter.max=10,nstart=i)ユーザ システム 経過0.02 0.00 2時間以上単一プロセスret_better <- function(x,y) {if (x$tot.withinss < y$tot.withinss) xelse y}res_kmean <- foreach(i =1:16,.combine="ret_better") %dopar%{kmeans(myimage2,centers=255,iter.max=50,nstart=i)}並列化
    • 減色例元画像(フルカラー) 画像アプリ減色(256色) R減色(256色)注:時間は数十倍、数百倍かかっています
    • 高速化検証のまとめ やっぱりベクトル化した処理が早い for文使うときは事前に領域を準備することにより高速化(個人的な感覚としては、なるべくforはさけた方がよいレベル) 繰り返し処理はforeachを用いた並列化を積極的におすすめ! マルチコアマシンで容易に並列化 マルチノードでも容易に並列化 大きなデータは遅くなる場合がある→処理が長ければ許容できる転送データ量大 小処理時間長 ◎ ◎短 × ○ぜひ、並列化を!
    • 付録
    • 並列化(転送データ 小、処理時間 中) コラッツの問題(未解決問題)以下を繰り返すとどんな値も1に集約する・偶数なら2で割る・奇数なら3を掛け1を足す00.511.522.533.544.551 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16時間(秒)並列度並列度と経過時間(5回平均)経過時間
    • ちなみに、コラッツの問題のグラフ化 X軸方向に初期値、Y軸方向に繰り返し回数
    • 付録(コラッツの問題)do_cluster <- function(num=10^4, client_num=1){cl <- makeCluster(c("localhost","localhost",中略,"Slave03")[1:client_num], type = "SOCK")registerDoSNOW(cl)ret = system.time(foreach(i = 1:num,.combine = "cbind") %dopar% {n <- iwhile(n != 1){if( n %% 2 == 0 ) n <- n / 2else n <- 3 * n + 1}})stopCluster(cl)ret}rm(ret)test_count <- 15ret <- c()for (i in 0:test_count) ret <- cbind(ret,do_cluster(num=10^3, client_num = (i%%16)+1))
    • 付録(減色処理)library("biOps”)myimage <- readJpeg("l_hires.jpg")myimage2 <- matrix(myimage,ncol=3)HEIGHT <- dim(myimage)[1]WIDTH <- dim(myimage)[2]ret_better <- function(x,y) {if (x$tot.withinss < y$tot.withinss) xelse y}registerDoSNOW(makeCluster(2, type = "SOCK"))res_kmean <- foreach(i = 1:30,.combine="ret_better") %dopar%{kmeans(myimage2,centers=255,iter.max=50,nstart=i)}plot(imagedata(c(res_kmean$centers[res_kmean$cluster,]),type="rgb",ncol=HEIGHT,nrow=WIDTH))