Shared arrays created by Array#dup and so on points
a shared_root object to manage lifetime of Array buffer.
However, sometimes shared_root is called only shared so
it is confusing. So I fixed these wording "shared" to "shared_root".

* RArray::heap::aux::shared -> RArray::heap::aux::shared_root
* ARY_SHARED() -> ARY_SHARED_ROOT()
* ARY_SHARED_NUM() -> ARY_SHARED_ROOT_REFCNT()

Also, add some debug_counters to count shared array objects.

* ary_shared_create: shared ary by Array#dup and so on.
* ary_shared: finished in shard.
* ary_shared_root_occupied: shared_root but has only 1 refcnt.
  The number (ary_shared - ary_shared_root_occupied) is meaningful.
This commit is contained in:
Koichi Sasada 2019-07-19 13:02:38 +09:00
Родитель 547f574b63
Коммит 182ae1407b
4 изменённых файлов: 75 добавлений и 57 удалений

115
array.c
Просмотреть файл

@ -10,7 +10,6 @@
Copyright (C) 2000 Information-technology Promotion Agency, Japan Copyright (C) 2000 Information-technology Promotion Agency, Japan
**********************************************************************/ **********************************************************************/
#include "ruby/encoding.h" #include "ruby/encoding.h"
#include "ruby/util.h" #include "ruby/util.h"
#include "ruby/st.h" #include "ruby/st.h"
@ -116,21 +115,21 @@ VALUE rb_cArray;
RARRAY(ary)->as.heap.aux.capa = (n); \ RARRAY(ary)->as.heap.aux.capa = (n); \
} while (0) } while (0)
#define ARY_SHARED(ary) (assert(ARY_SHARED_P(ary)), RARRAY(ary)->as.heap.aux.shared) #define ARY_SHARED_ROOT(ary) (assert(ARY_SHARED_P(ary)), RARRAY(ary)->as.heap.aux.shared_root)
#define ARY_SET_SHARED(ary, value) do { \ #define ARY_SET_SHARED(ary, value) do { \
const VALUE _ary_ = (ary); \ const VALUE _ary_ = (ary); \
const VALUE _value_ = (value); \ const VALUE _value_ = (value); \
assert(!ARY_EMBED_P(_ary_)); \ assert(!ARY_EMBED_P(_ary_)); \
assert(ARY_SHARED_P(_ary_)); \ assert(ARY_SHARED_P(_ary_)); \
assert(ARY_SHARED_ROOT_P(_value_)); \ assert(ARY_SHARED_ROOT_P(_value_)); \
RB_OBJ_WRITE(_ary_, &RARRAY(_ary_)->as.heap.aux.shared, _value_); \ RB_OBJ_WRITE(_ary_, &RARRAY(_ary_)->as.heap.aux.shared_root, _value_); \
} while (0) } while (0)
#define RARRAY_SHARED_ROOT_FLAG FL_USER5 #define RARRAY_SHARED_ROOT_FLAG FL_USER5
#define ARY_SHARED_ROOT_P(ary) (FL_TEST((ary), RARRAY_SHARED_ROOT_FLAG)) #define ARY_SHARED_ROOT_P(ary) (FL_TEST((ary), RARRAY_SHARED_ROOT_FLAG))
#define ARY_SHARED_NUM(ary) \ #define ARY_SHARED_ROOT_REFCNT(ary) \
(assert(ARY_SHARED_ROOT_P(ary)), RARRAY(ary)->as.heap.aux.capa) (assert(ARY_SHARED_ROOT_P(ary)), RARRAY(ary)->as.heap.aux.capa)
#define ARY_SHARED_OCCUPIED(ary) (ARY_SHARED_NUM(ary) == 1) #define ARY_SHARED_ROOT_OCCUPIED(ary) (ARY_SHARED_ROOT_REFCNT(ary) == 1)
#define ARY_SET_SHARED_NUM(ary, value) do { \ #define ARY_SET_SHARED_ROOT_REFCNT(ary, value) do { \
assert(ARY_SHARED_ROOT_P(ary)); \ assert(ARY_SHARED_ROOT_P(ary)); \
RARRAY(ary)->as.heap.aux.capa = (value); \ RARRAY(ary)->as.heap.aux.capa = (value); \
} while (0) } while (0)
@ -160,7 +159,7 @@ ary_verify_(VALUE ary, const char *file, int line)
assert(RB_TYPE_P(ary, T_ARRAY)); assert(RB_TYPE_P(ary, T_ARRAY));
if (FL_TEST(ary, ELTS_SHARED)) { if (FL_TEST(ary, ELTS_SHARED)) {
VALUE root = RARRAY(ary)->as.heap.aux.shared; VALUE root = RARRAY(ary)->as.heap.aux.shared_root;
const VALUE *ptr = ARY_HEAP_PTR(ary); const VALUE *ptr = ARY_HEAP_PTR(ary);
const VALUE *root_ptr = RARRAY_CONST_PTR_TRANSIENT(root); const VALUE *root_ptr = RARRAY_CONST_PTR_TRANSIENT(root);
long len = ARY_HEAP_LEN(ary), root_len = RARRAY_LEN(root); long len = ARY_HEAP_LEN(ary), root_len = RARRAY_LEN(root);
@ -470,16 +469,16 @@ ary_double_capa(VALUE ary, long min)
} }
static void static void
rb_ary_decrement_share(VALUE shared) rb_ary_decrement_share(VALUE shared_root)
{ {
if (shared) { if (shared_root) {
long num = ARY_SHARED_NUM(shared) - 1; long num = ARY_SHARED_ROOT_REFCNT(shared_root) - 1;
if (num == 0) { if (num == 0) {
rb_ary_free(shared); rb_ary_free(shared_root);
rb_gc_force_recycle(shared); rb_gc_force_recycle(shared_root);
} }
else if (num > 0) { else if (num > 0) {
ARY_SET_SHARED_NUM(shared, num); ARY_SET_SHARED_ROOT_REFCNT(shared_root, num);
} }
} }
} }
@ -487,8 +486,8 @@ rb_ary_decrement_share(VALUE shared)
static void static void
rb_ary_unshare(VALUE ary) rb_ary_unshare(VALUE ary)
{ {
VALUE shared = RARRAY(ary)->as.heap.aux.shared; VALUE shared_root = RARRAY(ary)->as.heap.aux.shared_root;
rb_ary_decrement_share(shared); rb_ary_decrement_share(shared_root);
FL_UNSET_SHARED(ary); FL_UNSET_SHARED(ary);
} }
@ -501,21 +500,22 @@ rb_ary_unshare_safe(VALUE ary)
} }
static VALUE static VALUE
rb_ary_increment_share(VALUE shared) rb_ary_increment_share(VALUE shared_root)
{ {
long num = ARY_SHARED_NUM(shared); long num = ARY_SHARED_ROOT_REFCNT(shared_root);
if (num >= 0) { if (num >= 0) {
ARY_SET_SHARED_NUM(shared, num + 1); ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1);
} }
return shared; return shared_root;
} }
static void static void
rb_ary_set_shared(VALUE ary, VALUE shared) rb_ary_set_shared(VALUE ary, VALUE shared_root)
{ {
rb_ary_increment_share(shared); rb_ary_increment_share(shared_root);
FL_SET_SHARED(ary); FL_SET_SHARED(ary);
ARY_SET_SHARED(ary, shared); RB_DEBUG_COUNTER_INC(obj_ary_shared_create);
ARY_SET_SHARED(ary, shared_root);
} }
static inline void static inline void
@ -531,28 +531,28 @@ rb_ary_modify(VALUE ary)
rb_ary_modify_check(ary); rb_ary_modify_check(ary);
if (ARY_SHARED_P(ary)) { if (ARY_SHARED_P(ary)) {
long shared_len, len = RARRAY_LEN(ary); long shared_len, len = RARRAY_LEN(ary);
VALUE shared = ARY_SHARED(ary); VALUE shared_root = ARY_SHARED_ROOT(ary);
ary_verify(shared); ary_verify(shared_root);
if (len <= RARRAY_EMBED_LEN_MAX) { if (len <= RARRAY_EMBED_LEN_MAX) {
const VALUE *ptr = ARY_HEAP_PTR(ary); const VALUE *ptr = ARY_HEAP_PTR(ary);
FL_UNSET_SHARED(ary); FL_UNSET_SHARED(ary);
FL_SET_EMBED(ary); FL_SET_EMBED(ary);
MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len); MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len);
rb_ary_decrement_share(shared); rb_ary_decrement_share(shared_root);
ARY_SET_EMBED_LEN(ary, len); ARY_SET_EMBED_LEN(ary, len);
} }
else if (ARY_SHARED_OCCUPIED(shared) && len > ((shared_len = RARRAY_LEN(shared))>>1)) { else if (ARY_SHARED_ROOT_OCCUPIED(shared_root) && len > ((shared_len = RARRAY_LEN(shared_root))>>1)) {
long shift = RARRAY_CONST_PTR_TRANSIENT(ary) - RARRAY_CONST_PTR_TRANSIENT(shared); long shift = RARRAY_CONST_PTR_TRANSIENT(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root);
FL_UNSET_SHARED(ary); FL_UNSET_SHARED(ary);
ARY_SET_PTR(ary, RARRAY_CONST_PTR_TRANSIENT(shared)); ARY_SET_PTR(ary, RARRAY_CONST_PTR_TRANSIENT(shared_root));
ARY_SET_CAPA(ary, shared_len); ARY_SET_CAPA(ary, shared_len);
RARRAY_PTR_USE_TRANSIENT(ary, ptr, { RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
MEMMOVE(ptr, ptr+shift, VALUE, len); MEMMOVE(ptr, ptr+shift, VALUE, len);
}); });
FL_SET_EMBED(shared); FL_SET_EMBED(shared_root);
rb_ary_decrement_share(shared); rb_ary_decrement_share(shared_root);
} }
else { else {
VALUE *ptr = ary_heap_alloc(ary, len); VALUE *ptr = ary_heap_alloc(ary, len);
@ -579,14 +579,14 @@ ary_ensure_room_for_push(VALUE ary, long add_len)
} }
if (ARY_SHARED_P(ary)) { if (ARY_SHARED_P(ary)) {
if (new_len > RARRAY_EMBED_LEN_MAX) { if (new_len > RARRAY_EMBED_LEN_MAX) {
VALUE shared = ARY_SHARED(ary); VALUE shared_root = ARY_SHARED_ROOT(ary);
if (ARY_SHARED_OCCUPIED(shared)) { if (ARY_SHARED_ROOT_OCCUPIED(shared_root)) {
if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR_TRANSIENT(shared) + new_len <= RARRAY_LEN(shared)) { if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root) + new_len <= RARRAY_LEN(shared_root)) {
rb_ary_modify_check(ary); rb_ary_modify_check(ary);
ary_verify(ary); ary_verify(ary);
ary_verify(shared); ary_verify(shared_root);
return shared; return shared_root;
} }
else { else {
/* if array is shared, then it is likely it participate in push/shift pattern */ /* if array is shared, then it is likely it participate in push/shift pattern */
@ -643,7 +643,7 @@ rb_ary_shared_with_p(VALUE ary1, VALUE ary2)
{ {
if (!ARY_EMBED_P(ary1) && ARY_SHARED_P(ary1) && if (!ARY_EMBED_P(ary1) && ARY_SHARED_P(ary1) &&
!ARY_EMBED_P(ary2) && ARY_SHARED_P(ary2) && !ARY_EMBED_P(ary2) && ARY_SHARED_P(ary2) &&
RARRAY(ary1)->as.heap.aux.shared == RARRAY(ary2)->as.heap.aux.shared && RARRAY(ary1)->as.heap.aux.shared_root == RARRAY(ary2)->as.heap.aux.shared_root &&
RARRAY(ary1)->as.heap.len == RARRAY(ary2)->as.heap.len) { RARRAY(ary1)->as.heap.len == RARRAY(ary2)->as.heap.len) {
return Qtrue; return Qtrue;
} }
@ -778,6 +778,13 @@ rb_ary_free(VALUE ary)
else { else {
RB_DEBUG_COUNTER_INC(obj_ary_embed); RB_DEBUG_COUNTER_INC(obj_ary_embed);
} }
if (ARY_SHARED_P(ary)) {
RB_DEBUG_COUNTER_INC(obj_ary_shared);
}
if (ARY_SHARED_ROOT_P(ary) && ARY_SHARED_ROOT_OCCUPIED(ary)) {
RB_DEBUG_COUNTER_INC(obj_ary_shared_root_occupied);
}
} }
RUBY_FUNC_EXPORTED size_t RUBY_FUNC_EXPORTED size_t
@ -806,7 +813,7 @@ ary_make_shared(VALUE ary)
ary_verify(ary); ary_verify(ary);
if (ARY_SHARED_P(ary)) { if (ARY_SHARED_P(ary)) {
return ARY_SHARED(ary); return ARY_SHARED_ROOT(ary);
} }
else if (ARY_SHARED_ROOT_P(ary)) { else if (ARY_SHARED_ROOT_P(ary)) {
return ary; return ary;
@ -815,7 +822,7 @@ ary_make_shared(VALUE ary)
rb_ary_transient_heap_evacuate(ary, TRUE); rb_ary_transient_heap_evacuate(ary, TRUE);
ary_shrink_capa(ary); ary_shrink_capa(ary);
FL_SET_SHARED_ROOT(ary); FL_SET_SHARED_ROOT(ary);
ARY_SET_SHARED_NUM(ary, 1); ARY_SET_SHARED_ROOT_REFCNT(ary, 1);
return ary; return ary;
} }
else { else {
@ -831,13 +838,15 @@ ary_make_shared(VALUE ary)
ARY_SET_PTR((VALUE)shared, ptr); ARY_SET_PTR((VALUE)shared, ptr);
ary_mem_clear((VALUE)shared, len, capa - len); ary_mem_clear((VALUE)shared, len, capa - len);
FL_SET_SHARED_ROOT(shared); FL_SET_SHARED_ROOT(shared);
ARY_SET_SHARED_NUM((VALUE)shared, 1); ARY_SET_SHARED_ROOT_REFCNT((VALUE)shared, 1);
FL_SET_SHARED(ary); FL_SET_SHARED(ary);
RB_DEBUG_COUNTER_INC(obj_ary_shared_create);
ARY_SET_SHARED(ary, (VALUE)shared); ARY_SET_SHARED(ary, (VALUE)shared);
OBJ_FREEZE(shared); OBJ_FREEZE(shared);
ary_verify((VALUE)shared); ary_verify((VALUE)shared);
ary_verify(ary); ary_verify(ary);
return (VALUE)shared; return (VALUE)shared;
} }
} }
@ -1279,7 +1288,7 @@ rb_ary_shift(VALUE ary)
ARY_SET(ary, 0, Qnil); ARY_SET(ary, 0, Qnil);
ary_make_shared(ary); ary_make_shared(ary);
} }
else if (ARY_SHARED_OCCUPIED(ARY_SHARED(ary))) { else if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) {
RARRAY_PTR_USE_TRANSIENT(ary, ptr, ptr[0] = Qnil); RARRAY_PTR_USE_TRANSIENT(ary, ptr, ptr[0] = Qnil);
} }
ARY_INCREASE_PTR(ary, 1); /* shift ptr */ ARY_INCREASE_PTR(ary, 1); /* shift ptr */
@ -1338,7 +1347,7 @@ rb_ary_behead(VALUE ary, long n)
rb_ary_modify_check(ary); rb_ary_modify_check(ary);
if (ARY_SHARED_P(ary)) { if (ARY_SHARED_P(ary)) {
if (ARY_SHARED_OCCUPIED(ARY_SHARED(ary))) { if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) {
setup_occupied_shared: setup_occupied_shared:
ary_mem_clear(ary, 0, n); ary_mem_clear(ary, 0, n);
} }
@ -1374,12 +1383,12 @@ ary_ensure_room_for_unshift(VALUE ary, int argc)
} }
if (ARY_SHARED_P(ary)) { if (ARY_SHARED_P(ary)) {
VALUE shared = ARY_SHARED(ary); VALUE shared_root = ARY_SHARED_ROOT(ary);
capa = RARRAY_LEN(shared); capa = RARRAY_LEN(shared_root);
if (ARY_SHARED_OCCUPIED(shared) && capa > new_len) { if (ARY_SHARED_ROOT_OCCUPIED(shared_root) && capa > new_len) {
rb_ary_modify_check(ary); rb_ary_modify_check(ary);
head = RARRAY_CONST_PTR_TRANSIENT(ary); head = RARRAY_CONST_PTR_TRANSIENT(ary);
sharedp = RARRAY_CONST_PTR_TRANSIENT(shared); sharedp = RARRAY_CONST_PTR_TRANSIENT(shared_root);
goto makeroom_if_need; goto makeroom_if_need;
} }
} }
@ -1410,10 +1419,10 @@ ary_ensure_room_for_unshift(VALUE ary, int argc)
head = sharedp + argc + room; head = sharedp + argc + room;
} }
ARY_SET_PTR(ary, head - argc); ARY_SET_PTR(ary, head - argc);
assert(ARY_SHARED_OCCUPIED(ARY_SHARED(ary))); assert(ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary)));
ary_verify(ary); ary_verify(ary);
return ARY_SHARED(ary); return ARY_SHARED_ROOT(ary);
} }
else { else {
/* sliding items */ /* sliding items */
@ -3784,24 +3793,24 @@ rb_ary_replace(VALUE copy, VALUE orig)
if (copy == orig) return copy; if (copy == orig) return copy;
if (RARRAY_LEN(orig) <= RARRAY_EMBED_LEN_MAX) { if (RARRAY_LEN(orig) <= RARRAY_EMBED_LEN_MAX) {
VALUE shared = 0; VALUE shared_root = 0;
if (ARY_OWNS_HEAP_P(copy)) { if (ARY_OWNS_HEAP_P(copy)) {
ary_heap_free(copy); ary_heap_free(copy);
} }
else if (ARY_SHARED_P(copy)) { else if (ARY_SHARED_P(copy)) {
shared = ARY_SHARED(copy); shared_root = ARY_SHARED_ROOT(copy);
FL_UNSET_SHARED(copy); FL_UNSET_SHARED(copy);
} }
FL_SET_EMBED(copy); FL_SET_EMBED(copy);
ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR_TRANSIENT(orig)); ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR_TRANSIENT(orig));
if (shared) { if (shared_root) {
rb_ary_decrement_share(shared); rb_ary_decrement_share(shared_root);
} }
ARY_SET_LEN(copy, RARRAY_LEN(orig)); ARY_SET_LEN(copy, RARRAY_LEN(orig));
} }
else { else {
VALUE shared = ary_make_shared(orig); VALUE shared_root = ary_make_shared(orig);
if (ARY_OWNS_HEAP_P(copy)) { if (ARY_OWNS_HEAP_P(copy)) {
ary_heap_free(copy); ary_heap_free(copy);
} }
@ -3811,7 +3820,7 @@ rb_ary_replace(VALUE copy, VALUE orig)
FL_UNSET_EMBED(copy); FL_UNSET_EMBED(copy);
ARY_SET_PTR(copy, ARY_HEAP_PTR(orig)); ARY_SET_PTR(copy, ARY_HEAP_PTR(orig));
ARY_SET_LEN(copy, ARY_HEAP_LEN(orig)); ARY_SET_LEN(copy, ARY_HEAP_LEN(orig));
rb_ary_set_shared(copy, shared); rb_ary_set_shared(copy, shared_root);
} }
ary_verify(copy); ary_verify(copy);
return copy; return copy;

