Skip to content

Commit

Permalink
Reduce array allocations for literal arrays with splats and other args
Browse files Browse the repository at this point in the history
Previously, a literal array with a splat and any other args resulted in
more than one array allocation:

```ruby
[1, *a]
[*a, 1]
[*a, *a]
[*a, 1, 2]

[*a, a]
[*a, 1, *a]
[*a, 1, a]
[*a, a, a]

[*a, a, *a]
[*a, 1, *a, 1]
[*a, 1, *a, *a]

[*a, a, *a, a]
```

This is because previously Ruby would use newarray and concatarray
to create the array, which both each allocate an array internally.

This changes the compilation to use concattoarray and pushtoarray,
which do not allocate arrays.  It also updates the peephole optimizer
to optimize the duparray/concattoarray sequence to
putobject/concattoarray, mirroring the existing duparray/concatarray
optimization.

These changes reduce the array allocations for the above examples to
a single array allocation, except for:

```
[*a, 1, a]
[*a, a, a]
```

The reason for this is because optimizing this case to only allocate
1 array requires changes to compile_array, which would currently
conflict with an unmerged pull request (#9721).  After that pull
request is merged, it should be possible to refactor things to only
allocate a 1 array for all literal arrays (or 2 for arrays with
keyword splats).
  • Loading branch information
jeremyevans committed Jan 27, 2024
1 parent e337c94 commit 223910b
Showing 1 changed file with 12 additions and 25 deletions.
37 changes: 12 additions & 25 deletions compile.c
Expand Up @@ -3377,15 +3377,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
/*
* ...
* duparray [...]
* concatarray
* concatarray | concattoarray
* =>
* ...
* putobject [...]
* concatarray
* concatarray | concattoarray
*/
if (IS_INSN_ID(iobj, duparray)) {
LINK_ELEMENT *next = iobj->link.next;
if (IS_INSN(next) && IS_INSN_ID(next, concatarray)) {
if (IS_INSN(next) && (IS_INSN_ID(next, concatarray) || IS_INSN_ID(next, concattoarray))) {
iobj->insn_id = BIN(putobject);
}
}
Expand Down Expand Up @@ -4916,25 +4916,6 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop
return 1;
}

/* Compile an array containing the single element represented by node */
static int
compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node)
{
if (static_literal_node_p(node, iseq, false)) {
VALUE ary = rb_ary_hidden_new(1);
rb_ary_push(ary, static_literal_value(node, iseq));
OBJ_FREEZE(ary);

ADD_INSN1(ret, node, duparray, ary);
}
else {
CHECK(COMPILE_(ret, "array element", node, FALSE));
ADD_INSN1(ret, node, newarray, INT2FIX(1));
}

return 1;
}

static inline int
static_literal_node_pair_p(const NODE *node, const rb_iseq_t *iseq)
{
Expand Down Expand Up @@ -10412,7 +10393,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
else {
CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
CHECK(COMPILE(ret, "argscat body", RNODE_ARGSCAT(node)->nd_body));
ADD_INSN(ret, node, concatarray);
ADD_INSN(ret, node, concattoarray);
}
break;
}
Expand All @@ -10425,8 +10406,14 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
else {
CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
CHECK(compile_array_1(iseq, ret, RNODE_ARGSPUSH(node)->nd_body));
ADD_INSN(ret, node, concatarray);
const NODE *body_node = RNODE_ARGSPUSH(node)->nd_body;
if (static_literal_node_p(body_node, iseq, false)) {
ADD_INSN1(ret, body_node, putobject, static_literal_value(body_node, iseq));
}
else {
CHECK(COMPILE_(ret, "array element", body_node, FALSE));
}
ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
}
break;
}
Expand Down

0 comments on commit 223910b

Please sign in to comment.