diff options
author | Peter Zhu <peter@peterzhu.ca> | 2022-07-27 11:26:31 -0400 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2022-07-28 10:02:12 -0400 |
commit | 1c16645216b6db04ccb1144e6f7e085e1e0a6132 (patch) | |
tree | 79f835bb18ecbabf593bbbc52eee85efd8e7c86c | |
parent | 2375afb8d6fd218cd9083749d87a7a59946d8938 (diff) | |
download | ruby-1c16645216b6db04ccb1144e6f7e085e1e0a6132.tar.gz |
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.
-rw-r--r-- | array.c | 39 | ||||
-rw-r--r-- | gc.c | 14 |
2 files changed, 43 insertions, 10 deletions
@@ -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); @@ -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); |