Make array slices views rather than copies

Before this commit, if the slice fits in VWA, it would make a copy
rather than a view. This is slower as it requires a memcpy of the
contents.
This commit is contained in:
Peter Zhu 2022-07-27 11:26:31 -04:00
Родитель 2375afb8d6
Коммит 1c16645216
2 изменённых файлов: 43 добавлений и 10 удалений

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

@ -1027,7 +1027,7 @@ rb_ary_memsize(VALUE ary)
static VALUE
ary_make_shared(VALUE ary)
{
assert(!ARY_EMBED_P(ary));
assert(USE_RVARGC || !ARY_EMBED_P(ary));
ary_verify(ary);
if (ARY_SHARED_P(ary)) {
@ -1037,21 +1037,38 @@ ary_make_shared(VALUE ary)
return ary;
}
else if (OBJ_FROZEN(ary)) {
rb_ary_transient_heap_evacuate(ary, TRUE);
ary_shrink_capa(ary);
if (!ARY_EMBED_P(ary)) {
rb_ary_transient_heap_evacuate(ary, TRUE);
ary_shrink_capa(ary);
}
return ary;
}
else {
long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary);
const VALUE *ptr;
rb_ary_transient_heap_evacuate(ary, TRUE);
long capa = ARY_CAPA(ary);
long len = RARRAY_LEN(ary);
/* Shared roots cannot be embedded because the reference count
* (refcnt) is stored in as.heap.aux.capa. */
VALUE shared = ary_alloc_heap(0);
rb_ary_transient_heap_evacuate(ary, TRUE);
ptr = ARY_HEAP_PTR(ary);
if (ARY_EMBED_P(ary)) {
/* Cannot use ary_heap_alloc because we don't want to allocate
* on the transient heap. */
VALUE *ptr = ALLOC_N(VALUE, capa);
ARY_SET_PTR(shared, ptr);
ary_memcpy(shared, 0, len, RARRAY_PTR(ary));
FL_UNSET_EMBED(ary);
ARY_SET_HEAP_LEN(ary, len);
ARY_SET_PTR(ary, ptr);
}
else {
ARY_SET_PTR(shared, RARRAY_PTR(ary));
}
FL_UNSET_EMBED(shared);
ARY_SET_LEN(shared, capa);
ARY_SET_PTR(shared, ptr);
ary_mem_clear(shared, len, capa - len);
FL_SET_SHARED_ROOT(shared);
ARY_SET_SHARED_ROOT_REFCNT(shared, 1);
@ -1318,7 +1335,9 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
assert(len >= 0);
assert(offset+len <= RARRAY_LEN(ary));
if (ary_embeddable_p(len)) {
const size_t rarray_embed_capa_max = (sizeof(struct RArray) - offsetof(struct RArray, as.ary)) / sizeof(VALUE);
if ((size_t)len <= rarray_embed_capa_max && ary_embeddable_p(len)) {
VALUE result = ary_alloc_embed(klass, len);
ary_memcpy(result, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary) + offset);
ARY_SET_EMBED_LEN(result, len);

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

@ -9958,7 +9958,21 @@ static void
gc_ref_update_array(rb_objspace_t * objspace, VALUE v)
{
if (ARY_SHARED_P(v)) {
#if USE_RVARGC
VALUE old_root = RARRAY(v)->as.heap.aux.shared_root;
#endif
UPDATE_IF_MOVED(objspace, RARRAY(v)->as.heap.aux.shared_root);
#if USE_RVARGC
VALUE new_root = RARRAY(v)->as.heap.aux.shared_root;
// If the root is embedded and its location has changed
if (ARY_EMBED_P(new_root) && new_root != old_root) {
size_t offset = (size_t)(RARRAY(v)->as.heap.ptr - RARRAY(old_root)->as.ary);
GC_ASSERT(RARRAY(v)->as.heap.ptr >= RARRAY(old_root)->as.ary);
RARRAY(v)->as.heap.ptr = RARRAY(new_root)->as.ary + offset;
}
#endif
}
else {
long len = RARRAY_LEN(v);