Skip to content

Commit

Permalink
compiler: Limit depth of sharing state tree
Browse files Browse the repository at this point in the history
With OTP 27.0 the xmerl tests showed a markedly increased run-time.
Profiling pointed to beam_ssa_ss:prune/2 as the culprit. It turns out
that if there are long value derivation chains which are extended one
element at a time (such as in the ss_depth_limit.erl test-case in this
patch), the effective time complexity of prune will be O(n^2) where n
is the number of elements in the chain.

By limiting the depth of the sharing state tree, we can avoid the
drastically increased run-times for the alias analysis. This is a
stop-gap fix until the alias analysis can be improved to avoid this
pit-fall.
  • Loading branch information
frej committed Jun 12, 2024
1 parent 98fa137 commit c63d259
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 13 deletions.
20 changes: 14 additions & 6 deletions lib/compiler/src/beam_ssa_alias.erl
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,20 @@
opt(StMap0, FuncDb0) ->
%% Ignore functions which are not in the function db (never
%% called).
Funs = [ F || F <- maps:keys(StMap0), is_map_key(F, FuncDb0)],
StMap1 = #{ F=>expand_record_update(OptSt) || F:=OptSt <- StMap0},
KillsMap = killsets(Funs, StMap1),
{StMap2, FuncDb} = aa(Funs, KillsMap, StMap1, FuncDb0),
StMap = #{ F=>restore_update_record(OptSt) || F:=OptSt <- StMap2},
{StMap, FuncDb}.
try
Funs = [ F || F <- maps:keys(StMap0), is_map_key(F, FuncDb0)],
StMap1 = #{ F=>expand_record_update(OptSt) || F:=OptSt <- StMap0},
KillsMap = killsets(Funs, StMap1),
{StMap2, FuncDb} = aa(Funs, KillsMap, StMap1, FuncDb0),
StMap = #{ F=>restore_update_record(OptSt) || F:=OptSt <- StMap2},
{StMap, FuncDb}
catch
throw:too_deep ->
%% Give up and leave the module unmodified. Thrown by
%% beam_ssa_ss:prune/2 when the depth of sharing state
%% value chains exceeds a fixed limit.
{StMap0,FuncDb0}
end.

%%%
%%% Calculate the set of variables killed at each instruction. The
Expand Down
21 changes: 15 additions & 6 deletions lib/compiler/src/beam_ssa_ss.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
-import(lists, [foldl/3]).

-define(ARGS_DEPTH_LIMIT, 4).
-define(SS_DEPTH_LIMIT, 250).

%% -define(DEBUG, true).

Expand Down Expand Up @@ -412,28 +413,36 @@ accumulate_edges(V, State, Edges0) ->
new() ->
beam_digraph:new().

%%%
%%% Throws `too_deep` if the depth of sharing state value chains
%%% exceeds SS_DEPTH_LIMIT.
%%%
-spec prune(sets:set(beam_ssa:b_var()), sharing_state()) -> sharing_state().
prune(LiveVars, State) ->
?assert_state(State),
?DP("Pruning to ~p~n", [sets:to_list(LiveVars)]),
?DBG(dump(State)),
R = prune(sets:to_list(LiveVars), [], new(), State),
R = prune([{0,V} || V <- sets:to_list(LiveVars)], [], new(), State),
?DP("Pruned result~n"),
?DBG(dump(R)),
?assert_state(R).

