Skip to content
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

[#235] Single pass over forms #261

Merged
merged 2 commits into from
Nov 22, 2019
Merged

[#235] Single pass over forms #261

merged 2 commits into from
Nov 22, 2019

Conversation

jfacorro
Copy link
Contributor

@jfacorro jfacorro commented Nov 21, 2019

Description

Expose functions from els_dodger to be able to make a single pass over all the forms. This improves the performance a bit, although not as much as we would have expected.

Fixes #235.

The following are flame graphs captured by running eflame:apply(els_indexer, index_dir, ["src"]). and then generating the SVG with the following command:

export NAME=stacks; cat $NAME.out | sort | grep -v "<0.349.0>" > "$NAME.out.sort"; _build/debug/lib/eflame/stack_to_flame.sh < $NAME.out.sort > $NAME.svg; open -a Google\ Chrome $NAME.svg

Before

before

After

after

@jfacorro jfacorro requested a review from robertoaloi November 21, 2019 16:14
@@ -34,6 +34,7 @@
%% upstream version:
%% https://github.com/massemanet/redbug/pull/10
, [ {redbug, {git, "https://github.com/robertoaloi/redbug.git", {branch, "relax-beam-lib-error-matching"}}}
, {eflame, {git, "https://github.com/jfacorro/eflame.git", {branch, "various.improvements"}}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to bring in the upstream version, if possible.
What are the included changes?
Are you pull-requesting them to upstream?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the PR with all the improvements.

{'ok', erl_syntax:forms() | none, integer()}
| {'eof', integer()}
| {'error', any(), integer()}.
parse_form(Dev, L0, Parser, _Options) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please use the original naming for the variables? E.g. IoDevice instead of Dev, StartLoc instead of L0, Tokens instead of Ts, etc?

parse_form(Dev, L0, Parser, _Options) ->
case io:scan_erl_form(Dev, "", L0) of
{ok, Ts, L1} ->
case catch {ok, Parser(Ts, undefined)} of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use the deprecated catch. Use try/catch instead.

-spec find_attribute_pois(erl_parse:abstract_form(), [erl_scan:token()]) ->
[poi()].
find_attribute_pois(Form, Tokens) ->
case erl_syntax:type(Form) of
attribute ->
case erl_syntax_lib:analyze_attribute(Form) of
case catch erl_syntax_lib:analyze_attribute(Form) of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Please don't use the old-style catch.

| none, integer()}
| {'eof', integer()}
| {'error', any(), integer()}.
parse_form(Dev, L0, Options) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a re-implementation of els_dodger:parse_form/3. Why not to expose that one, instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was misreading the code. Ignore this comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative option would have been to add a callback function to the Options list which could have been executed from within the els_dodger, and have the parse_form/4 return the final accumulator together with the list of forms.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would have required a change in the implementation of dodger which I was trying to avoid.

),
{ok, Acc}
{error, _IoErr, _L1} = Err -> Err;
{error, _Reason} -> {eof, L0}; % This is probably encoding problem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the (missing an article) comment comes from the dodger, but I would get rid of it and, instead, add a comment to the top of this function explaining this is a simplified version of the respective dodger function.

fun(T, Acc) ->
[do_points_of_interest(T)|Acc]
end, [], Tree)).
FoldFun = fun(T, Acc) -> do_points_of_interest(T) ++ Acc end,
Copy link
Member

@robertoaloi robertoaloi Nov 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the ++ in here? Or can we live with nested lists that we can flatten at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean at the end as in parse/1 or at the end of the fold? If it's the former then I would agree, if it's the latter, I think it's much clearer what's happening if we use the ++.

, [EndLoc, ErrorInfo]
),
{ok, Acc}
{error, _IoErr, _L1} = Err -> Err;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering we are pattern matching on {ok, ...} above, maybe we can avoid the defensive style altogether here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result from this function is processed by the code in els_dodger which is pattern matching and handling each of these results though.

{ok, Ts, L1} ->
case catch {ok, Parser(Ts, undefined)} of
{ok, F} ->
POIs = [find_attribute_pois(F, Ts), points_of_interest(F)],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have the single pass, it probably makes more sense to check here whether we are dealing with an attribute and execute the find_attribute_pois function only in that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check is done in the function itself, adding it here would only make this function harder to read and would bring no benefit IMO.

@jfacorro jfacorro requested a review from robertoaloi November 22, 2019 11:28
@robertoaloi robertoaloi merged commit da9b7c8 into master Nov 22, 2019
@robertoaloi robertoaloi deleted the 235-single-pass branch November 22, 2019 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fork the epp_dodger module
2 participants