Ruby1.9/オブジェクトのC表現を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#contents
*RObject [#u173efc8]
Ruby1.9ではObjectのC表現RObject構造体がかなり変わっていま...
struct RObject {
struct RBasic basic;
struct st_table *iv_tbl;
};
シンプルです。次にRuby1.9のRObject構造体です。((unionの名...
#define ROBJECT_EMBED_LEN_MAX 3
struct RObject {
struct RBasic basic;
union {
struct {
long len;
VALUE *ptr;
} heap;
VALUE ary[ROBJECT_EMBED_LEN_MAX];
} as;
};
#define ROBJECT_EMBED FL_USER1
#define ROBJECT_LEN(o) \
((RBASIC(o)->flags & ROBJECT_EMBED) ? \
ROBJECT_EMBED_LEN_MAX : \
ROBJECT(o)->as.heap.len)
#define ROBJECT_PTR(o) \
((RBASIC(o)->flags & ROBJECT_EMBED) ? \
ROBJECT(o)->as.ary : \
ROBJECT(o)->as.heap.ptr)
すっげー複雑です:-)。読解しましょう。
**rb_ivar_set(variable.c) [#q1dde9b3]
***インスタンス変数番号テーブル [#tfafe155]
Ruby1.8のiv_tblはインスタンス変数のテーブルです。というわ...
switch (TYPE(obj)) {
case T_OBJECT:
klass = rb_obj_class(obj);
if (!RCLASS_IV_INDEX_TBL(klass))
RCLASS_IV_INDEX_TBL(klass) = st_init_numtabl...
Ruby1.9ではRObject単独でインスタンス変数を管理するのでは...
typedef struct {
VALUE super;
struct st_table *iv_tbl;
} rb_classext_t;
struct RClass {
struct RBasic basic;
rb_classext_t *ptr;
struct st_table *m_tbl;
struct st_table *iv_index_tbl;
};
#define RCLASS_IV_INDEX_TBL(c) (RCLASS(c)->iv_index_tbl)
先に進みましょう。
ivar_extended = 0;
if (!st_lookup(RCLASS_IV_INDEX_TBL(klass), id, &...
index = RCLASS_IV_INDEX_TBL(klass)->num_entr...
st_add_direct(RCLASS_IV_INDEX_TBL(klass), id...
ivar_extended = 1;
}
変数名を示すidからindexを拾う、ないのならエントリの追加を...
|key(id)|value(index)|
|ID(x)|0|
|ID(y)|1|
***埋め込みVALUE配列の初期化 [#r3b9f965]
len = ROBJECT_LEN(obj);
if (len <= index) {
VALUE *ptr = ROBJECT_PTR(obj);
if (index < ROBJECT_EMBED_LEN_MAX) {
RBASIC(obj)->flags |= ROBJECT_EMBED;
ptr = ROBJECT(obj)->as.ary;
for (i = 0; i < ROBJECT_EMBED_LEN_MAX; i...
ptr[i] = Qundef;
}
}
初めてrb_ivar_setが呼ばれた場合、ROBJECT_EMBEDフラグが立...
***ヒープVALUE配列の確保 [#x9237e8d]
埋め込みVALUE配列に収まらない場合、ヒープにVALUE配列が確...
else {
VALUE *newptr;
long newsize = (index+1) + (index+1)/4; ...
if (!ivar_extended &&
RCLASS_IV_INDEX_TBL(klass)->num_entr...
newsize = RCLASS_IV_INDEX_TBL(klass)...
}
番号テーブルは全インスタンスで共有されているので、新しい...
-そのクラスの新しいオブジェクト
-index => 4
-klass->iv_index_tbl->num_entries => 5
ちなみに、klass->iv_index_tbl->num_entriesが8だとifの中は...
if (RBASIC(obj)->flags & ROBJECT_EMBED) {
newptr = ALLOC_N(VALUE, newsize);
MEMCPY(newptr, ptr, VALUE, len);
RBASIC(obj)->flags &= ~ROBJECT_EMBED;
ROBJECT(obj)->as.heap.ptr = newptr;
}
else {
REALLOC_N(ROBJECT(obj)->as.heap.ptr,...
newptr = ROBJECT(obj)->as.heap.ptr;
}
for (; len < newsize; len++)
newptr[len] = Qundef;
ROBJECT(obj)->as.heap.len = newsize;
}
}
***変数値の設定 [#f0740f22]
というわけで値の設定です。
ROBJECT_PTR(obj)[index] = val;
***ところで何故埋め込めるのか [#jaf6eaa2]
何で埋め込んでいるのか、は何となくわかると思います。では...
**まとめ [#g43c94b7]
Ruby1.8とRuby1.9でのインスタンス変数の扱いをまとめると以...
-Ruby1.8
--変数の取得方法:id→VALUEのテーブルから取得
--各RObjectが変数テーブルを管理
--変数追加の際、常にメモリ確保
-Ruby1.9
--変数の取得方法:id→indexのテーブルの後、VALUE配列から取得
--RClassが変数番号テーブルを管理
--変数追加の際、必要があったらメモリ確保
スピードはそんなに変わらない気がします。メモリ効率はRuby1...
**謝辞 [#e80bcc09]
「ところで何故埋め込めるのか」の部分は[[第1回 RHGの逆襲>h...
*st_table [#i325f8fb]
Rubyのオブジェクトではありません((Hashが裏で使ってはいま...
まずRuby1.8のst_tableの宣言です。
st.h
struct st_table {
struct st_hash_type *type;
int num_bins;
int num_entries;
struct st_table_entry **bins;
};
Ruby1.9では以下のようになっています。
include/ruby/st.h
typedef st_data_t st_index_t;
#define ST_INDEX_BITS (sizeof(st_index_t) * CHAR_BIT)
struct st_table {
const struct st_hash_type *type;
st_index_t num_bins;
unsigned int entries_packed : 1;
st_index_t num_entries : ST_INDEX_BITS - 1;
struct st_table_entry **bins;
struct st_table_entry *head;
};
entries_packedとheadが増えています。なお、num_entriesが1...
次にst_table_entry構造体(Ruby1.9)です。Ruby1.8に比べてf...
st.c
struct st_table_entry {
unsigned int hash;
st_data_t key;
st_data_t record;
st_table_entry *next;
st_table_entry *fore, *back;
};
**st_init_table_with_size [#baf9f1fe]
Ruby実装ではメソッドテーブルなどいろいろなところでst_init...
st_init_numtable(void)
{
return st_init_table(&type_numhash);
}
st_init_table(const struct st_hash_type *type)
{
return st_init_table_with_size(type, 0);
}
とst_init_table_with_size関数に処理が委譲されています。
#define MAX_PACKED_NUMHASH 5
st_init_table_with_size(const struct st_hash_type *type,...
{
st_table *tbl;
size = new_size(size); /* round up to prime number */
tbl = alloc(st_table);
tbl->type = type;
tbl->num_entries = 0;
tbl->entries_packed = type == &type_numhash && size/...
tbl->num_bins = size;
tbl->bins = (st_table_entry **)Calloc(size, sizeof(s...
tbl->head = 0;
return tbl;
}
というわけでハッシュのタイプがnumhashでsizeがMAX_PACKED_N...
**st_add_direct [#df54412e]
それではentries_packedがどう使われているか見てみましょう...
st_add_direct(st_table *table, st_data_t key, st_data_t ...
{
if (table->entries_packed) {
int i;
if ((table->num_entries+1) * 2 <= table->num_bin...
i = table->num_entries++;
table->bins[i*2] = (struct st_table_entry*)k...
table->bins[i*2+1] = (struct st_table_entry*...
return;
}
else {
unpack_entries(table);
}
}
従来のst_add_direct()
}
何をしているかと言うとbinsの偶数番目にキー、binsの奇数番...
**st_table_entry.{fore,back} [#s15c8dd8]
次にst_table_entry構造体に増えているforeとbackについて調...
if ((head = table->head) != 0) {\
entry->fore = head;\
(entry->back = head->back)->fore = entry;\
head->back = entry;\
}\
else {\
table->head = entry->fore = entry->back = entry;\
}\
というわけでエントリをつないでいます。どんな感じにつなが...
1エントリ目
+---- head ----+
| ^ ^ |
| back| |fore |
+-----+ +-----+
2エントリ目
+---- head -- fore --> ent2 ----+
| ^ ^ | ^ |
| fore| |back | |back |
| | +-------------+ | |
| +------------------------+
+-------------------------+
3エントリ目
+---- head -- fore --> ent2 -- fore --> ent3 ----+
| ^ ^ | ^ | ^ |
| fore| |back | |back | |back |
| | +-------------+ +-------------+ | |
| +-----------------------------------------+
+------------------------------------------+
こうして作られたリストがどこで使われているかと言うといろ...
hash = {}
hash['abc'] = 123
hash['ABC'] = 456
hash['zyx'] = 789
hash.each do |k,v|
p k, v
end ...
というコードがあった場合、Ruby1.8とRuby1.9で結果が異なり...
Ruby1.8
"ABC"
456
"abc"
123
"zyx"
789 ...
Ruby1.9
"abc"
123
"ABC"
456
"zyx"
789
ruby-listで「RubyってHashに格納した順番って保持されないん...
**まとめ [#b161787b]
Ruby1.9になったからと言って変わってなさそうなst_tableです...
-数値テーブルの場合、小さいサイズならハッシュではなくただ...
-ハッシュエントリを格納順につないだ双方向リスト(Hash#eac...
終了行:
#contents
*RObject [#u173efc8]
Ruby1.9ではObjectのC表現RObject構造体がかなり変わっていま...
struct RObject {
struct RBasic basic;
struct st_table *iv_tbl;
};
シンプルです。次にRuby1.9のRObject構造体です。((unionの名...
#define ROBJECT_EMBED_LEN_MAX 3
struct RObject {
struct RBasic basic;
union {
struct {
long len;
VALUE *ptr;
} heap;
VALUE ary[ROBJECT_EMBED_LEN_MAX];
} as;
};
#define ROBJECT_EMBED FL_USER1
#define ROBJECT_LEN(o) \
((RBASIC(o)->flags & ROBJECT_EMBED) ? \
ROBJECT_EMBED_LEN_MAX : \
ROBJECT(o)->as.heap.len)
#define ROBJECT_PTR(o) \
((RBASIC(o)->flags & ROBJECT_EMBED) ? \
ROBJECT(o)->as.ary : \
ROBJECT(o)->as.heap.ptr)
すっげー複雑です:-)。読解しましょう。
**rb_ivar_set(variable.c) [#q1dde9b3]
***インスタンス変数番号テーブル [#tfafe155]
Ruby1.8のiv_tblはインスタンス変数のテーブルです。というわ...
switch (TYPE(obj)) {
case T_OBJECT:
klass = rb_obj_class(obj);
if (!RCLASS_IV_INDEX_TBL(klass))
RCLASS_IV_INDEX_TBL(klass) = st_init_numtabl...
Ruby1.9ではRObject単独でインスタンス変数を管理するのでは...
typedef struct {
VALUE super;
struct st_table *iv_tbl;
} rb_classext_t;
struct RClass {
struct RBasic basic;
rb_classext_t *ptr;
struct st_table *m_tbl;
struct st_table *iv_index_tbl;
};
#define RCLASS_IV_INDEX_TBL(c) (RCLASS(c)->iv_index_tbl)
先に進みましょう。
ivar_extended = 0;
if (!st_lookup(RCLASS_IV_INDEX_TBL(klass), id, &...
index = RCLASS_IV_INDEX_TBL(klass)->num_entr...
st_add_direct(RCLASS_IV_INDEX_TBL(klass), id...
ivar_extended = 1;
}
変数名を示すidからindexを拾う、ないのならエントリの追加を...
|key(id)|value(index)|
|ID(x)|0|
|ID(y)|1|
***埋め込みVALUE配列の初期化 [#r3b9f965]
len = ROBJECT_LEN(obj);
if (len <= index) {
VALUE *ptr = ROBJECT_PTR(obj);
if (index < ROBJECT_EMBED_LEN_MAX) {
RBASIC(obj)->flags |= ROBJECT_EMBED;
ptr = ROBJECT(obj)->as.ary;
for (i = 0; i < ROBJECT_EMBED_LEN_MAX; i...
ptr[i] = Qundef;
}
}
初めてrb_ivar_setが呼ばれた場合、ROBJECT_EMBEDフラグが立...
***ヒープVALUE配列の確保 [#x9237e8d]
埋め込みVALUE配列に収まらない場合、ヒープにVALUE配列が確...
else {
VALUE *newptr;
long newsize = (index+1) + (index+1)/4; ...
if (!ivar_extended &&
RCLASS_IV_INDEX_TBL(klass)->num_entr...
newsize = RCLASS_IV_INDEX_TBL(klass)...
}
番号テーブルは全インスタンスで共有されているので、新しい...
-そのクラスの新しいオブジェクト
-index => 4
-klass->iv_index_tbl->num_entries => 5
ちなみに、klass->iv_index_tbl->num_entriesが8だとifの中は...
if (RBASIC(obj)->flags & ROBJECT_EMBED) {
newptr = ALLOC_N(VALUE, newsize);
MEMCPY(newptr, ptr, VALUE, len);
RBASIC(obj)->flags &= ~ROBJECT_EMBED;
ROBJECT(obj)->as.heap.ptr = newptr;
}
else {
REALLOC_N(ROBJECT(obj)->as.heap.ptr,...
newptr = ROBJECT(obj)->as.heap.ptr;
}
for (; len < newsize; len++)
newptr[len] = Qundef;
ROBJECT(obj)->as.heap.len = newsize;
}
}
***変数値の設定 [#f0740f22]
というわけで値の設定です。
ROBJECT_PTR(obj)[index] = val;
***ところで何故埋め込めるのか [#jaf6eaa2]
何で埋め込んでいるのか、は何となくわかると思います。では...
**まとめ [#g43c94b7]
Ruby1.8とRuby1.9でのインスタンス変数の扱いをまとめると以...
-Ruby1.8
--変数の取得方法:id→VALUEのテーブルから取得
--各RObjectが変数テーブルを管理
--変数追加の際、常にメモリ確保
-Ruby1.9
--変数の取得方法:id→indexのテーブルの後、VALUE配列から取得
--RClassが変数番号テーブルを管理
--変数追加の際、必要があったらメモリ確保
スピードはそんなに変わらない気がします。メモリ効率はRuby1...
**謝辞 [#e80bcc09]
「ところで何故埋め込めるのか」の部分は[[第1回 RHGの逆襲>h...
*st_table [#i325f8fb]
Rubyのオブジェクトではありません((Hashが裏で使ってはいま...
まずRuby1.8のst_tableの宣言です。
st.h
struct st_table {
struct st_hash_type *type;
int num_bins;
int num_entries;
struct st_table_entry **bins;
};
Ruby1.9では以下のようになっています。
include/ruby/st.h
typedef st_data_t st_index_t;
#define ST_INDEX_BITS (sizeof(st_index_t) * CHAR_BIT)
struct st_table {
const struct st_hash_type *type;
st_index_t num_bins;
unsigned int entries_packed : 1;
st_index_t num_entries : ST_INDEX_BITS - 1;
struct st_table_entry **bins;
struct st_table_entry *head;
};
entries_packedとheadが増えています。なお、num_entriesが1...
次にst_table_entry構造体(Ruby1.9)です。Ruby1.8に比べてf...
st.c
struct st_table_entry {
unsigned int hash;
st_data_t key;
st_data_t record;
st_table_entry *next;
st_table_entry *fore, *back;
};
**st_init_table_with_size [#baf9f1fe]
Ruby実装ではメソッドテーブルなどいろいろなところでst_init...
st_init_numtable(void)
{
return st_init_table(&type_numhash);
}
st_init_table(const struct st_hash_type *type)
{
return st_init_table_with_size(type, 0);
}
とst_init_table_with_size関数に処理が委譲されています。
#define MAX_PACKED_NUMHASH 5
st_init_table_with_size(const struct st_hash_type *type,...
{
st_table *tbl;
size = new_size(size); /* round up to prime number */
tbl = alloc(st_table);
tbl->type = type;
tbl->num_entries = 0;
tbl->entries_packed = type == &type_numhash && size/...
tbl->num_bins = size;
tbl->bins = (st_table_entry **)Calloc(size, sizeof(s...
tbl->head = 0;
return tbl;
}
というわけでハッシュのタイプがnumhashでsizeがMAX_PACKED_N...
**st_add_direct [#df54412e]
それではentries_packedがどう使われているか見てみましょう...
st_add_direct(st_table *table, st_data_t key, st_data_t ...
{
if (table->entries_packed) {
int i;
if ((table->num_entries+1) * 2 <= table->num_bin...
i = table->num_entries++;
table->bins[i*2] = (struct st_table_entry*)k...
table->bins[i*2+1] = (struct st_table_entry*...
return;
}
else {
unpack_entries(table);
}
}
従来のst_add_direct()
}
何をしているかと言うとbinsの偶数番目にキー、binsの奇数番...
**st_table_entry.{fore,back} [#s15c8dd8]
次にst_table_entry構造体に増えているforeとbackについて調...
if ((head = table->head) != 0) {\
entry->fore = head;\
(entry->back = head->back)->fore = entry;\
head->back = entry;\
}\
else {\
table->head = entry->fore = entry->back = entry;\
}\
というわけでエントリをつないでいます。どんな感じにつなが...
1エントリ目
+---- head ----+
| ^ ^ |
| back| |fore |
+-----+ +-----+
2エントリ目
+---- head -- fore --> ent2 ----+
| ^ ^ | ^ |
| fore| |back | |back |
| | +-------------+ | |
| +------------------------+
+-------------------------+
3エントリ目
+---- head -- fore --> ent2 -- fore --> ent3 ----+
| ^ ^ | ^ | ^ |
| fore| |back | |back | |back |
| | +-------------+ +-------------+ | |
| +-----------------------------------------+
+------------------------------------------+
こうして作られたリストがどこで使われているかと言うといろ...
hash = {}
hash['abc'] = 123
hash['ABC'] = 456
hash['zyx'] = 789
hash.each do |k,v|
p k, v
end ...
というコードがあった場合、Ruby1.8とRuby1.9で結果が異なり...
Ruby1.8
"ABC"
456
"abc"
123
"zyx"
789 ...
Ruby1.9
"abc"
123
"ABC"
456
"zyx"
789
ruby-listで「RubyってHashに格納した順番って保持されないん...
**まとめ [#b161787b]
Ruby1.9になったからと言って変わってなさそうなst_tableです...
-数値テーブルの場合、小さいサイズならハッシュではなくただ...
-ハッシュエントリを格納順につないだ双方向リスト(Hash#eac...
ページ名: