foreachでprogressbarを表示する
最近、pforeachという簡単に並列処理を行うパッケージが僕の中で話題です。とても便利です。
R で超簡単に並列処理を書けるパッケージ pforeach を作った - ほくそ笑む
ただpforeachを使って並列化できるのは良いのですが、並列処理だと普通にやってもprogressbarが表示できません。(僕が知らないだけかもしれないですが)
普通にやるとこうなります。
pb <- txtProgressBar(min = 0, max = 10, style = 3) pforeach(i = 1:10) ({ setTxtProgressBar(pb, i) sqrt(i) }) ## [1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427 ## [9] 3.000000 3.162278
ダメですね...
ということで考えました。案としては2つです。ループの前に出すか後に出すか。
案1 ループの前に出す
iterators
パッケージの関数をいじります。iterators::icount
関数に関しては、各ループの実行時にイテレータに渡す値を生成して返しています。だったら一緒にprogressbarの表示も一緒にやらせよう、といった発想です。
以下に、iterators::icount
関数に少し手を加えたpb_icount
関数を作成したのでこれを示します。
pb_icount <- function (count) { if (missing(count)) count <- NULL else if (!is.numeric(count) || length(count) != 1) stop("count must be a numeric value") i <- 0L pb <- txtProgressBar(min = 0, max = count, style = 3) # 追加 nextEl <- function() { if (is.null(count) || i < count) { setTxtProgressBar(pb, i+1) # 追加 (i <<- i + 1L) } else { cat("\n") # 追加 stop("StopIteration", call. = FALSE) } } it <- list(nextElem = nextEl) class(it) <- c("abstractiter", "iter") it }
これを使ってもう一度並列処理をやってみます。
N <- 10000 pforeach(i = pb_icount(N)) ({ sqrt(i) })[1:10] |========================================================================| 100% ## [1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427 ## [9] 3.000000 3.162278
やりました!!
ただ100%になってからちょっと間があるのが気になる所です。もしかしてループの割当を先に行っていたりするのでしょうか。そうであればこの案はボツなんですが。
他にはiterators::iter
関数はなかなか複雑で難しそうではありますが、こちらでもできればやりたいですね。
その他の関数については、iterators::icount
関数と似た構造であれば同様にできるのではないでしょうか。
案2 ループの後に出す
イテレータではなく、foreach
関数の引数.combine
に注目してprogress barを表示させている例がありました。
How do you create a progress bar when using the "foreach()" function in R? - Stack Overflow
これをもとにして、以下にpb
関数を作成しました。(08/16修正)
pb <- function(N, FUN = c) { pbar <- txtProgressBar(min = 2, max = N, style = 3) count <- 0L if(is.character(FUN)) { FUN <- get(FUN) } function(...) { count <<- count + length(list(...)) - 1L setTxtProgressBar(pbar, count) if(count == (N-1)) { cat("\n") } FUN(...) } }
では検証します。.combine
がc
関数でなくなるので、引数.multicombine
も付けておきます。
N <- 10000 pforeach(i = 1:N, .combine = pb(N), .multicombine = TRUE) ({ sqrt(i) })[1:10] |=======================================================================| 100% ## [1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427 ## [9] 3.000000 3.162278
やりました!!
でも実は.multicombine=TRUE
にしたことでループの回数が変化しているので、ループ回数を表すprogressbarにはならずcombine回数となっています。しかし進捗状況を表すという目的であれば十分だと思います。
速度比較
最後に速度比較です。
- progressbarなし
- 案1
- 案2
を比較します。
# progressbarなし system.time({ N <- 10000 pforeach(i = icount(N)) ({ sum(rnorm(10000)) })[1:10] }) ## user system elapsed ## 27.616 1.214 11.974 # 案1 system.time({ N <- 10000 pforeach(i = pb_icount(N)) ({ sum(rnorm(10000)) })[1:10] }) |========================================================================| 100% ## user system elapsed ## 16.797 0.945 12.455 # 案2 system.time({ N <- 10000 pforeach(i = icount(N), .combine = pb(N)) ({ sum(rnorm(10000)) })[1:10] }) |========================================================================| 100% ## user system elapsed ## 24.370 1.107 12.799
まとめ
案1(ループの前に出す)
- あらかじめ関数を作成する必要がある
- まだ
icount
関数しか対応できていない - 100%になってから完了まで少し間がある
案2(ループの後に出す)
- 引数にループ回数が必要
- 任意の関数に適用できる
- progressbarの進行速度が適切そう(これについては実行して確認してみてください)
.multicombine=TRUE
にすると(.maxcombine
を変化させると)progressbarの回数に影響する
.multicombine
でcombine回数が変化することで、案2ではループ回数を表すprogressbarにはなりませんが、進捗状況を表すという意味では案2は十分に良い策だと思います。
きちんとループ回数にしたいという場合は案1を使っていくしかないかもしれないです。
LC_ALL=Cでsortを高速に
bashのsortがLC_ALL=Cにすると速くなるという記事を見つけました(割と有名なようで)。
- 追記: sort を使うときは,LC_ALL=C を忘れずに - ny23の日記
- LC_ALL環境変数とsortコマンド - sileのブログ
- データ集計コマンドを極めてシステム処理と業務速度を爆速化するお話 - Web就活日記
ちょっとbashで試してみます。 一応sortのversionも晒しておきます。
$ sort --version sort (GNU coreutils) 5.93 Copyright (C) 2005 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. Written by Mike Haertel and Paul Eggert.
それでは、10桁の英数字列を100万個生成し、それをソートして比較します。
$ cat /dev/urandom | LC_ALL=C tr -dc '[:alnum:]' | fold -w 10 | head -n 1000000 > words.txt $ time LC_ALL=C sort words.txt > /dev/null real 0m0.751s user 0m0.712s sys 0m0.039s $ time LC_ALL=ja_JP.UTF-8 sort words.txt > /dev/null real 0m16.918s user 0m16.820s sys 0m0.076s $ time LC_ALL=en_US.UTF-8 sort words.txt > /dev/null real 0m17.057s user 0m16.915s sys 0m0.093s
なぜLC_ALL=Cにすると速くなるかというと、バイト列でソートしてくれるからだそうです。でもそのせいで並び順も変わってしまう点には注意が必要です。
Rで試した
Rのsort関数も同じような挙動をするのでしょうか。気になります。
dat <- read.table("words.txt")[[1]] bench <- function(lc_all) { Sys.setlocale(locale = lc_all) time <- proc.time() assign(lc_all, sort(dat), pos = globalenv()) proc.time() - time } bench("C") ## user system elapsed ## 5.850 0.042 7.430 bench("ja_JP.UTF-8") ## user system elapsed ## 19.240 0.085 20.647 bench("en_US.UTF-8") ## user system elapsed ## 19.081 0.076 20.366
しっかり差がついています。
all(get("C") == get("ja_JP.UTF-8")) ## [1] FALSE all(get("C") == get("en_US.UTF-8")) ## [1] FALSE all(get("ja_JP.UTF-8") == get("en_US.UTF-8")) ## [1] TRUE
ただutf-8かどうかで並び順は変わってしまうようですね。
ちなみにこちらはdplyrのarrange関数を使って比較した場合です。
bench2 <- function(lc_all) { Sys.setlocale(locale = lc_all) time <- proc.time() assign(lc_all, arrange(as.data.frame(dat), dat)[[1]], pos = globalenv()) proc.time() - time } bench2("C") ## user system elapsed ## 2.406 0.013 2.722 bench2("ja_JP.UTF-8") ## user system elapsed ## 2.877 0.020 3.294 bench2("en_US.UTF-8") ## user system elapsed ## 2.325 0.012 2.602
as.data.frameをはさんでもこの速さ。さすがの羽鳥氏です。
all(get("C") == get("ja_JP.UTF-8")) ## [1] TRUE all(get("C") == get("en_US.UTF-8")) ## [1] TRUE all(get("ja_JP.UTF-8") == get("en_US.UTF-8")) ## [1] TRUE
速度がほとんど変わらないように、結果も変わらない。安心です。
Pythonでlightblueを入れたときのなぐり書き
bluetooth通信をMacでするために必要なlightblueを入れる際に苦労したのでメモ。
環境
第一の壁
公式サイトから落としたファイルでやったときも試しましたが、そっちのエラーの方がイミフで解決不能だったので、githubから落とします。
適当なフォルダに移動してファイルを落としましょう。
$ cd python/tmp $ git clone https://github.com/0-1-0/lightblue-0.4
解凍します。
$ tar xzvf lightblue-0.4.tar.gz
解凍したら、そのフォルダに移動して、とその前に必要なパッケージを入れないといけません(どこかに書いてあったような気がするだけでいらないかもしれないです)。
$ pip install pyobjc
をしておいて下さい。 それでは
$ sudo python setup.py install
でインストールです。本来ならこれでうまくいくとおもうのですが、僕の場合はだめでした。
こんな具合でエラーが出れば僕と一緒です。
$ sudo python setup.py install running install running build running build_py running install_lib running install_egg_info Removing /***/***/***/lib/python2.7/site-packages/lightblue-0.4-py2.7.egg-info Writing /***/***/***/lib/python2.7/site-packages/lightblue-0.4-py2.7.egg-info Build settings from command line: ARCHS = $(NATIVE_ARCH_ACTUAL) DEPLOYMENT_LOCATION = YES DSTROOT = / INSTALL_PATH = /Library/Frameworks === BUILD TARGET LightAquaBlue OF PROJECT LightAquaBlue WITH CONFIGURATION Release === Check dependencies No architectures to compile for (ARCHS=$(NATIVE_ARCH_ACTUAL), VALID_ARCHS=i386 x86_64). ** INSTALL FAILED ** The following build commands failed: Check dependencies (1 failure)
正しい解決策かどうかわかりませんが、やってみたことを羅列しておきます。
- setup.pyのline66の
-arch 'x86_64'
を取り除く - src/mac/Lightblue/Lightblue.xcodeprojをXcodeで開き、Build SettingのArchitecturesのReleaseを
ppc i386
からNative Architecture of Build Machine (x86_64)
に変更 - 同じファイルについて、Build SettingのValid Architecturesを
i386 x86_64
からx86_64
のみに変更
これをしたら再び、
$ sudo python setup.py install
と唱えます。さあimport lightblue
と入力してみましょう!
何も言われなければ成功です!僕の場合はだめでした!
第二の壁
さてさきほどのコマンドでエラーがでてしまった僕ですが、エラー内容は以下のとおりです。
>>> import lightblue /***/***/***/lib/python2.7/site-packages/objc/_bridgesupport.py:643: RuntimeWarning: Error parsing BridgeSupport data for IOBluetooth: PyObjCRT_SkipTypeSpec: Got '0x29' at end of union encoding, expecting '0x29' warnings.warn("Error parsing BridgeSupport data for %s: %s" % (frameworkName, e), RuntimeWarning)
ただこのバグは解決済みのようでした。しかしpipで入れたもの(3.0.4)は解決前でしたので、
https://bitbucket.org/ronaldoussoren/pyobjcから最新のものを落としましょう。
そんでもってディレクトリ移動してインストール。
$ cd ronaldoussoren-pyobjc-**********/pyobjc-core $ sudo python setup.py install
エラーも吐かずに終えたので、import lightblue
を試したところ、無事に成功しました。
foreachについてまとめたい
foreachパッケージのforeach
関数についてです。
Rで並列処理を行う際に今まで使用してきましたが、引数は.combine
をいじるくらいでした。他にも%dopar%
とかよくわからないものを蔑ろにしていました。この関数は今後もよく使うことになりそうなので、頑張ってまとめてみたいと思います。
基本
基本形としてはこんな感じです。
foreach(i = 範囲) %do% { -- 処理 -- }
1~3の平方根の計算例が以下になります。返り値はデフォルトでlist
です。
> foreach(i = 1:3) %do% { sqrt(i) } [[1]] [1] 1 [[2]] [1] 1.414214 [[3]] [1] 1.732051
イテレータをここではi
としていますが、もちろんa
でもb
でもokです。さらに言えば、イテレータは2つ以上あっても大丈夫で、その場合は要素数の少ない方に合わせて終了します。
> foreach(a = 1:1000, b = rep(10, 2)) %do% { a + b } [[1]] [1] 11 [[2]] [1] 12
%do% と %dopar%
%do%
と%dopar%
の違いは並列処理をするかどうかです。
%do% : 並列処理なし
%dopar% : 並列処理あり
本エントリは並列処理ではなく、あくまでもforeach
の使い方としたいので、%do%
を用いていきます。(一部除く)
また余談ですが、並列化する/しないをうまく書き分ける話があるみたいです。
- foreachパッケージで、並列化する/しないを綺麗に書き分ける - My Life as a Mock Quant
- foreach の registerDoSEQ() について #rstatsj - Qiita
引数
.combine
これは返り値に関する引数です。正確には、返り値を生成する関数を指定する引数でしょうか。c
やrbind
などの関数を指定することでベクトルや行列で返り値を構成することができます。指定しないとlist
形式で返されます。
> foreach(i = 1:3, .combine = c) %do% { exp(i) } [1] 2.718282 7.389056 20.085537 > foreach(i = 1:3, .combine = rbind) %do% { rnorm(3) } [,1] [,2] [,3] result.1 -0.6189962 1.081181 0.8920234 result.2 -0.6117038 1.418355 0.1769341 result.3 -0.4464221 -0.357747 -0.9254935
少し応用で、以下のような用法もあります。
> foreach(i = 1:3, .combine = sum) %do% { i^2 } [1] 14
.combine
の関数はこの記事によるとReduce
関数をイメージすると良いとのことです。(Reduce
関数についてはこの記事がわかりやすかったです)
.init
この引数で指定した値が返り値の初めに入ります。その際.combine
を指定しないとエラーが出ます。若干わかりにくいかもしれません。以下に例を示します。
foreach(i = 1:5, .combine = c) %do% { i+10 } ## [1] 11 12 13 14 15 foreach(i = 1:5, .combine = c, .init = 1:2) %do% { i+10 } ## [1] 1 2 11 12 13 14 15
先ほど述べたように、そのまま最初に加わります。
rbind
など行列で返すと行(列)名が異なるようです。
foreach(i = 1:5, .combine = cbind, .init = 1:4) %do% { rnorm(4) } ## accum result.1 result.2 result.3 result.4 result.5 ## [1,] 1 -0.6002596 -1.0264209 -0.34754260 -1.6679419 0.60796432 ## [2,] 2 2.1873330 -0.7104066 -0.95161857 -0.3802265 -1.61788271 ## [3,] 3 1.5326106 0.2568837 -0.04502772 0.9189966 -0.05556197 ## [4,] 4 -0.2357004 -0.2466919 -0.78490447 -0.5753470 0.51940720
.final
これは、繰り返し処理後にかける関数を指定します。デフォルトではNULL
ですので何もかけません。単純に次にかける処理をここに指定しても良いと思います。
例えば、ベクトルで返ってきた正規乱数の和をとってみましょう。まず和をとる前が、以下です。
# 10個の正規乱数 set.seed(123) foreach(i = 1:10, .combine = c) %do% { rnorm(1) } ## [1] -0.56047565 -0.23017749 1.55870831 0.07050839 0.12928774 1.71506499 ## [7] 0.46091621 -1.26506123 -0.68685285 -0.44566197
これらの和をとります。和をとるだけなら色々な方法がありますので、複数示します。
set.seed(123) foreach(i = 1:10, .combine = c, .final = sum) %do% { rnorm(1) } ## [1] 0.7462564 set.seed(123) sum(foreach(i = 1:10, .combine = c) %do% { rnorm(1) }) ## [1] 0.7462564 set.seed(123) foreach(i = 1:10, .combine = sum) %do% { rnorm(1) } ## [1] 0.7462564
どれも正しく同じ答えが出ています。しかし、繰り返し数を増やしてみると3番目の方法がやや時間がかかるようになります。これについては.combine
のところで記述したReduce
関数をイメージすることができるとよくわかります。簡単に言えば、1番目と2番目は最後に一度関数をかけただけ、一方3番目は繰り返しのたびにsum
関数をかけているという違いがあり、その分だけ処理が増えています。
.inorder
これは返り値を正しい順番で返すかどうかの引数です。もう少し正確に言うと、正しい順番であることを保証するかどうかの引数です。デフォルトでは、順番を保証する(TRUE
)となっています。
では"正しい順番"とはどういうことでしょう。ここでの"正しい"は、イテレータが指定した順番通りに結果が返ってくることです。これが保証されるのがTRUE
の時、保証されないのがFALSE
の時です。FALSE
の時の順番を、仮に"素直な順番"とします。パラレルに計算が行われると、複数の処理が別々に行われます。しかしその処理時間はそれぞれで異なる場合があり、その処理が終わった順に並べられた時、これが"素直な順番"になります。"素直な順番"は、偶然"正しい順番"になることもあるので、そういった意味で保証されません。
詳しい説明はUsing The foreach Packageを参照下さい。
正しい順番であるかどうかが重要でない場合や、.combine='+'
とした時のように順番が影響しない場合は、この引数をFALSE
にすることで少しだけ処理が速くなるようです。以下に簡単な処理を100回ほど繰り返してsummary
で比較しました。
require(doMC) # ここは各自適切なものを registerDoMC(detectCores()) # 実行時は4コア bench1 <- bench2 <- numeric(100) for(i in 1:100) { bench1[i] <- system.time(foreach(j = 1:1000, .combine = '+', .inorder = T) %dopar% { sum(rnorm(100)) })[3] bench2[i] <- system.time(foreach(j = 1:1000, .combine = '+', .inorder = F) %dopar% { sum(rnorm(100)) })[3] } summary(bench1) # .inorder = TRUE ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 0.5300 0.5550 0.5650 0.5838 0.5920 0.7520 summary(bench2) # .inorder = FALSE ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 0.4850 0.5050 0.5170 0.5286 0.5420 0.7200
FALSE
にした方がやはり多少速くなっていることがわかります。ちなみに並列処理にしなくても(%do%
でも)少し速くなります。
.multicombine
これ、注目です。
この引数は.combine
引数で与えた関数の引数が3個以上受け取れる時にTRUE
にすることで活躍します。デフォルトでは関数の引数の数が3個以上受け取れない場合を考えているのでFALSE
です(c
,cbind
,rbind
は例外で、3個以上受け取りが可能なことがわかっているので自動でTRUE
になります)。受け取れないのにも関わらずTRUE
にしてしまうとエラーが大量発生するので注意です。
受け取り可能かどうかがわからない場合は、args
関数をその関数にかけてみるとよくわかります。
例えば以下です。
args(c) ## function (..., recursive = FALSE) ## NULL args(append) ## function (x, values, after = length(x)) ## NULL
c
関数の引数は...
であり、引数にした値全部を結合してくれます。一方でappend
関数の引数はx
とvalues
であり、x
の後ろにvalues
を結合します。つまり、同様の処理をすることを考えると、c
関数は引数を3個以上受け取ることが可能であり、append
関数は引数を2個までしか受け取ることができません。
さて、これによって何が良いかというと、適宜TRUE
にしてあげることで処理が速くなります。
以下で、100個の正規乱数の和を1000個計算し、その和をとるという処理について速度比較をしました。
bench1 <- bench2 <- numeric(100) for(i in 1:100) { bench1[i] <- system.time(foreach(j = 1:1000, .combine = sum, .multicombine = T) %do% { rnorm(100) })[3] bench2[i] <- system.time(foreach(j = 1:1000, .combine = sum, .multicombine = F) %do% { rnorm(100) })[3] } summary(bench1) # .multicombine = TRUE ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 0.3500 0.3680 0.3920 0.4388 0.4290 1.2000 summary(bench2) # .multicombine = FALSE ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 0.5230 0.5535 0.6000 0.6630 0.6665 1.7420
結構速くなりましたね。パフォーマンス向上についてはforeach の .multicombine 引数について #rstatsj - Qiitaにもありましたので、そちらも御覧ください。
.maxcombine
.combine
引数で与えた関数の引数の最大個数を指定します。.multicombine = FALSE
の時(デフォルト)では2になり、.multicombine = TRUE
の時は100になります。
.errorhandling
エラー発生時の動作を示す文字列オブジェクトを指定します。選択肢は以下になります。
'stop' : エラーが発生すると処理を停止する 'remove' : エラーが発生したイテレーションの処理結果を省く 'pass' : エラーが発生したイテレーションの処理結果をエラーオブジェクトとして返す
デフォルトではstop
です。
.packages
別のパッケージの関数を用いる場合、この引数にそのパッケージ名を文字列ベクトルとして指定します%do%
の時は省略可能です。ただ以下に示す例では外しても僕の環境では実行できるのでどう影響があるのかはわかりません。
x <- matrix(runif(500), 100) y <- gl(2, 50) require(randomForest) # %do% の場合 foreach(ntree = rep(250, 4), .combine = combine) %do% { randomForest(x, y, ntree = ntree) } ## ## Call: ## randomForest(x = x, y = y, ntree = ntree) ## Type of random forest: classification ## Number of trees: 1000 ## No. of variables tried at each split: 2 ## # %dopar% の場合 foreach(ntree = rep(250, 4), .combine = combine, .packages = 'randomForest') %dopar% { randomForest(x, y, ntree = ntree) } ## ## Call: ## randomForest(x = x, y = y, ntree = ntree) ## Type of random forest: classification ## Number of trees: 1000 ## No. of variables tried at each split: 2 ##
.export
この引数で、オブジェクト名を文字列ベクトルで指定します。ここで指定するのは、現在の環境で定義されていないオブジェクトを指定します。
この引数で、現在の環境で定義されていないオブジェクトを文字列ベクトルで指定し、使用可能にします。デフォルトはNULL
です。
foreachパッケージで並列化する時、現在の"環境"にない変数・関数は、明示的に.export引数にて指定しなければならない - My Life as a Mock Quant
この記事によれば、上の環境のオブジェクトも.export
しないといけないとありましたが、これまたどうも僕の実行環境ではexport必要なしにできました。どういうことなんでしょう。
.noexport
この引数は、上記の.export
でオブジェクトを使用可能にしてしまったせいで、同名のオブジェクトに影響が出てしまう際に用います。.export
で例えばls()
として一度にexportした場合、余計なものまでexportされてしまうので、この引数で除外するオブジェクトを文字列ベクトルで指定します。
.verbose
これをTRUE
にすることで、繰り返しの詳細を出力することができます。エラーが発生した場合に使ってみると良いでしょう。デフォルトはFALSE
です。
foreach(i = 1:2, .combine = c, .verbose = TRUE) %do% { i } ## evaluation # 1: ## $i ## [1] 1 ## ## result of evaluating expression: ## [1] 1 ## got results for task 1 ## numValues: 1, numResults: 1, stopped: FALSE ## returning status FALSE ## evaluation # 2: ## $i ## [1] 2 ## ## result of evaluating expression: ## [1] 2 ## got results for task 2 ## numValues: 2, numResults: 2, stopped: FALSE ## returning status FALSE ## numValues: 2, numResults: 2, stopped: TRUE ## first call to combine function ## evaluating call object to combine results: ## fun(result.1, result.2) ## [1] 1 2
when
ヘルプマニュアルの同ページに記載されていたのでついでに。
これは%:%
演算子と組み合わせてif
文のように利用することができます。例えば以下。
set.seed(123) foreach(a = rnorm(10), .combine = c) %do% sqrt(a) ## [1] NaN NaN 1.2484824 0.2655342 0.3595660 1.3096049 0.6789081 ## [8] NaN NaN NaN Warning messages: 1: In sqrt(a) : NaNs produced 2: In sqrt(a) : NaNs produced 3: In sqrt(a) : NaNs produced 4: In sqrt(a) : NaNs produced 5: In sqrt(a) : NaNs produced
これは10個の正規乱数について「平方根を求めベクトルで返す」という処理になっています。当然負の実数(numeric
)に対し平方根をとることはできないので、NaN
及び警告メッセージが出ています。これに対して、when
を用いた例が以下になります。
set.seed(123) foreach(a = rnorm(10), .combine = c) %:% when(a >= 0) %do% sqrt(a) ## [1] 1.2484824 0.2655342 0.3595660 1.3096049 0.6789081
when
によって、これは10個の正規乱数について「a
が0以上ならば平方根を求めベクトルで返す」という処理になっています。
条件を満たしたものがどれかがわからないので、あまり使う機会はなさそうです。
速度比較
並列処理を指定しているわけではないですが、通常のfor
文との速度比較をしてみたいと思います。雑比較です。
# for文を回します before <- proc.time() x <- numeric(10000) for(i in 1:10000) x[i] <- sum(rnorm(1000)) proc.time() - before ## user system elapsed ## 1.174 0.010 1.261 # foreach文を回します(工夫なし) before <- proc.time() x <- foreach(i = 1:10000, .combine = c) %do% { sum(rnorm(1000)) } proc.time() - before ## user system elapsed ## 6.664 0.042 6.703 # foreach文を回します(.inorder変更) before <- proc.time() x <- foreach(i = 1:10000, .combine = c, .inorder = FALSE) %do% { sum(rnorm(1000)) } proc.time() - before ## user system elapsed ## 6.447 0.047 6.489 # foreach文を回します(.multicombine変更) before <- proc.time() x <- foreach(i = 1:10000, .combine = c, .multicombine = TRUE) %do% { sum(rnorm(1000)) } proc.time() - before ## user system elapsed ## 4.936 0.036 4.879 # foreach文を回します(.inorderと.multicombine変更) before <- proc.time() x <- foreach(i = 1:10000, .combine = c, .multicombine = TRUE, .inorder = FALSE) %do% { sum(rnorm(1000)) } proc.time() - before ## user system elapsed ## 4.697 0.025 4.702
やっぱり並列処理をしないとforeach
を使う意味はなさそうですね。
工夫点として.inorder
と.multicombine
がありましたが、.inorder
は小さい効果、.multicombine
は大きい効果が見込めそうです。
参考
- foreach+doSNOWパッケージを使って、並列処理をやってみた - My Life as a Mock Quant
- Rで最小公倍数 - もうカツ丼でいいよな
- Package 'foreach'
- Using The foreach Package
- Getting Started with doMC and foreach
- Rのforeach関数を使って並列計算をしたい (その1) - 300億円欲しい
- foreachパッケージで並列化する時、現在の"環境"にない変数・関数は、明示的に.export引数にて指定しなければならない - My Life as a Mock Quant
- foreach の .multicombine 引数について #rstatsj - Qiita
- 作者: 福島真太朗
- 出版社/メーカー: ソシム
- 発売日: 2014/09/24
- メディア: 単行本
- この商品を含むブログ (2件) を見る