Просмотреть файл

@ -206,6 +206,15 @@ RB_DEBUG_COUNTER(obj_str_fstr)
RB_DEBUG_COUNTER(obj_ary_embed) RB_DEBUG_COUNTER(obj_ary_embed)
RB_DEBUG_COUNTER(obj_ary_transient) RB_DEBUG_COUNTER(obj_ary_transient)
RB_DEBUG_COUNTER(obj_ary_ptr) RB_DEBUG_COUNTER(obj_ary_ptr)
/*
ary_shared_create: shared ary by Array#dup and so on.
ary_shared: finished in shard.
ary_shared_root_occupied: shared_root but has only 1 refcnt.
The number (ary_shared - ary_shared_root_occupied) is meaningful.
*/
RB_DEBUG_COUNTER(obj_ary_shared_create)
RB_DEBUG_COUNTER(obj_ary_shared)
RB_DEBUG_COUNTER(obj_ary_shared_root_occupied)
RB_DEBUG_COUNTER(obj_hash_empty) RB_DEBUG_COUNTER(obj_hash_empty)
RB_DEBUG_COUNTER(obj_hash_under4) RB_DEBUG_COUNTER(obj_hash_under4)

6
gc.c
Просмотреть файл

@ -5083,7 +5083,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
case T_ARRAY: case T_ARRAY:
if (FL_TEST(obj, ELTS_SHARED)) { if (FL_TEST(obj, ELTS_SHARED)) {
VALUE root = any->as.array.as.heap.aux.shared; VALUE root = any->as.array.as.heap.aux.shared_root;
gc_mark(objspace, root); gc_mark(objspace, root);
} }
else { else {
@ -8007,7 +8007,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
case T_ARRAY: case T_ARRAY:
if (FL_TEST(obj, ELTS_SHARED)) { if (FL_TEST(obj, ELTS_SHARED)) {
UPDATE_IF_MOVED(objspace, any->as.array.as.heap.aux.shared); UPDATE_IF_MOVED(objspace, any->as.array.as.heap.aux.shared_root);
} }
else { else {
gc_ref_update_array(objspace, obj); gc_ref_update_array(objspace, obj);
@ -11219,7 +11219,7 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj)
case T_ARRAY: case T_ARRAY:
if (FL_TEST(obj, ELTS_SHARED)) { if (FL_TEST(obj, ELTS_SHARED)) {
APPENDF((BUFF_ARGS, "shared -> %s", APPENDF((BUFF_ARGS, "shared -> %s",
rb_obj_info(RARRAY(obj)->as.heap.aux.shared))); rb_obj_info(RARRAY(obj)->as.heap.aux.shared_root)));
} }
else if (FL_TEST(obj, RARRAY_EMBED_FLAG)) { else if (FL_TEST(obj, RARRAY_EMBED_FLAG)) {
APPENDF((BUFF_ARGS, "[%s%s] len: %d (embed)", APPENDF((BUFF_ARGS, "[%s%s] len: %d (embed)",

Просмотреть файл

@ -1054,7 +1054,7 @@ struct RArray {
long len; long len;
union { union {
long capa; long capa;
VALUE shared; VALUE shared_root;
} aux; } aux;
const VALUE *ptr; const VALUE *ptr;
} heap; } heap;