File 7921-Add-fast-lanes-for-2-and-2-on-nil-arguments.patch of Package erlang
From 8eeb95d479c61ad30aa70aef3f686f9107845b27 Mon Sep 17 00:00:00 2001
From: Michael Davis <mcarsondavis@gmail.com>
Date: Mon, 26 Aug 2024 16:39:42 -0400
Subject: [PATCH] Add fast-lanes for '++'/2 and '--'/2 on nil arguments
Currently appending an empty list to another list causes the left-hand
side list to be fully copied. We can skip the append operation when the
right-hand side is nil since it shouldn't change the left-hand side
list. In this fast-lane we scan the list to determine if it is improper
so that cases like `[1, 2 | 3] ++ []` behave the same as cases like
`[1, 2 | 3] ++ [4]`: return a badarg error.
This patch adds similar fast-lanes for '--'/2: we can return the first
argument when either the first or second arguments are nil.
---
erts/emulator/beam/erl_bif_lists.c | 56 +++++++++++++++++++++++++++++-
1 file changed, 55 insertions(+), 1 deletion(-)
diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c
index 5dcd53d613..10afb3adaa 100644
--- a/erts/emulator/beam/erl_bif_lists.c
+++ b/erts/emulator/beam/erl_bif_lists.c
@@ -56,6 +56,36 @@ static int append_ctx_bin_dtor(Binary *context_bin) {
return 1;
}
+static int append_empty_rhs(Process *p, ErtsAppendContext *context) {
+ static const Sint ELEMENTS_PER_RED = 32;
+ Sint budget, count;
+
+ budget = ELEMENTS_PER_RED * ERTS_BIF_REDS_LEFT(p);
+
+#ifdef DEBUG
+ budget = budget / 10 + 1;
+#endif
+
+ for (count = 0; count < budget && is_list(context->iterator); count++) {
+ context->iterator = CDR(list_val(context->iterator));
+ }
+
+ BUMP_REDS(p, count / ELEMENTS_PER_RED);
+
+ if (is_list(context->iterator)) {
+ return 0;
+ } else if (is_nil(context->iterator)) {
+#ifdef DEBUG
+ context->result_cdr = &context->rhs_original;
+#endif
+ context->result = context->lhs_original;
+
+ return 1;
+ }
+
+ return -1;
+}
+
static Eterm append_create_trap_state(Process *p,
ErtsAppendContext *from_context) {
ErtsAppendContext *to_context;
@@ -203,6 +233,11 @@ static BIF_RETTYPE lists_append_onheap(Process *p, ErtsAppendContext *context) {
}
static int append_continue(Process *p, ErtsAppendContext *context) {
+ /* Fast-lane when the rhs is nil: return lhs. */
+ if (is_nil(context->rhs_original)) {
+ return append_empty_rhs(p, context);
+ }
+
/* We build the result on the unused part of the heap if possible to save
* us the trouble of having to figure out the list size. We fall back to
* lists_append_alloc when we run out of space. */
@@ -906,7 +941,12 @@ static int subtract_continue(Process *p, ErtsSubtractContext *context) {
return res;
}
- if (context->lhs_remaining <= SUBTRACT_LHS_THRESHOLD) {
+ /* If the lhs list is empty then there's nothing to do.
+ * Returning early will be taken care of in the `SUBTRACT_STAGE_LEN_RHS`
+ * stage after the rhs list has been scanned. */
+ if (context->lhs_remaining > 0 &&
+ context->lhs_remaining <= SUBTRACT_LHS_THRESHOLD &&
+ is_list(context->rhs_original)) {
return subtract_enter_naive_lhs(p, context);
}
@@ -926,6 +966,20 @@ static int subtract_continue(Process *p, ErtsSubtractContext *context) {
return res;
}
+ /* If the lhs list is empty then the subtraction must return nil. */
+ if (context->lhs_remaining == 0) {
+ ASSERT(is_nil(context->lhs_original));
+ context->result = NIL;
+ return 1;
+ }
+ /* If the rhs list is empty then the subtraction should return the
+ * lhs list unchanged. */
+ if (context->rhs_remaining == 0) {
+ ASSERT(is_nil(context->rhs_original));
+ context->result = context->lhs_original;
+ return 1;
+ }
+
/* We've walked through both lists fully now so we no longer need
* to check for errors past this point. */
--
2.43.0