-
Notifications
You must be signed in to change notification settings - Fork 3k
optimize beam_trim #10272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
optimize beam_trim #10272
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -24,7 +24,8 @@ | |||||
| -moduledoc false. | ||||||
| -export([module/2]). | ||||||
|
|
||||||
| -import(lists, [any/2,reverse/1,reverse/2,seq/2,sort/1]). | ||||||
| -import(lists, [any/2,reverse/2,seq/2,sort/1,last/1]). | ||||||
| -import(sets, [from_list/1,intersection/2,is_disjoint/2,is_element/2,is_subset/2,union/2,subtract/2,del_element/2,to_list/1]). | ||||||
|
|
||||||
| -include("beam_asm.hrl"). | ||||||
|
|
||||||
|
|
@@ -40,16 +41,21 @@ module({Mod,Exp,Attr,Fs0,Lc}, _Opts) -> | |||||
| Fs = [function(F) || F <- Fs0], | ||||||
| {ok,{Mod,Exp,Attr,Fs,Lc}}. | ||||||
|
|
||||||
| function({function,Name,Arity,CLabel,Is0}) -> | ||||||
| try | ||||||
| St = #st{safe=safe_labels(Is0, []),fsz=0}, | ||||||
| Usage = none, | ||||||
| Is = trim(Is0, Usage, St), | ||||||
| {function,Name,Arity,CLabel,Is} | ||||||
| catch | ||||||
| Class:Error:Stack -> | ||||||
| io:fwrite("Function: ~w/~w\n", [Name,Arity]), | ||||||
| erlang:raise(Class, Error, Stack) | ||||||
| function({function,Name,Arity,CLabel,Is0}=F) -> | ||||||
| case length(Is0) of | ||||||
| N when N < 50 -> | ||||||
| F; | ||||||
| _ -> | ||||||
| try | ||||||
| St = #st{safe=safe_labels(Is0, []),fsz=0}, | ||||||
| Usage = none, | ||||||
| Is = trim(Is0, Usage, St), | ||||||
| {function,Name,Arity,CLabel,Is} | ||||||
| catch | ||||||
| Class:Error:Stack -> | ||||||
| io:fwrite("Function: ~w/~w\n", [Name,Arity]), | ||||||
| erlang:raise(Class, Error, Stack) | ||||||
| end | ||||||
| end. | ||||||
|
|
||||||
| trim([{init_yregs,_}=I|Is], none, St0) -> | ||||||
|
|
@@ -116,7 +122,7 @@ is_not_recursive(_) -> false. | |||||
| %% Y registers. Return the recipe that trims the most. | ||||||
|
|
||||||
| trim_recipe(Layout, IsNotRecursive, {Us,Ns}) -> | ||||||
| UsedRegs = ordsets:union(Us, Ns), | ||||||
| UsedRegs = union(Us, Ns), | ||||||
| Recipes = construct_recipes(Layout, 0, [], []), | ||||||
| NumOrigKills = length([I || {kill,_}=I <- Layout]), | ||||||
| IsTooExpensive = is_too_expensive_fun(IsNotRecursive), | ||||||
|
|
@@ -125,7 +131,8 @@ trim_recipe(Layout, IsNotRecursive, {Us,Ns}) -> | |||||
| not is_too_expensive(R, NumOrigKills, IsTooExpensive)], | ||||||
| case Rs of | ||||||
| [] -> none; | ||||||
| [R|_] -> R | ||||||
| [R] -> R; | ||||||
| [_|_] -> last(Rs) | ||||||
jhogberg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| end. | ||||||
|
|
||||||
| construct_recipes([{kill,{y,Trim0}}|Ks], Trim0, Moves, Acc) -> | ||||||
|
|
@@ -151,15 +158,18 @@ construct_recipes(_, _, _, Acc) -> | |||||
| Acc. | ||||||
|
|
||||||
| take_last_dead(L) -> | ||||||
| take_last_dead_1(reverse(L)). | ||||||
|
|
||||||
| take_last_dead_1([{live,_}|Is]) -> | ||||||
| take_last_dead_1(Is); | ||||||
| take_last_dead_1([{kill,Reg}|Is]) -> | ||||||
| {Reg,reverse(Is)}; | ||||||
| take_last_dead_1([{dead,Reg}|Is]) -> | ||||||
| {Reg,reverse(Is)}; | ||||||
| take_last_dead_1(_) -> none. | ||||||
| take_last_dead_1(L, []). | ||||||
|
|
||||||
| take_last_dead_1([{live,_}|T], Acc) -> | ||||||
| take_last_dead_1(T, Acc); | ||||||
| take_last_dead_1([{kill,Reg}|T], Acc) -> | ||||||
| {Reg,reverse(T, Acc)}; | ||||||
| take_last_dead_1([{dead,Reg}|T], Acc) -> | ||||||
| {Reg,reverse(T, Acc)}; | ||||||
| take_last_dead_1([], _) -> | ||||||
| none; | ||||||
| take_last_dead_1([H|T], Acc) -> | ||||||
| take_last_dead_1(T, [H|Acc]). | ||||||
|
||||||
|
|
||||||
| %% Is trimming too expensive? | ||||||
| is_too_expensive({Ks,_,Moves}, NumOrigKills, IsTooExpensive) -> | ||||||
|
|
@@ -199,13 +209,12 @@ is_too_expensive_fun(false) -> | |||||
| end. | ||||||
|
|
||||||
| is_recipe_viable({_,Trim,Moves}, UsedRegs) -> | ||||||
| Moved = ordsets:from_list([Src || {move,Src,_} <- Moves]), | ||||||
| Illegal = ordsets:from_list([Dst || {move,_,Dst} <- Moves]), | ||||||
| Moved = from_list([Src || {move,Src,_} <- Moves]), | ||||||
| Illegal = from_list([Dst || {move,_,Dst} <- Moves]), | ||||||
| Eliminated = [{y,N} || N <- seq(0, Trim - 1)], | ||||||
| %% All eliminated registers that are also in the used set must be moved. | ||||||
| UsedEliminated = ordsets:intersection(Eliminated, UsedRegs), | ||||||
| case ordsets:is_subset(UsedEliminated, Moved) andalso | ||||||
| ordsets:is_disjoint(Illegal, UsedRegs) of | ||||||
| UsedEliminated = intersection(from_list(Eliminated), UsedRegs), | ||||||
| case is_subset(UsedEliminated, Moved) andalso is_disjoint(Illegal, UsedRegs) of | ||||||
| true -> | ||||||
| UsedEliminated = Moved, %Assertion. | ||||||
|
||||||
| UsedEliminated = Moved, %Assertion. | |
| true = gb_sets:is_equal(UsedEliminated, Moved), %Assertion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: our convention in the compiler is to only import the
listsmodule.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reverted this and used gb_sets, although I notice a diffrence in compile time when importing the relevent functions vs not:
With import
Without import
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also looks like it’s not correct as there is precedent here: https://github.com/erlang/otp/blob/master/lib/compiler/src/v3_core.erl#L93
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some files differ, mainly those that haven't been touched for a very long time. If you have a look at
git blameyou'll see that the related lines were touched some 10 years ago, and some "16 years" which is as far back as the git history goes. In either case the reason we don't want to import is largely because the diffs become very large when any ofsets/gb_sets/ordsetsare used in the same module since they largely share the same interface. They each have their uses.As for with/without import, you probably haven't set up your computer fully for benchmarking, and this is something I also noticed in the forum thread. Modern computers vary their processing speed pretty wildly based on near-term usage, temperature, etc. To get accurate results you need to (at least) disable frequency scaling.