summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2022-07-27 11:26:31 -0400
committerPeter Zhu <peter@peterzhu.ca>2022-07-28 10:02:12 -0400
commit1c16645216b6db04ccb1144e6f7e085e1e0a6132 (patch)
tree79f835bb18ecbabf593bbbc52eee85efd8e7c86c
parent2375afb8d6fd218cd9083749d87a7a59946d8938 (diff)
downloadruby-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.c39
-rw-r--r--gc.c14
2 files changed, 43 insertions, 10 deletions
diff --git a/array.c b/array.c
index 8c7fa583e9..b2ebf3c0e9 100644
--- a/array.c
+++ b/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);
diff --git a/gc.c b/gc.c
index 84f9aa30f6..cd48c47a94 100644
--- a/gc.c
+++ b/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);