prune([V|Wanted], Edges, New0, Old) ->
prune([{Depth0,V}|Wanted], Edges, New0, Old) ->
case beam_digraph:has_vertex(New0, V) of
true ->
%% This variable is alread added.
%% This variable is already added.
prune(Wanted, Edges, New0, Old);
false ->
false when Depth0 < ?SS_DEPTH_LIMIT ->
%% This variable has to be kept. Add it to the new graph.
New = add_vertex(New0, V, beam_digraph:vertex(Old, V)),
%% Add all incoming edges to this node.
InEdges = beam_digraph:in_edges(Old, V),
InNodes = [From || {From,_,_} <- InEdges],
prune(InNodes ++ Wanted, InEdges ++ Edges, New, Old)
Depth = Depth0 + 1,
InNodes = [{Depth, From} || {From,_,_} <- InEdges],
prune(InNodes ++ Wanted, InEdges ++ Edges, New, Old);
false ->
%% We're in too deep, give up.
throw(too_deep)
end;
prune([], Edges, New0, _Old) ->
foldl(fun({Src,Dst,Lbl}, New) ->
Expand Down
3 changes: 2 additions & 1 deletion lib/compiler/test/beam_ssa_check_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ end_per_group(_GroupName, Config) ->
alias_checks(Config) when is_list(Config) ->
run_post_ssa_opt(alias, Config),
run_post_ssa_opt(alias_non_convergence, Config),
run_post_ssa_opt(alias_chain, Config).
run_post_ssa_opt(alias_chain, Config),
run_post_ssa_opt(ss_depth_limit, Config).

annotation_checks(Config) when is_list(Config) ->
run_post_ssa_opt(annotations, Config).
Expand Down
283 changes: 283 additions & 0 deletions lib/compiler/test/beam_ssa_check_SUITE_data/ss_depth_limit.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
%%%
%%% Check that the limit imposed on the depth of sharing state value
%%% chains is imposed.
%%%

-module(ss_depth_limit).

-export([f/0]).

-if(false).
%% f/0 is generated by the following program. N should be
%% SS_DEPTH_LIMIT + 1 (from beam_ssa_ss.erl).
do(N) ->
Data = [
"-module(ss_depth_limit).\n\n",
"-export([f/0]).\n\n",
"f() ->\n",
"%ssa% fail () when post_ssa_opt ->\n"
"%ssa% ret(X) { unique => [X] }.\n"
" X0 = e:f(),\n",
[io_lib:format(" X~p = {X~p,e:f()},~n", [X, X-1])
|| X<- lists:seq(1, N)],
io_lib:format(" X~p.~n", [N])
],
file:write_file("ss_depth_limit.erl", Data).
-endif.

f() ->
%ssa% fail () when post_ssa_opt ->
%ssa% ret(X) { unique => [X] }.
X0 = e:f(),
X1 = {X0,e:f()},
X2 = {X1,e:f()},
X3 = {X2,e:f()},
X4 = {X3,e:f()},
X5 = {X4,e:f()},
X6 = {X5,e:f()},
X7 = {X6,e:f()},
X8 = {X7,e:f()},
X9 = {X8,e:f()},
X10 = {X9,e:f()},
X11 = {X10,e:f()},
X12 = {X11,e:f()},
X13 = {X12,e:f()},
X14 = {X13,e:f()},
X15 = {X14,e:f()},
X16 = {X15,e:f()},
X17 = {X16,e:f()},
X18 = {X17,e:f()},
X19 = {X18,e:f()},
X20 = {X19,e:f()},
X21 = {X20,e:f()},
X22 = {X21,e:f()},
X23 = {X22,e:f()},
X24 = {X23,e:f()},
X25 = {X24,e:f()},
X26 = {X25,e:f()},
X27 = {X26,e:f()},
X28 = {X27,e:f()},
X29 = {X28,e:f()},
X30 = {X29,e:f()},
X31 = {X30,e:f()},
X32 = {X31,e:f()},
X33 = {X32,e:f()},
X34 = {X33,e:f()},
X35 = {X34,e:f()},
X36 = {X35,e:f()},
X37 = {X36,e:f()},
X38 = {X37,e:f()},
X39 = {X38,e:f()},
X40 = {X39,e:f()},
X41 = {X40,e:f()},
X42 = {X41,e:f()},
X43 = {X42,e:f()},
X44 = {X43,e:f()},
X45 = {X44,e:f()},
X46 = {X45,e:f()},
X47 = {X46,e:f()},
X48 = {X47,e:f()},
X49 = {X48,e:f()},
X50 = {X49,e:f()},
X51 = {X50,e:f()},
X52 = {X51,e:f()},
X53 = {X52,e:f()},
X54 = {X53,e:f()},
X55 = {X54,e:f()},
X56 = {X55,e:f()},
X57 = {X56,e:f()},
X58 = {X57,e:f()},
X59 = {X58,e:f()},
X60 = {X59,e:f()},
X61 = {X60,e:f()},
X62 = {X61,e:f()},
X63 = {X62,e:f()},
X64 = {X63,e:f()},
X65 = {X64,e:f()},
X66 = {X65,e:f()},
X67 = {X66,e:f()},
X68 = {X67,e:f()},
X69 = {X68,e:f()},
X70 = {X69,e:f()},
X71 = {X70,e:f()},
X72 = {X71,e:f()},
X73 = {X72,e:f()},
X74 = {X73,e:f()},
X75 = {X74,e:f()},
X76 = {X75,e:f()},
X77 = {X76,e:f()},
X78 = {X77,e:f()},
X79 = {X78,e:f()},
X80 = {X79,e:f()},
X81 = {X80,e:f()},
X82 = {X81,e:f()},
X83 = {X82,e:f()},
X84 = {X83,e:f()},
X85 = {X84,e:f()},
X86 = {X85,e:f()},
X87 = {X86,e:f()},
X88 = {X87,e:f()},
X89 = {X88,e:f()},
X90 = {X89,e:f()},
X91 = {X90,e:f()},
X92 = {X91,e:f()},
X93 = {X92,e:f()},
X94 = {X93,e:f()},
X95 = {X94,e:f()},
X96 = {X95,e:f()},
X97 = {X96,e:f()},
X98 = {X97,e:f()},
X99 = {X98,e:f()},
X100 = {X99,e:f()},
X101 = {X100,e:f()},
X102 = {X101,e:f()},
X103 = {X102,e:f()},
X104 = {X103,e:f()},
X105 = {X104,e:f()},
X106 = {X105,e:f()},
X107 = {X106,e:f()},
X108 = {X107,e:f()},
X109 = {X108,e:f()},
X110 = {X109,e:f()},
X111 = {X110,e:f()},
X112 = {X111,e:f()},
X113 = {X112,e:f()},
X114 = {X113,e:f()},
X115 = {X114,e:f()},
X116 = {X115,e:f()},
X117 = {X116,e:f()},
X118 = {X117,e:f()},
X119 = {X118,e:f()},
X120 = {X119,e:f()},
X121 = {X120,e:f()},
X122 = {X121,e:f()},
X123 = {X122,e:f()},
X124 = {X123,e:f()},
X125 = {X124,e:f()},
X126 = {X125,e:f()},
X127 = {X126,e:f()},
X128 = {X127,e:f()},
X129 = {X128,e:f()},
X130 = {X129,e:f()},
X131 = {X130,e:f()},
X132 = {X131,e:f()},
X133 = {X132,e:f()},
X134 = {X133,e:f()},
X135 = {X134,e:f()},
X136 = {X135,e:f()},
X137 = {X136,e:f()},
X138 = {X137,e:f()},
X139 = {X138,e:f()},
X140 = {X139,e:f()},
X141 = {X140,e:f()},
X142 = {X141,e:f()},
X143 = {X142,e:f()},
X144 = {X143,e:f()},
X145 = {X144,e:f()},
X146 = {X145,e:f()},
X147 = {X146,e:f()},
X148 = {X147,e:f()},
X149 = {X148,e:f()},
X150 = {X149,e:f()},
X151 = {X150,e:f()},
X152 = {X151,e:f()},
X153 = {X152,e:f()},
X154 = {X153,e:f()},
X155 = {X154,e:f()},
X156 = {X155,e:f()},
X157 = {X156,e:f()},
X158 = {X157,e:f()},
X159 = {X158,e:f()},
X160 = {X159,e:f()},
X161 = {X160,e:f()},
X162 = {X161,e:f()},
X163 = {X162,e:f()},
X164 = {X163,e:f()},
X165 = {X164,e:f()},
X166 = {X165,e:f()},
X167 = {X166,e:f()},
X168 = {X167,e:f()},
X169 = {X168,e:f()},
X170 = {X169,e:f()},
X171 = {X170,e:f()},
X172 = {X171,e:f()},
X173 = {X172,e:f()},
X174 = {X173,e:f()},
X175 = {X174,e:f()},
X176 = {X175,e:f()},
X177 = {X176,e:f()},
X178 = {X177,e:f()},
X179 = {X178,e:f()},
X180 = {X179,e:f()},
X181 = {X180,e:f()},
X182 = {X181,e:f()},
X183 = {X182,e:f()},
X184 = {X183,e:f()},
X185 = {X184,e:f()},
X186 = {X185,e:f()},
X187 = {X186,e:f()},
X188 = {X187,e:f()},
X189 = {X188,e:f()},
X190 = {X189,e:f()},
X191 = {X190,e:f()},
X192 = {X191,e:f()},
X193 = {X192,e:f()},
X194 = {X193,e:f()},
X195 = {X194,e:f()},
X196 = {X195,e:f()},
X197 = {X196,e:f()},
X198 = {X197,e:f()},
X199 = {X198,e:f()},
X200 = {X199,e:f()},
X201 = {X200,e:f()},
X202 = {X201,e:f()},
X203 = {X202,e:f()},
X204 = {X203,e:f()},
X205 = {X204,e:f()},
X206 = {X205,e:f()},
X207 = {X206,e:f()},
X208 = {X207,e:f()},
X209 = {X208,e:f()},
X210 = {X209,e:f()},
X211 = {X210,e:f()},
X212 = {X211,e:f()},
X213 = {X212,e:f()},
X214 = {X213,e:f()},
X215 = {X214,e:f()},
X216 = {X215,e:f()},
X217 = {X216,e:f()},
X218 = {X217,e:f()},
X219 = {X218,e:f()},
X220 = {X219,e:f()},
X221 = {X220,e:f()},
X222 = {X221,e:f()},
X223 = {X222,e:f()},
X224 = {X223,e:f()},
X225 = {X224,e:f()},
X226 = {X225,e:f()},
X227 = {X226,e:f()},
X228 = {X227,e:f()},
X229 = {X228,e:f()},
X230 = {X229,e:f()},
X231 = {X230,e:f()},
X232 = {X231,e:f()},
X233 = {X232,e:f()},
X234 = {X233,e:f()},
X235 = {X234,e:f()},
X236 = {X235,e:f()},
X237 = {X236,e:f()},
X238 = {X237,e:f()},
X239 = {X238,e:f()},
X240 = {X239,e:f()},
X241 = {X240,e:f()},
X242 = {X241,e:f()},
X243 = {X242,e:f()},
X244 = {X243,e:f()},
X245 = {X244,e:f()},
X246 = {X245,e:f()},
X247 = {X246,e:f()},
X248 = {X247,e:f()},
X249 = {X248,e:f()},
X250 = {X249,e:f()},
X251 = {X250,e:f()},
X251.

0 comments on commit c63d259

Please sign in to comment.