diff options
author | Björn-Egil Dahlberg <[email protected]> | 2016-04-13 16:23:34 +0200 |
---|---|---|
committer | Björn-Egil Dahlberg <[email protected]> | 2016-04-13 16:23:34 +0200 |
commit | 5fb93f69a6a0cb99d3ebdead3f2549e01a6d6aa8 (patch) | |
tree | 2c7b7229c51a9e721b792eda3a012fd5107fe337 | |
parent | ea6c780391c8bc51fa04ee2e4892bdfd49d5623e (diff) | |
parent | c56c6d36c3060e62925f036ad020da1753e48829 (diff) | |
download | otp-5fb93f69a6a0cb99d3ebdead3f2549e01a6d6aa8.tar.gz otp-5fb93f69a6a0cb99d3ebdead3f2549e01a6d6aa8.tar.bz2 otp-5fb93f69a6a0cb99d3ebdead3f2549e01a6d6aa8.zip |
Merge branch 'egil/erts/opt-list_append/OTP-13487'
* egil/erts/opt-list_append/OTP-13487:
erts: Optimize '++' operator
-rw-r--r-- | erts/emulator/beam/erl_bif_lists.c | 93 |
1 files changed, 77 insertions, 16 deletions
diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index fcccb5a4a0..73d327da3e 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -40,32 +40,93 @@ static BIF_RETTYPE append(Process* p, Eterm A, Eterm B) Eterm list; Eterm copy; Eterm last; - size_t need; - Eterm* hp; + Eterm* hp = NULL; Sint i; - if ((i = erts_list_length(A)) < 0) { - BIF_ERROR(p, BADARG); + list = A; + + if (is_nil(list)) { + BIF_RET(B); } - if (i == 0) { - BIF_RET(B); - } else if (is_nil(B)) { - BIF_RET(A); + + if (is_not_list(list)) { + BIF_ERROR(p, BADARG); } - need = 2*i; - hp = HAlloc(p, need); - list = A; + /* optimistic append on heap first */ + + if ((i = HeapWordsLeft(p) / 2) < 4) { + goto list_tail; + } + + hp = HEAP_TOP(p); copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2)); list = CDR(list_val(list)); - hp += 2; + hp += 2; + i -= 2; /* don't use the last 2 words (extra i--;) */ + + while(i-- && is_list(list)) { + Eterm* listp = list_val(list); + last = CONS(hp, CAR(listp), make_list(hp+2)); + list = CDR(listp); + hp += 2; + } + + /* A is proper and B is NIL return A as-is, don't update HTOP */ + + if (is_nil(list) && is_nil(B)) { + BIF_RET(A); + } + + if (is_nil(list)) { + HEAP_TOP(p) = hp; + CDR(list_val(last)) = B; + BIF_RET(copy); + } + +list_tail: + + if ((i = erts_list_length(list)) < 0) { + BIF_ERROR(p, BADARG); + } + + /* remaining list was proper and B is NIL */ + if (is_nil(B)) { + BIF_RET(A); + } + + if (hp) { + /* Note: fall through case, already written + * on the heap. + * The last 2 words of the heap is not written yet + */ + Eterm *hp_save = hp; + ASSERT(i != 0); + HEAP_TOP(p) = hp + 2; + if (i == 1) { + hp[0] = CAR(list_val(list)); + hp[1] = B; + BIF_RET(copy); + } + hp = HAlloc(p, 2*(i - 1)); + last = CONS(hp_save, CAR(list_val(list)), make_list(hp)); + } else { + hp = HAlloc(p, 2*i); + copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2)); + hp += 2; + } + + list = CDR(list_val(list)); i--; + + ASSERT(i > -1); while(i--) { - Eterm* listp = list_val(list); - last = CONS(hp, CAR(listp), make_list(hp+2)); - list = CDR(listp); - hp += 2; + Eterm* listp = list_val(list); + last = CONS(hp, CAR(listp), make_list(hp+2)); + list = CDR(listp); + hp += 2; } + CDR(list_val(last)) = B; BIF_RET(copy); } |