More Related Content Similar to Rのデータ構造とメモリ管理(20) More from Takeshi Arabiki More from Takeshi Arabiki(17) Rのデータ構造とメモリ管理1. Rのデータ構造とメモリ管理
一人 R 勉強会 #1 (2012/11/25)
@a_bicky
2012/12/15 改訂版
2. 自己紹介
• Takeshi Arabiki
‣ Web 業界の底辺エンジニア
‣ Twitter & はてな: @a_bicky & id:a_bicky
• 興味など
機械学習、自然言語処理、R
• ブログ
あらびき日記 http://d.hatena.ne.jp/a_bicky/
3. R関係の主な発表
Tokyo.R #16 Tsukuba.R #9 Rユーザ会 2011
http://www.slideshare.net/abicky/r-9034336 http://www.slideshare.net/abicky/r-10128090 http://www.slideshare.net/abicky/rtwitter
6. 2つのデータ構造
SEXPREC
たぶん Symbolic EXPression RECord (S-EXPression RECord) の略
*SEXP = SEXPREC
VECTOR_SEXPREC 以外のノード(オブジェクト)
VECTOR_SEXPREC
SEXPREC の Vector 版(メモリを少し節約)
*VECSEXP = VECTOR_SEXPREC
データ部分は直後のアドレスに格納
raw, logical, integer, numeric, complex, character, list, expression 等
7. SEXPREC
// cf. src/include/Rinternals.h
typedef struct SEXPREC {
struct sxpinfo_struct sxpinfo; // 詳細は後述
struct SEXPREC *attrib; // 属性情報
struct SEXPREC *gengc_next_node; // GC で使用
struct SEXPREC *gengc_prev_node; // GC で使用
// データ部分
union {
struct primsxp_struct primsxp;
struct symsxp_struct symsxp;
struct listsxp_struct listsxp;
struct envsxp_struct envsxp;
struct closxp_struct closxp;
struct promsxp_struct promsxp;
} u;
} SEXPREC, *SEXP;
8. VECTOR_SEXPREC
// cf. src/include/Rinternals.h
typedef struct VECTOR_SEXPREC {
struct sxpinfo_struct sxpinfo; // 詳細は後述
struct SEXPREC *attrib; // 属性情報
struct SEXPREC *gengc_next_node; // GC で使用
struct SEXPREC *gengc_prev_node; // GC で使用
struct vecsxp_struct vecsxp; // length, truelength
} VECTOR_SEXPREC, *VECSEXP;
// データ部分のアライメントをするための変数?
typedef union {
VECTOR_SEXPREC s;
double align; } SEXPREC_ALIGN;
// データ部分の先頭アドレスの定義
#define DATAPTR(x) (((SEXPREC_ALIGN *) (x)) + 1)
9. VECTOR_SEXPREC
VECTOR_SEXPREC のイメージ
VECTOR_SEXPREC
ノード
sxpinfo attrib gengc_next_node
gengc_prev_node vecsxp_struct
データ部分 sizeof(VECREC) * length
10. sxpinfo_struct
// cf. src/include/Rinternals.h
struct sxpinfo_struct {
SEXPTYPE type : 5; // ノードのタイプ
unsigned int obj : 1;
unsigned int named : 2; // コピーの際の挙動を制御
unsigned int gp : 16;
unsigned int mark : 1; // Mark-and-Sweap の mark
unsigned int debug : 1;
unsigned int trace : 1; // tracemem 等で使用
unsigned int spare : 1; // もう使われていないらしい
unsigned int gcgen : 1; // GC の世代情報
unsigned int gccls : 3; // サイズに応じたクラス番号
};
12. 基礎知識
• メモリ管理の上でノードは3種類に分かれる
‣ non-vector (gccls = 0)
‣ small vector(gccls = 1, 2, 3, 4, 5, 6)
‣ large vector (gccls = 7)
• GC は世代別 GC
‣ old 世代は gcgen = 0, 1 の2つ存在
‣ old 世代からより若い世代への参照は参照が発生する度
にリストに追加する形で管理(デフォルトオプション)
13. R の世代別 GC
各世代は双方向リストとなっている
→ 挿入・削除を定数時間で行える
R_GenHeap[gccls].New R_GenHeap[gccls].Old[gen]
= &R_GenHeap[gccls].NewPeg = &R_GenHeap[gccls].OldPeg[gen]
gengc_prev_node
gengc_next_node
node3 node1
node2 node1
node2
gen th
New generation Old generation
14. R の世代別 GC の手順
1. old 世代のうち何番目の世代までを GC 対象とするか決定
2. GC 対象の old 世代から参照されているノードを参照元世代に移す
3. GC 対象の old 世代の世代 (gcgen) をインクリメントして unmark
した上で new 世代に移す
4. GC 対象外の old 世代から参照されているノードを mark し old 世
代に移す
5. root からたどって参照のあるノードを mark し old 世代に移す
6. large vector の new 世代に存在するノードを解放
7. 場合によっては non-vector, small vector のうち unmarked な
ノードを解放
cf. RunGenCollect (src/main/memory.c)
15. gc 関数の結果の意味
> gc() # 全世代を対象とした GC (full GC)
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 180515 9.7 407500 21.8 350000 18.7
Vcells 287939 2.2 905753 7.0 877008 6.7
used Mb
使用中のノードの数。 sizeof(SEXPREC) x
non-vector, small ノードの数 / 1024 /
Ncells
vector, large vecotor 全 1024
てを含む。
全 vector ノードのデータ ← を個数ではなく Mb で
部分(ヘッダ部分を除 表した値。
Vcells
く)が VECREC 何個分の つまり sizeof(VECREC)
サイズに相当するか x 個数 / 1024 / 1024
16. メモリ確保の仕組み
• small vector
1. データ部分のサイズが VECREC 何個分相当かを算出
2. 1 の結果が1以下であれば gccls = 1、2以下は2、4以下は3、6
以下は4、8以下は5、16以下は6という具合に gccls をセット
3. 対応する gccls の使用されていないノードを割り当てる
4. 使用されていないノードが存在しない場合は一定量のノードを新
しく生成 (GetNewPage)
• non-vector
‣ ↑の手順のうち gccls を 0 とする
17. メモリ確保の仕組み
• large vector
1. データ部分のサイズが VECREC 何個分相当かを算出
2. 1 の結果が16より大きければ gccls を 7 にセット
3. 必要なサイズをその都度 malloc で確保
いずれの場合もメモリが足りない場合は GC を実行したり
ヒープサイズを調整する
18. tracemem の仕組み
// cf. src/main/duplicate.c
SEXP duplicate(SEXP s){
SEXP t;
duplicate_counter++;
t = duplicate1(s);
// (s)->sxpinfo.trace フラグが立っていればレポートを出力
if (RTRACE(s) && !(TYPEOF(s) == CLOSXP ||
TYPEOF(s) == BUILTINSXP ||
TYPEOF(s) == SPECIALSXP ||
TYPEOF(s) == PROMSXP ||
TYPEOF(s) == ENVSXP)){
memtrace_report(s,t);
// コピーしたノードにもフラグを立てる
SET_RTRACE(t,1);
}
return t;
}
19. tracemem の挙動
duplicate が呼ばれないコピーはトレースできない
> tracemem(a <- 1:5)
[1] "<0x103011a48>"
> b <- a
> b[1] <- 1 # そのまま代入した場合はトレースされる
tracemem[0x103011a48 -> 0x101b41760]:
tracemem[0x101b41760 -> 0x103063188]:
> c <- a[1:5]
> c[1] <- 1 # インデックスを指定した場合はトレースされない
> tracemem(a <- list(a = 1:5))
[1] "<0x1064c0958>"
> b <- a
> a$a <- 1 # そのまま代入した場合はトレースされる
tracemem[0x1064c0958 -> 0x1067e9598]:
> c <- a$a
> c[1] <- 1 # 一部の要素の代入はトレースされない
21. 参考文献
• R Internals - 1.1 SEXPs
http://cran.r-project.org/doc/manuals/R-ints.html#SEXPs
• RObjectModel - r-optimization-engine - Optimizing the
performance of R - Google Project Hosting
http://code.google.com/p/r-optimization-engine/wiki/
RObjectModel
• R 2.15.2 のソースコード
主に src/include/Rinternals.h, src/main/include/Defn.h,
src/main/memory.c
22. 変更履歴
• 2012/12/15
世代別 GC の旧世代の世代数が2なのに3になっていたのを訂正