File 3746-stdlib-Optimize-fun-usage.patch of Package erlang
From 3346acf491bf33267242f9100bd32da92f090d44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20H=C3=B6gberg?= <john@erlang.org>
Date: Tue, 19 Oct 2021 20:18:51 +0200
Subject: [PATCH 6/6] stdlib: Optimize fun usage
Hoisting the fun check outside these loops lets us skip error
checks on the fun calls themselves, which is very nice for
functions like foldl/3.
---
lib/debugger/test/fun_SUITE.erl | 2 +-
lib/kernel/test/code_SUITE.erl | 19 +++-
lib/stdlib/src/gb_sets.erl | 2 +-
lib/stdlib/src/lists.erl | 195 ++++++++++++++++++++++----------
lib/stdlib/src/sets.erl | 16 ++-
5 files changed, 162 insertions(+), 72 deletions(-)
diff --git a/lib/debugger/test/fun_SUITE.erl b/lib/debugger/test/fun_SUITE.erl
index 7eb53e4ce4..962c54f22a 100644
--- a/lib/debugger/test/fun_SUITE.erl
+++ b/lib/debugger/test/fun_SUITE.erl
@@ -289,7 +289,7 @@ eep37(Config) when is_list(Config) ->
10 = Add(9),
50 = UnusedName(8),
[1,1,2,6,24,120] = lists:map(F, lists:seq(0, 5)),
- {'EXIT',{{badarity,_},_}} = (catch lists:map(fun G() -> G() end, [1])),
+ {'EXIT',{function_clause,_}} = (catch lists:map(fun G() -> G() end, [1])),
{'EXIT',{{badarity,_},_}} = (catch F()),
ok.
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 740f05ac94..5227346217 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -869,13 +869,15 @@ check_funs({'$M_EXPR','$F_EXPR',_},
[{code_server,do_mod_call,4},
{code_server,handle_call,3}|_]) -> 0;
check_funs({'$M_EXPR','$F_EXPR',_},
- [{lists,flatmap,2},
+ [{lists,flatmap_1,2},
+ {lists,flatmap,2},
{lists,concat,1},
{code_server,load_abs,4},
{code_server,handle_call,3},
{code_server,loop,1}|_]) -> 0;
check_funs({'$M_EXPR','$F_EXPR',_},
- [{lists,foreach,2},
+ [{lists,foreach_1,2},
+ {lists,foreach,2},
{code_server,stick_dir,3},
{code_server,handle_call,3},
{code_server,loop,1}|_]) -> 0;
@@ -891,6 +893,19 @@ check_funs({'$M_EXPR','$F_EXPR',1},
{code_server,get_user_lib_dirs,0},
{code_server,init,3},
{code_server,start_link,1}]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',1},
+ [{lists,all_1,2},
+ {lists,all,2},
+ {code_server,is_numstr,1},
+ {code_server,is_vsn,1},
+ {code_server,vsn_to_num,1},
+ {code_server,create_bundle,2},
+ {code_server,choose_bundles,1},
+ {code_server,make_path,2},
+ {code_server,get_user_lib_dirs_1,1},
+ {code_server,get_user_lib_dirs,0},
+ {code_server,init,3},
+ {code_server,start_link,1}]) -> 0;
check_funs({'$M_EXPR','$F_EXPR',1},
[{lists,filter,2},
{code_server,try_archive_subdirs,3}|_]) -> 0;
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index 6d6f7d40ac..8dda0d4ee0 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -871,7 +871,7 @@ is_set(_) -> false.
Set1 :: set(Element),
Set2 :: set(Element).
-filter(F, S) ->
+filter(F, S) when is_function(F, 1) ->
from_ordset([X || X <- to_list(S), F(X)]).
-spec fold(Function, Acc0, Set) -> Acc1 when
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index b82732e0ca..63d55f5630 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -1231,24 +1231,46 @@ rumerge(T1, [H2 | T2]) ->
List :: [T],
T :: term().
-all(Pred, [Hd|Tail]) ->
+all(Pred, List) when is_function(Pred, 1) ->
+ case List of
+ [Hd | Tail] ->
+ case Pred(Hd) of
+ true -> all_1(Pred, Tail);
+ false -> false
+ end;
+ [] -> true
+ end.
+
+all_1(Pred, [Hd | Tail]) ->
case Pred(Hd) of
- true -> all(Pred, Tail);
- false -> false
+ true -> all_1(Pred, Tail);
+ false -> false
end;
-all(Pred, []) when is_function(Pred, 1) -> true.
+all_1(_Pred, []) ->
+ true.
-spec any(Pred, List) -> boolean() when
Pred :: fun((Elem :: T) -> boolean()),
List :: [T],
T :: term().
-any(Pred, [Hd|Tail]) ->
+any(Pred, List) when is_function(Pred, 1) ->
+ case List of
+ [Hd | Tail] ->
+ case Pred(Hd) of
+ true -> true;
+ false -> any_1(Pred, Tail)
+ end;
+ [] -> false
+ end.
+
+any_1(Pred, [Hd | Tail]) ->
case Pred(Hd) of
- true -> true;
- false -> any(Pred, Tail)
+ true -> true;
+ false -> any_1(Pred, Tail)
end;
-any(Pred, []) when is_function(Pred, 1) -> false.
+any_1(_Pred, []) ->
+ false.
-spec map(Fun, List1) -> List2 when
Fun :: fun((A) -> B),
@@ -1257,9 +1279,16 @@ any(Pred, []) when is_function(Pred, 1) -> false.
A :: term(),
B :: term().
-map(F, [H|T]) ->
- [F(H)|map(F, T)];
-map(F, []) when is_function(F, 1) -> [].
+map(F, List) when is_function(F, 1) ->
+ case List of
+ [Hd | Tail] -> [F(Hd) | map_1(F, Tail)];
+ [] -> []
+ end.
+
+map_1(F, [Hd | Tail]) ->
+ [F(Hd) | map_1(F, Tail)];
+map_1(_F, []) ->
+ [].
-spec flatmap(Fun, List1) -> List2 when
Fun :: fun((A) -> [B]),
@@ -1268,9 +1297,13 @@ map(F, []) when is_function(F, 1) -> [].
A :: term(),
B :: term().
-flatmap(F, [Hd|Tail]) ->
- F(Hd) ++ flatmap(F, Tail);
-flatmap(F, []) when is_function(F, 1) -> [].
+flatmap(F, List) when is_function(F, 1) ->
+ flatmap_1(F, List).
+
+flatmap_1(F, [Hd | Tail]) ->
+ F(Hd) ++ flatmap_1(F, Tail);
+flatmap_1(_F, []) ->
+ [].
-spec foldl(Fun, Acc0, List) -> Acc1 when
Fun :: fun((Elem :: T, AccIn) -> AccOut),
@@ -1281,9 +1314,16 @@ flatmap(F, []) when is_function(F, 1) -> [].
List :: [T],
T :: term().
-foldl(F, Accu, [Hd|Tail]) ->
- foldl(F, F(Hd, Accu), Tail);
-foldl(F, Accu, []) when is_function(F, 2) -> Accu.
+foldl(F, Accu, List) when is_function(F, 2) ->
+ case List of
+ [Hd | Tail] -> foldl_1(F, F(Hd, Accu), Tail);
+ [] -> Accu
+ end.
+
+foldl_1(F, Accu, [Hd | Tail]) ->
+ foldl_1(F, F(Hd, Accu), Tail);
+foldl_1(_F, Accu, []) ->
+ Accu.
-spec foldr(Fun, Acc0, List) -> Acc1 when
Fun :: fun((Elem :: T, AccIn) -> AccOut),
@@ -1294,9 +1334,13 @@ foldl(F, Accu, []) when is_function(F, 2) -> Accu.
List :: [T],
T :: term().
-foldr(F, Accu, [Hd|Tail]) ->
- F(Hd, foldr(F, Accu, Tail));
-foldr(F, Accu, []) when is_function(F, 2) -> Accu.
+foldr(F, Accu, List) when is_function(F, 2) ->
+ foldr_1(F, Accu, List).
+
+foldr_1(F, Accu, [Hd | Tail]) ->
+ F(Hd, foldr_1(F, Accu, Tail));
+foldr_1(_F, Accu, []) ->
+ Accu.
-spec filter(Pred, List1) -> List2 when
Pred :: fun((Elem :: T) -> boolean()),
@@ -1317,15 +1361,15 @@ filter(Pred, List) when is_function(Pred, 1) ->
NotSatisfying :: [T],
T :: term().
-partition(Pred, L) ->
- partition(Pred, L, [], []).
+partition(Pred, L) when is_function(Pred, 1) ->
+ partition_1(Pred, L, [], []).
-partition(Pred, [H | T], As, Bs) ->
+partition_1(Pred, [H | T], As, Bs) ->
case Pred(H) of
- true -> partition(Pred, T, [H | As], Bs);
- false -> partition(Pred, T, As, [H | Bs])
+ true -> partition_1(Pred, T, [H | As], Bs);
+ false -> partition_1(Pred, T, As, [H | Bs])
end;
-partition(Pred, [], As, Bs) when is_function(Pred, 1) ->
+partition_1(_Pred, [], As, Bs) ->
{reverse(As), reverse(Bs)}.
-spec filtermap(Fun, List1) -> List2 when
@@ -1335,16 +1379,20 @@ partition(Pred, [], As, Bs) when is_function(Pred, 1) ->
Elem :: term(),
Value :: term().
-filtermap(F, [Hd|Tail]) ->
+filtermap(F, List) when is_function(F, 1) ->
+ filtermap_1(F, List).
+
+filtermap_1(F, [Hd|Tail]) ->
case F(Hd) of
- true ->
- [Hd|filtermap(F, Tail)];
- {true,Val} ->
- [Val|filtermap(F, Tail)];
- false ->
- filtermap(F, Tail)
+ true ->
+ [Hd | filtermap_1(F, Tail)];
+ {true,Val} ->
+ [Val | filtermap_1(F, Tail)];
+ false ->
+ filtermap_1(F, Tail)
end;
-filtermap(F, []) when is_function(F, 1) -> [].
+filtermap_1(_F, []) ->
+ [].
-spec zf(fun((T) -> boolean() | {'true', X}), [T]) -> [(T | X)].
@@ -1356,10 +1404,14 @@ zf(F, L) ->
List :: [T],
T :: term().
-foreach(F, [Hd|Tail]) ->
+foreach(F, List) when is_function(F, 1) ->
+ foreach_1(F, List).
+
+foreach_1(F, [Hd | Tail]) ->
F(Hd),
- foreach(F, Tail);
-foreach(F, []) when is_function(F, 1) -> ok.
+ foreach_1(F, Tail);
+foreach_1(_F, []) ->
+ ok.
-spec mapfoldl(Fun, Acc0, List1) -> {List2, Acc1} when
Fun :: fun((A, AccIn) -> {B, AccOut}),
@@ -1372,11 +1424,15 @@ foreach(F, []) when is_function(F, 1) -> ok.
A :: term(),
B :: term().
-mapfoldl(F, Accu0, [Hd|Tail]) ->
- {R,Accu1} = F(Hd, Accu0),
- {Rs,Accu2} = mapfoldl(F, Accu1, Tail),
- {[R|Rs],Accu2};
-mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}.
+mapfoldl(F, Accu, List) when is_function(F, 2) ->
+ mapfoldl_1(F, Accu, List).
+
+mapfoldl_1(F, Accu0, [Hd | Tail]) ->
+ {R, Accu1} = F(Hd, Accu0),
+ {Rs, Accu2} = mapfoldl_1(F, Accu1, Tail),
+ {[R | Rs], Accu2};
+mapfoldl_1(_F, Accu, []) ->
+ {[], Accu}.
-spec mapfoldr(Fun, Acc0, List1) -> {List2, Acc1} when
Fun :: fun((A, AccIn) -> {B, AccOut}),
@@ -1389,11 +1445,15 @@ mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}.
A :: term(),
B :: term().
-mapfoldr(F, Accu0, [Hd|Tail]) ->
- {Rs,Accu1} = mapfoldr(F, Accu0, Tail),
- {R,Accu2} = F(Hd, Accu1),
- {[R|Rs],Accu2};
-mapfoldr(F, Accu, []) when is_function(F, 2) -> {[],Accu}.
+mapfoldr(F, Accu, List) when is_function(F, 2) ->
+ mapfoldr_1(F, Accu, List).
+
+mapfoldr_1(F, Accu0, [Hd|Tail]) ->
+ {Rs, Accu1} = mapfoldr_1(F, Accu0, Tail),
+ {R, Accu2} = F(Hd, Accu1),
+ {[R | Rs], Accu2};
+mapfoldr_1(_F, Accu, []) ->
+ {[], Accu}.
-spec takewhile(Pred, List1) -> List2 when
Pred :: fun((Elem :: T) -> boolean()),
@@ -1401,12 +1461,16 @@ mapfoldr(F, Accu, []) when is_function(F, 2) -> {[],Accu}.
List2 :: [T],
T :: term().
-takewhile(Pred, [Hd|Tail]) ->
+takewhile(Pred, List) when is_function(Pred, 1) ->
+ takewhile_1(Pred, List).
+
+takewhile_1(Pred, [Hd | Tail]) ->
case Pred(Hd) of
- true -> [Hd|takewhile(Pred, Tail)];
- false -> []
+ true -> [Hd | takewhile_1(Pred, Tail)];
+ false -> []
end;
-takewhile(Pred, []) when is_function(Pred, 1) -> [].
+takewhile_1(_Pred, []) ->
+ [].
-spec dropwhile(Pred, List1) -> List2 when
Pred :: fun((Elem :: T) -> boolean()),
@@ -1414,24 +1478,31 @@ takewhile(Pred, []) when is_function(Pred, 1) -> [].
List2 :: [T],
T :: term().
-dropwhile(Pred, [Hd|Tail]=Rest) ->
+dropwhile(Pred, List) when is_function(Pred, 1) ->
+ dropwhile_1(Pred, List).
+
+dropwhile_1(Pred, [Hd | Tail]=Rest) ->
case Pred(Hd) of
- true -> dropwhile(Pred, Tail);
- false -> Rest
+ true -> dropwhile_1(Pred, Tail);
+ false -> Rest
end;
-dropwhile(Pred, []) when is_function(Pred, 1) -> [].
+dropwhile_1(_Pred, []) ->
+ [].
-spec search(Pred, List) -> {value, Value} | false when
Pred :: fun((T) -> boolean()),
List :: [T],
Value :: T.
-search(Pred, [Hd|Tail]) ->
+search(Pred, List) when is_function(Pred, 1) ->
+ search_1(Pred, List).
+
+search_1(Pred, [Hd | Tail]) ->
case Pred(Hd) of
true -> {value, Hd};
- false -> search(Pred, Tail)
+ false -> search_1(Pred, Tail)
end;
-search(Pred, []) when is_function(Pred, 1) ->
+search_1(_Pred, []) ->
false.
-spec splitwith(Pred, List) -> {List1, List2} when
@@ -1442,14 +1513,14 @@ search(Pred, []) when is_function(Pred, 1) ->
T :: term().
splitwith(Pred, List) when is_function(Pred, 1) ->
- splitwith(Pred, List, []).
+ splitwith_1(Pred, List, []).
-splitwith(Pred, [Hd|Tail], Taken) ->
+splitwith_1(Pred, [Hd|Tail], Taken) ->
case Pred(Hd) of
- true -> splitwith(Pred, Tail, [Hd|Taken]);
+ true -> splitwith_1(Pred, Tail, [Hd|Taken]);
false -> {reverse(Taken), [Hd|Tail]}
end;
-splitwith(Pred, [], Taken) when is_function(Pred, 1) ->
+splitwith_1(_Pred, [], Taken) ->
{reverse(Taken),[]}.
-spec split(N, List1) -> {List2, List3} when
diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl
index 086de0f202..e272b33bf6 100644
--- a/lib/stdlib/src/sets.erl
+++ b/lib/stdlib/src/sets.erl
@@ -266,7 +266,8 @@ is_subset(S1, S2) ->
Acc1 :: Acc,
AccIn :: Acc,
AccOut :: Acc.
-fold(F, Acc, D) -> fold_set(F, Acc, D).
+fold(F, Acc, D) when is_function(F, 2)->
+ fold_set(F, Acc, D).
%% filter(Fun, Set) -> Set.
%% Filter Set with Fun.
@@ -274,7 +275,8 @@ fold(F, Acc, D) -> fold_set(F, Acc, D).
Pred :: fun((Element) -> boolean()),
Set1 :: set(Element),
Set2 :: set(Element).
-filter(F, D) -> filter_set(F, D).
+filter(F, D) when is_function(F, 1)->
+ filter_set(F, D).
%% get_slot(Hashdb, Key) -> Slot.
%% Get the slot. First hash on the new range, if we hit a bucket
@@ -299,7 +301,7 @@ get_bucket(T, Slot) -> get_bucket_s(T#set.segs, Slot).
%% implemented map and hash using fold but these should be faster.
%% We hope!
-fold_set(F, Acc, D) when is_function(F, 2) ->
+fold_set(F, Acc, D) ->
Segs = D#set.segs,
fold_segs(F, Acc, Segs, tuple_size(Segs)).
@@ -316,7 +318,7 @@ fold_bucket(F, Acc, [E|Bkt]) ->
fold_bucket(F, F(E, Acc), Bkt);
fold_bucket(_, Acc, []) -> Acc.
-filter_set(F, D) when is_function(F, 1) ->
+filter_set(F, D) ->
Segs0 = tuple_to_list(D#set.segs),
{Segs1,Fc} = filter_seg_list(F, Segs0, [], 0),
maybe_contract(D#set{segs = list_to_tuple(Segs1)}, Fc).
--
2.31.1