「大人の教養・知識・気付き」を伸ばすブログ

一流の大人(ビジネスマン、政治家、リーダー…)として知っておきたい、教養・社会動向を意外なところから取り上げ学ぶことで“気付く力”を伸ばすブログです。データ分析・語学に力点を置いています。 →現在、コンサルタントの雛になるべく、少しずつ勉強中です(※2024年1月21日改訂)。

MENU

プログラムとしてのRを学ぶ(その14/16)

 \mathrm{R}をプログラムとして見たときに注意・検討すべきところを学んでおきたい、ということで

を読んでいく。

14. パフォーマンス強化

 プログラムで一般的な問題である時間と空間のトレードオフは、\mathrm{R}だと以下の理由からより一層関心が集まる:

  • \mathrm{R}インタプリタ型言語であり、\mathrm{C}で記述されていないコマンドや独自の\mathrm{R}コードでは想定外に遅くなることがある。
  • \mathrm{R}セッションのすべてのオブジェクトは\mathrm{R}のメモリアドレス空間に格納される。

14.1 高速なRコードの記述

 \mathrm{R}コードを高速化するための主な方法は以下のとおりである:

  • ベクトル化、バイトコードコンパイルおよびその他の手法を用いて\mathrm{R}コードを最適化する
  • \mathrm{CPU}に負担を掛けるコードの主要部分を\mathrm{C}\mathrm{C++}などのコンパイル型言語で記述する
  • 何らかの形の並列\mathrm{R}でコードを記述する

ここでは1番目を取り上げる。

14.2 ループに対する恐れ

 ループを回避するだけでは必ずしもパフォーマンス改善につながらないものの、ベクトル化によって劇的なパフォーマンス改善が図れる場合がある。

14.2.1 高速化のためのベクトル化

 ループの代わりにベクトル化を用いることができる。
 ループを用いると速度低下をもたらすのは、以下の原因がある:

  • 構文上はループは無害のように見えるものの、\mathrm{R}\mathrm{for}は関数である。
  • \mathrm{:}もまた関数である。
  • ベクトルの添字演算は関数呼び出しを表しており、読込のための[]の呼び出しおよび書き込みの<-で関数呼び出しが生じる。

 他には\mathrm{ifelse}(),\mathrm{which}(),\mathrm{where}(),\mathrm{any}(),\mathrm{all}(),\mathrm{cumsum}()および\mathrm{cumprod}()関数が改善に寄与し得る。

14.3 関数型プログラミングとメモリに関する問題

 ほとんどの\mathrm{R}オブジェクトは不変である。そのため、\mathrm{R}の演算は特定のオブジェクトに再割り当てする関数として実装されており、パフォーマンスに影響がある。

14.3.1 ベクトル割り当ての問題

 例として、ベクトルのある要素に値を割り当てる

z[3] <- 8 # これは正確にはz <- "[<-"(z,3,value=8)である

を考える。これは内部で\mathrm{z}をコピーし、コピーの要素38に変更し、その結果のベクトルを\mathrm{z}に再割り当てする。このように表面上はベクトルの1要素を変更しているにもかかわらず、内部的にはベクトル全体を再計算しているのに他ならない。

14.3.2 変更時コピーの問題

 \mathrm{R}は(通常)「変更時コピー」という方針に従う。たとえば

y <- z

を実行すると、当初\mathrm{y}\mathrm{z}と同じメモリ領域を共有する。しかしどちらかを変更すると、メモリの異なる領域にコピーが作成され、変更された変数は新しいメモリを占有する。しかし変更された変数のメモリを再配置すると共有問題が無くなるため、最初の変更のみが影響を受ける。\mathrm{tracemem}()はこうしたメモリ再配置を報告する。

# メモリ配置変更動作を行なわない例
z <- runif(10)
tracemem(z)
z[3] <- 8
tracemem(z)

14.4 Rprof()の動作

 \mathrm{R}コードの動作が不必要に遅いと感じた場合、\mathrm{Rprof}()を用いる。

x <- runif(1000000)
Rprof()
invisible(powers1(x,8))
Rprof(NULL)
summaryRprof()

14.5 バイトコードコンパイル

 バイトコードコンパイラを用いると、コードの高速化を図ることができる。

# z <- x +yは
# z[i] <- x[i] + y[i]よりも高速

library(compiler)
f <- function() for (i in 1:length(x)) z[i] <<- x[i] + y[i]
cf <- cmpfun(f)
system.time(cf())

14.6 データがメモリに収まらない

 \mathrm{R}はマシンのワードサイズ(32ビットか64ビットか)や\mathrm{RAM}要領にかかわらずオブジェクトのサイズを2^{31}-1に制限している。しかし注意を払えば大量のメモリを必要とするアプリケーションにも適切に対応できる。

14.6.1 チャンキング

 パッケージを追加する必要のないのは、ディスクファイルからデータを一度にチャンクとして読み込む方法である。たとえば変数の平均や割合を求めることが目的ならば、\mathrm{read.table}()\mathrm{skip}引数を用いる。

14.6.2 メモリ管理のためのRパッケージ

 \mathrm{RMySQL}\mathrm{biglm}により、大規模なデータに対して操作を行う方法がある。
 また\mathrm{ff}\mathrm{bigmemory}により、大規模なデータセットを用いる方法がある。

プライバシーポリシー お問い合わせ