summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2022-07-20 14:06:28 -0700
committerJeremy Evans <code@jeremyevans.net>2022-08-09 22:19:46 -0700
commitfc4b4f2e8db3d68b80b9c7580c40a0165736006c (patch)
tree7004516463d341a5cb5795e43eaf4a0894d57d5c
parent7922fd65e30fb9f011b939dead38cda94a7e2721 (diff)
Expand newarray/expandarray optimization for unequal operands
This optimizes unbalanced multiple assignment cases such as: ```ruby a.b, c.d = e, f, g a.b, c.d, e.f = g, h ``` Previously, this would use: ``` newarray(3) expandarray(2, 0) newarray(2) expandarray(3, 0) ``` These would both allocate arrays. This switches to opt_reverse with either pop or putnil: ``` pop opt_reverse(2) putnil opt_reverse(3) ``` This avoids an unnecessary array allocation, and results in a 35-76% performance increase in these types of unbalanced cases (tested with benchmark/masgn.yml).
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/6158
-rw-r--r--compile.c42
1 files changed, 39 insertions, 3 deletions
diff --git a/compile.c b/compile.c
index 9716f04374..8879e661fe 100644
--- a/compile.c
+++ b/compile.c
@@ -3335,16 +3335,20 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
if (IS_INSN_ID(iobj, newarray)) {
LINK_ELEMENT *next = iobj->link.next;
if (IS_INSN(next) && IS_INSN_ID(next, expandarray) &&
- OPERAND_AT(iobj, 0) == OPERAND_AT(next, 0) &&
OPERAND_AT(next, 1) == INT2FIX(0)) {
- ELEM_REMOVE(next);
+ VALUE op1, op2;
+ op1 = OPERAND_AT(iobj, 0);
+ op2 = OPERAND_AT(next, 0);
+ ELEM_REMOVE(next);
+
+ if (op1 == op2) {
/*
* newarray 2
* expandarray 2, 0
* =>
* swap
*/
- if (OPERAND_AT(iobj, 0) == INT2FIX(2)) {
+ if (op1 == INT2FIX(2)) {
INSN_OF(iobj) = BIN(swap);
iobj->operand_size = 0;
}
@@ -3357,6 +3361,38 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
else {
INSN_OF(iobj) = BIN(opt_reverse);
}
+ }
+ else {
+ NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id);
+ long diff = FIX2LONG(op1) - FIX2LONG(op2);
+ INSN_OF(iobj) = BIN(opt_reverse);
+ OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0);
+
+ if (op1 > op2) {
+ /* X > Y
+ * newarray X
+ * expandarray Y, 0
+ * =>
+ * pop * (Y-X)
+ * opt_reverse Y
+ */
+ for (; diff > 0; diff--) {
+ INSERT_BEFORE_INSN(iobj, &dummy_line_node, pop);
+ }
+ }
+ else { /* (op1 < op2) */
+ /* X < Y
+ * newarray X
+ * expandarray Y, 0
+ * =>
+ * putnil * (Y-X)
+ * opt_reverse Y
+ */
+ for (; diff < 0; diff++) {
+ INSERT_BEFORE_INSN(iobj, &dummy_line_node, putnil);
+ }
+ }
+ }
}
}