File 2861-erts-Reduce-number-of-list-traversals-in-term_to_bin.patch of Package erlang

From 2c8aafb446fd35d5f9c392ef2789905509f426ff Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Thu, 14 Feb 2019 19:08:49 +0100
Subject: [PATCH 1/2] erts: Reduce number of list traversals in term_to_binary

by patching list length when we reach end of list.

And remove unnecessary traversals in is_string and is_external_string
beyond the max length for STRING_EXT.
---
 erts/emulator/beam/external.c | 62 +++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 32 deletions(-)

diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 9a66e491f3..e87843bf2d 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -102,7 +102,7 @@ static byte* enc_term(ErtsAtomCacheMap *, Eterm, byte*, Uint32, struct erl_off_h
 struct TTBEncodeContext_;
 static int enc_term_int(struct TTBEncodeContext_*,ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Uint32 dflags,
 			struct erl_off_heap_header** off_heap, Sint *reds, byte **res);
-static Uint is_external_string(Eterm obj, int* p_is_string);
+static int is_external_string(Eterm obj, Uint* lenp);
 static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint32);
 static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint32);
 struct B2TContext_t;
@@ -2481,11 +2481,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
 	    {
 		Eterm* cons = list_val(obj);
 		Eterm tl;
+                Uint len_cnt = WSTACK_POP(s);
 
 		obj = CAR(cons);
 		tl = CDR(cons);
-		WSTACK_PUSH2(s, (is_list(tl) ? ENC_ONE_CONS : ENC_TERM),
-			     tl);
+                if (is_list(tl)) {
+                    len_cnt++;
+                    WSTACK_PUSH3(s, len_cnt, ENC_ONE_CONS, tl);
+                }
+                else {
+                    byte* list_lenp = (byte*) WSTACK_POP(s);
+		    ASSERT(list_lenp[-1] == LIST_EXT);
+                    put_int32(len_cnt, list_lenp);
+
+                    WSTACK_PUSH2(s, ENC_TERM, tl);
+                }
 	    }
 	    break;
 	case ENC_PATCH_FUN_SIZE:
@@ -2689,10 +2699,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
 	}
 	case LIST_DEF:
 	    {
-		int is_str;
-
-		i = is_external_string(obj, &is_str);
-		if (is_str) {
+		if (is_external_string(obj, &i)) {
 		    *ep++ = STRING_EXT;
 		    put_int16(i, ep);
 		    ep += 2;
@@ -2703,7 +2710,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
 		    }
 		} else {
 		    *ep++ = LIST_EXT;
-		    put_int32(i, ep);
+                    /* Patch list length when we find end of list */
+                    WSTACK_PUSH2(s, (UWord)ep, 1);
 		    ep += 4;
 		    goto encode_one_cons;
 		}
@@ -2961,9 +2969,13 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
     return 0;
 }
 
+/** @brief Is it a list of bytes not longer than MAX_STRING_LEN?
+ * @param lenp out: string length or number of list cells traversed
+ * @return true/false
+ */
 static
-Uint
-is_external_string(Eterm list, int* p_is_string)
+int
+is_external_string(Eterm list, Uint* lenp)
 {
     Uint len = 0;
 
@@ -2975,29 +2987,15 @@ is_external_string(Eterm list, int* p_is_string)
 	Eterm* consp = list_val(list);
 	Eterm hd = CAR(consp);
 
-	if (!is_byte(hd)) {
-	    break;
+	if (!is_byte(hd) || ++len > MAX_STRING_LEN) {
+	    *lenp = len;
+            return 0;
 	}
-	len++;
 	list = CDR(consp);
     }
 
-    /*
-     * If we have reached the end of the list, and we have
-     * not exceeded the maximum length of a string, this
-     * is a string.
-     */
-    *p_is_string = is_nil(list) && len < MAX_STRING_LEN;
-
-    /*
-     * Continue to calculate the length.
-     */
-    while (is_list(list)) {
-	Eterm* consp = list_val(list);
-	len++;
-	list = CDR(consp);
-    }
-    return len;
+    *lenp = len;
+    return is_nil(list);
 }
 
 
@@ -4166,8 +4164,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
 	    result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) +
 		      4 + 1);
 	    break;
-	case LIST_DEF:
-	    if ((m = is_string(obj)) && (m < MAX_STRING_LEN)) {
+        case LIST_DEF:
+	    if (is_external_string(obj, &m)) {
 		result += m + 2 + 1;
 	    } else {
 		result += 5;
@@ -4317,7 +4315,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
 
         if (is_header(obj)) {
             switch (obj) {
-	    case LIST_TAIL_OP:
+            case LIST_TAIL_OP:
 		obj = (Eterm) WSTACK_POP(s);
 		if (is_list(obj)) {
 		    Eterm* cons = list_val(obj);
-- 
2.16.4

openSUSE Build Service is sponsored by