[m-rev.] for preliminary review: Add option to break trans-opt dependencies.
Peter Wang
novalazy at gmail.com
Wed Dec 21 18:06:47 AEDT 2022
On Mon, 19 Dec 2022 22:40:50 +1100 "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
>
> 2022-12-19 17:48 GMT+11:00 "Peter Wang" <novalazy at gmail.com>:
> > Here is a patch to improve parallel making of .trans_opt files.
> > The naming of things is rather clunky.
>
> My prelim review is attached.
>
> > Here are times for "mmake -j32 trans_opts" in the library directory,
> > using the attached mer_std.make-trans-opt-deps file:
> >
> > - without the new option
> >
> > real 0m39.742s
> > user 2m11.086s
> > sys 0m5.166s
> >
> > - with --make-trans-opt-deps-spec
> >
> > real 0m18.700s
> > user 1m42.337s
> > sys 0m4.331s
>
> That is nice. What do you think is the cause of the
> reductions in user and sys times? Is it just fewer
> .trans_opt files being read, or is there some other factor as well?
That is my guess as well.
> > The information of interest in the mer_std.make-trans-opt-deps file is
> > all in the %-commented out lines.
>
> What is the purpose of the /* */ commented out lines?
>
There wasn't any need to remove outgoing edges from those modules
to end up with an acyclic graph.
> For the bitmap regression and possibly others, we could fix it
> by moving is_error, throw_on_error and their variants, along with
> the types specific to their operations, to a new module. We could
> keep forwarding predicates, marked obsolete, in io.m for now,
> but redirect all references from the Mercury stdlib now.
>
Good idea. Forwarding predicates are not required, though.
is_error, throw_on_error, etc. are not part of the public interface.
> > For a given source module, we want to
> > prevent the compiler reading another module's .trans_opt file.
> > Therefore, I'm thinking the file should state the negative dependencies
> > directly, e.g.
> >
> > foo - {
> > not bar,
> > not baz
> > }
>
> I am not a fan of using generic constructors, such as - and {},
> to encode specific semantics; that's why I have been replacing bools
> in the compiler with purpose-specific types for a while now.
> I would prefer using terms such as
>
> module_allowed_deps(foo, [x, y])
> module_nonallowed_deps(foo, [bar, baz])
>
> In fact, we could allow the deps spec file to contain any entry
> for a module using either (but obviously not both) of these two forms.
>
Ok.
> I would also change the overall deps file syntax from
>
> { entry, entry, ... }
>
> where each entry is one of the above, to just
>
> entry.
> entry.
> ...
>
> This would eliminate the outer {}s which have no real purpose,
> and allow all entries to be written out without having to keep track
> of whether the entry being written out is the last one (since the last gets
> a close brace put after them, not a comma).
>
> This would also get rid of the "something after the one term we expect"
> error type, replacing it with the already existing "malformed entry" error type.
Ok.
> > This is of course similar to my pragma no_trans_opt proposal,
> > only contained in a single file (arguably better).
>
> I think it is better. And supporting the specification of both allowed
> and nonallowed deps satisfies both your preference and mine.
We would want to state the deps of builtin and private_builtin
as "allow" lists anyway, which I hadn't considered.
> > +:- type make_trans_opt_deps_spec == map(module_name, set(module_name)).
> > +
> > + % The --make-trans-opts-deps-spec file shall contain a term of the form:
>
> Why is "make" part of these names? Shouldn't the mmc --make and mmake
> treat .trans_opt files the same?
The "make-trans-opt" part referred to the "--make-trans-opt" option.
The file affects the dependencies when making a .trans_opt file, but not
which .trans_opt files will be read when generating target code.
I will change the option to --trans-opt-deps-spec. Other suggestions are
welcome.
> > + % { PAIRS... }.
> > + %
> > + % where PAIRS is a comma-separated list of pairs:
> > + %
> > + % M - { ALLOWED... }
> > + %
> > + % M is a Mercury module name.
> > + % ALLOWED is a comma-separated list of module names.
>
> Why are the ...s doing in the above? They make it look like
> what is inside each brace is a list of LISTS OF pairs/module names.
>
Deleted.
> > + % Such a pair specifies that for the module M,
> > + % if it has a cyclic dependency on T,
>
> I think this would be simpler described as "if module M and T depend on
> each other, directly or indirectly".
>
> > + % then the edge M -> T is removed from the dependency graph
> > + % for the purposes of making trans-opt files.
>
> Delete this, ...
>
> > + ... in the process of making M.trans_opt,
> > + % the compiler may not read T.trans_opt.
>
> and keep just this.
>
I followed your suggestions.
> > + % To make the file less verbose, `builtin' and `private_builtin' are
> > + % implicitly included in the ALLOWED list unless M is itself
> > + % `builtin' or `private_builtin'.
> > + %
> > +:- pred read_make_trans_opt_deps_spec(string::in,
> > + maybe_error(make_trans_opt_deps_spec)::out, io::di, io::uo) is det.
>
> As above, this name would be better without the "make" part.
> Likewise for the predicates below.
Done.
>
> > +read_make_trans_opt_deps_spec(FileName, Result, !IO) :-
> > + io.read_named_file_as_string(FileName, ReadResult, !IO),
> > + (
> > + ReadResult = ok(Contents),
> > + read_term_from_string(FileName, Contents, _EndPos, ReadTerm),
> > + (
> > + ReadTerm = eof,
> > + Result = error("expected term, got EOF")
> > + ;
> > + ReadTerm = error(Error, LineNum),
> > + string.format("line %d: %s", [i(LineNum), s(Error)], Msg),
> > + Result = error(Msg)
> > + ;
> > + ReadTerm = term(_VarSet, Term),
> > + parse_make_trans_opt_deps_spec(Term, ParseResult, map.init, Spec),
> > + (
> > + ParseResult = ok,
> > + % XXX check for EOF
> > + Result = ok(Spec)
>
> I don't see any point in deferring acting on this XXX; it needs only
> a few lines of code.
>
TODO
> > +parse_make_trans_opt_deps_spec(Term, Result, !Spec) :-
> > + ( if Term = functor(atom("{}"), TermArgs, _Context) then
> > + parse_make_trans_opt_deps_spec_pairs(TermArgs, Result, !Spec)
> > + else
> > + get_term_context(Term) = context(_FileName, LineNum),
> > + string.format("line %d: expected {}", [i(LineNum)], Msg),
> > + Result = error(Msg)
> > + ).
>
> I would include the file name in the error message, in our usual format,
> i.e. "FileName:LineNum: Msg".
TODO
> However, it would be even better if you
> constructed an error_spec. Same for every place below where parsing fails.
>
I will defer this to a later change, once the functionality and file
format are finalised.
> To make this less confusing, you would have to rename the variables
> representing make_trans_opt_deps_specs to something other than "Spec".
> And maybe the type as well. Some name involving "allowed edges/deps",
> perhaps?
>
> > +:- pred parse_make_trans_opt_deps_spec_pairs(list(term)::in, maybe_error::out,
> > + make_trans_opt_deps_spec::in, make_trans_opt_deps_spec::out) is det.
> > +
> > +parse_make_trans_opt_deps_spec_pairs([], ok, !Spec).
> > +parse_make_trans_opt_deps_spec_pairs([Term | Terms], Result, !Spec) :-
> > + parse_make_trans_opt_deps_spec_pair(Term, Result0, !Spec),
> > + (
> > + Result0 = ok,
> > + parse_make_trans_opt_deps_spec_pairs(Terms, Result, !Spec)
> > + ;
> > + Result0 = error(Error),
> > + Result = error(Error)
> > + ).
>
> If you can't parse a pair, it would be better to record an error_spec and
> keep going, to find and diagnose any later problems in one compiler invocation.
>
I will defer this to a later change.
> > +:- pred remove_edges_for_make_trans_opt_deps(make_trans_opt_deps_spec::in,
> > + digraph(module_name)::in, digraph(module_name)::out) is det.
> > +
> > +remove_edges_for_make_trans_opt_deps(Spec, !Graph) :-
> > + SCCs = set.to_sorted_list(digraph.cliques(!.Graph)),
> > + map.foldl(remove_edges_for_make_trans_opt_deps_2(SCCs), Spec, !Graph).
>
> Wouldn't it be easier to
>
> - loop over SCCs, and
> - in each SCC, loop over the modules in that SCC, looking them up in Spec?
Done.
>
> After applying a key-value pair in Spec, you could delete it from Spec,
> which would allow you to find and report any entries in the file that
> talk about nonexistent modules.
>
TODO
> > diff --git a/compiler/options.m b/compiler/options.m
> > index 7d27b7819..def491228 100644
> > --- a/compiler/options.m
> > +++ b/compiler/options.m
> > @@ -387,6 +387,7 @@
> > ; show_developer_type_repns
> > ; show_dependency_graph
> > ; imports_graph
> > + ; make_trans_opt_deps_spec
> > ; dump_trace_counts
> > ; dump_hlds
> > ; dump_hlds_pred_id
> > @@ -1436,6 +1437,7 @@ optdef(oc_aux_output, show_all_type_repns, bool(no)).
> > optdef(oc_aux_output, show_developer_type_repns, bool(no)).
> > optdef(oc_aux_output, show_dependency_graph, bool(no)).
> > optdef(oc_aux_output, imports_graph, bool(no)).
> > +optdef(oc_aux_output, make_trans_opt_deps_spec, maybe_string(no)).
> > optdef(oc_aux_output, dump_trace_counts, accumulating([])).
> > optdef(oc_aux_output, dump_hlds, accumulating([])).
> > optdef(oc_aux_output, dump_hlds_pred_id, accumulating([])).
> > @@ -2388,6 +2390,7 @@ long_option("show-developer-type-repns", show_developer_type_repns).
> > long_option("show-developer-type-representations", show_developer_type_repns).
> > long_option("show-dependency-graph", show_dependency_graph).
> > long_option("imports-graph", imports_graph).
> > +long_option("make-trans-opt-deps-spec", make_trans_opt_deps_spec).
> > long_option("dump-trace-counts", dump_trace_counts).
> > long_option("dump-hlds", dump_hlds).
> > long_option("hlds-dump", dump_hlds).
>
> Again, I would delete "make" from the option name.
>
Done.
> > diff --git a/compiler/write_deps_file.m b/compiler/write_deps_file.m
> > index 6cb5cbcda..d74eb4da6 100644
> > --- a/compiler/write_deps_file.m
> > +++ b/compiler/write_deps_file.m
> > @@ -32,10 +32,11 @@
> > :- type maybe_intermod_deps
> > ---> no_intermod_deps
> > ; intermod_deps(
> > - id_int_deps :: set(module_name),
> > - id_imp_deps :: set(module_name),
> > - id_indirect_deps :: set(module_name),
> > - id_fim_deps :: set(module_name)
> > + id_int_deps :: set(module_name),
> > + id_imp_deps :: set(module_name),
> > + id_indirect_deps :: set(module_name),
> > + id_fim_deps :: set(module_name),
> > + id_make_trans_opt_deps :: set(module_name)
> > ).
>
> And here.
>
"id_make_trans_opt_deps" is only relevant when making a .trans_opt file.
I've added a comment.
I'll resume work on this change next year.
Peter
More information about the reviews
mailing list