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

Opaque Mode #1079

Merged
merged 7 commits into from
Aug 2, 2018
Merged

Opaque Mode #1079

merged 7 commits into from
Aug 2, 2018

Conversation

rgrinberg
Copy link
Member

@rgrinberg rgrinberg commented Aug 1, 2018

Implement #1058

For now, this is just a cleaned up version of my WIP. What remains is:

  • Implement better user customization. Env & Stanza level. I'm hoping that resolving [PROPOSAL] Env Defaults #1073 first will address this.

  • Cleaning up some duplication. I have a few hard coded profile = "dev" in some places for example.

  • Choosing good names. I know that @diml has suggested cross_module_inlining, but IMHO the effect that this has on compilation speed isn't all that intuitive for users.

  • Update the documentation

@rgrinberg rgrinberg requested review from emillon and a user August 1, 2018 08:47
@ghost
Copy link

ghost commented Aug 1, 2018

What is the rationale for adding an opaque field in the library stanza? Is it possible that one would want to set in stone that cross module inlining is always disabled for a library?

@rgrinberg
Copy link
Member Author

If the plan is to add the option to env, then I think it makes sense from a consistency point of view to add the option to individual stanzas. I agree with your point from the other thread that anything that can be set in a stanza shouldn't necessarily exist in env. But I think the converse should always hold.

@ghost
Copy link

ghost commented Aug 1, 2018

Alright, I don't have a strong opinion on this question. I'm happy to go ahead with this.

For the name, it feels like it should be something like (compilation_mode <fast_exe|fast_build>). That should be intuitive.

BTW, it feels unnecessarily painful to add such an option through the Lib module, what about we regroup all the options that are used to build the library itself in a single field that is shared between Lib.t and Lib.Info.t? Or alternatively, we can add a field info : Info.t to Lib.t and remove the duplicated fields.

@rgrinberg
Copy link
Member Author

BTW, it feels unnecessarily painful to add such an option through the Lib module, what about we regroup all the options that are used to build the library itself in a single field that is shared between Lib.t and Lib.Info.t? Or alternatively, we can add a field info : Info.t to Lib.t and remove the duplicated fields.

Yeah, that would be fine. I'll try doing this separately first.

@jberdine
Copy link
Contributor

jberdine commented Aug 1, 2018 via email

@rgrinberg rgrinberg mentioned this pull request Aug 1, 2018
@rgrinberg
Copy link
Member Author

seems too general a name for such a specific
compiler flag. The fast_exe vs fast_build distinction is basically the
same as the dev vs release profiles

Well, we might actually put more options under this umbrella name later. In which case, we'd of course need a proper cross_module_inlining option.

This seems like one of those situations where we can't really win with the names. We can have a name that reflects what is the source in the compiler (opaque), a name that reflects what the option will actually do (cross_module_inlining), or a name that will explain to the user why they might want to toggle this option (compilation_mode).

@ghost
Copy link

ghost commented Aug 1, 2018

Note that yet another possibility would be to analyze the compilation flags and act accordingly. For instance if we see -opaque in the list of flags then we set the opaque mode. That would have the advantage that you only need to know the compiler flags and we don't need to invent a new name in Dune. However, it is hard to reliably analyze a command line as we don't know what options take parameters.

If we expose the functionality at the dune level via a library field, it seems to me that we should abstract away from the low-level details.

@@ -233,6 +233,7 @@ include Sub_system.Register_end_point(
~scope
~dir:inline_test_dir
~modules
~opaque:true
Copy link
Member Author

Choose a reason for hiding this comment

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

Not entirely sure that this is correct, but it seems like we'd want the tests to compiler as fast as possible. @diml?

Copy link

Choose a reason for hiding this comment

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

It's not going to make much difference as this context is only used to build the test runner which contains the entry point and so is not used by anything else. Given that we should stick to the default, i.e. opaque=false.

@rgrinberg
Copy link
Member Author

Note that yet another possibility would be to analyze the compilation flags and act accordingly. For instance if we see -opaque in the list of flags then we set the opaque mode. That would have the advantage that you only need to know the compiler flags and we don't need to invent a new name in Dune. However, it is hard to reliably analyze a command line as we don't know what options take parameters.

I'd prefer not to do this as I agree that this isn't as straight forward as it may seem initially. We'd have to do the same trade off that we'd do in menhir and resort to only statically provided options. Im menhir, that seems inevitable, but let's try to avoid this necessary evil here. It would also have the small disadvantage of not being able to disable as easily anymore. Removing something from a set of flags can't be done reliably, and the user would have to set the flags from scratch.

If we expose the functionality at the dune level via a library field, it seems to me that we should abstract away from the low-level details.

As I mentioned before, this would also give us the freedom to add additional meaning to this option in the future.

@emillon
Copy link
Collaborator

emillon commented Aug 1, 2018

If we don't want to analyze the arguments too hard, maybe we do the following:

  • add an option that enables -opaque and does the right thing regarding build rules (I agree that naming is hard)
  • leave flags alone: it is possible for a user to pass -opaque there but they won't have the benefits of the rule improvement
  • add a separate linting rule/command that warns when -opaque is passed directly in flags (we can probably live with false positives & false negatives here)

This approach can also be used if we want to provide a DSL to express warning arguments, for example ((warnings :standard (partial-match :fatal) unused-rec)).

@rgrinberg
Copy link
Member Author

leave flags alone: it is possible for a user to pass -opaque there but they won't have the benefits of the rule improvement

What's the use case for the user ever using -opaque without making use of the better build rules? If there's a valid use case for this then I agree, otherwise if anything we should just error if the user tries to pass this manually.

@@ -197,6 +197,8 @@ module Libs : sig
all the files with extension [ext] of libraries [libs]. *)
val file_deps : t -> Lib.L.t -> ext:string -> Path.t list

val file_deps_with_exts : t -> (Lib.t * string) list -> Path.t list
Copy link
Member Author

Choose a reason for hiding this comment

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

A suggestion for a better name/implementation would be appreciated. I could also implement file_deps on top of this function, but don't see the point of making the code slower for no reason.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The name is not too much of a problem, but I agree that duplicating is not ideal.

Fortunately, you can extract the body of List.map into private function val file_deps_for_lib : Lib.t -> ext:string -> Build_system.t -> Path.t. That way, the two functions file_deps & file_deps_with_exts become one-liners.

@ghost
Copy link

ghost commented Aug 2, 2018

FTR, since dependencies can be specified dynamically, we could take into account -opaque flags that are computed dynamically

@rgrinberg
Copy link
Member Author

FTR, since dependencies can be specified dynamically, we could take into account -opaque flags that are computed dynamically

When we calculate the includes, don't we have to know whether the library is opaque statically?

@ghost
Copy link

ghost commented Aug 2, 2018

I believe the include directories won't change since we still need them for the cmi files

@rgrinberg
Copy link
Member Author

Okay, I see. Btw, I referred to Compilation_context.Includes.t. Which also introduce glob dependencies along with the flags (*.cmi vs *.cmi and .cmx). I suppose we could just change the type of this includes field to introduce dynamic dependencies

@rgrinberg
Copy link
Member Author

Another issue with inferring this property from -opaque is that we might allow users to customize flags per module in the future. It would be much more complicated to support -opaque on a per module basis I think.

@rgrinberg
Copy link
Member Author

@jberdine @emillon @diml I honestly have a hard time seeing the need for a fine grained customization option for this flag btw. I see 2 use cases:

  • You would like to use optimized binaries in development (for benchmarking for example). In this case, I think that a separate benchmarking profile/context would serve the user best.

  • You'd like to ship the unoptimized object files in release because the optimization doesn't do much for example. But then users can't really make use of this fact to speed up their builds, since we don't detect "opacity" for external libs.

In summary, I think the benefit of this option is miniscule.

@ghost
Copy link

ghost commented Aug 2, 2018

I agree. That's why I was surprised it ended up in library stanzas.

@rgrinberg
Copy link
Member Author

rgrinberg commented Aug 2, 2018

I agree. That's why I was surprised it ended up in library stanzas.

Yeah, although I think it's just as marginally useful in the env stanza. My arguments for adding it to the env stanza are purely from a consistency stand point with env. But now that I'm thinking that this option is barely useful on any level customization, we should wait until users demand it before adding it.

Also note that it's no longer in the Library stanza. We could remove this option from the Lib.t, since we can simply infer what it's going to be based on the profile and on whether the library is internal/external. Do you think we should make this simplification?

@ghost
Copy link

ghost commented Aug 2, 2018

I think so yes. It seems better to me if the implementation is close to the high-level design

@rgrinberg
Copy link
Member Author

Okay, I will remove it from Lib.t. Should keep things nice and simple (and not delay the release)

@emillon
Copy link
Collaborator

emillon commented Aug 2, 2018

I agree. The knob we have for fast compilation vs fast binary is the profile so it makes sense to support it only there.

Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
@rgrinberg
Copy link
Member Author

@diml okay, I removed all mentions from opaque in Lib.t

@emillon ready for review. Have a look at SC.file_deps_with_exts. I believe that this is currently the most problematic part.

@emillon
Copy link
Collaborator

emillon commented Aug 2, 2018

Do you have some benchmarks to see if it's actually faster?

@@ -197,6 +197,8 @@ module Libs : sig
all the files with extension [ext] of libraries [libs]. *)
val file_deps : t -> Lib.L.t -> ext:string -> Path.t list

val file_deps_with_exts : t -> (Lib.t * string) list -> Path.t list
Copy link
Collaborator

Choose a reason for hiding this comment

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

The name is not too much of a problem, but I agree that duplicating is not ideal.

Fortunately, you can extract the body of List.map into private function val file_deps_for_lib : Lib.t -> ext:string -> Build_system.t -> Path.t. That way, the two functions file_deps & file_deps_with_exts become one-liners.

@@ -58,13 +58,14 @@ let build_cm cctx ?sandbox ?(dynlink=true) ~dep_graphs ~cm_kind (m : Module.t) =
| Cmi | Cmo -> other_targets
in
let dep_graph = Ml_kind.Dict.get dep_graphs ml_kind in
let opaque = CC.opaque cctx && ctx.version >= (4, 03, 0) in
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that this kind of test should be done in Context, like Context.supports_opaque_option. There's a similar code path for keep_locs, if you agree with that I can extract the relevant functions in a separate PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, feel free to do it post this PR. I don't have a strong preference where the check should be done but I suppose that diml should have a loc b/c I just kept things as is.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, this made me realize that we should turn off the option everywhere altogether if the OCaml version is below 4.03.0. This is not the only place we should be doing this.

@rgrinberg
Copy link
Member Author

Do you have some benchmarks to see if it's actually faster?

Yup. I've noticed an a speed up on on clean builds for cohttp-lwt-unix for example. It's a bit hard to test apples to apples here b/c what we want is to just build a big binary, since that should reduce building non native dependencies. Let me try and setup another little test.

By the way, the certainty in the speed up comes more from observing the rules themselves. There are far less inter cmx dependencies which should result in more paralallization and less recompilation.

Subsequently, use it as a flag when calculating rules and includes

Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
Only use Compilation_context.t for controlling this

Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
@rgrinberg
Copy link
Member Author

@emillon have another look

Copy link
Collaborator

@emillon emillon left a comment

Choose a reason for hiding this comment

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

Great, thanks. Some figures would be nice but as you say it's hard to imagine how it could make things worse.

@emillon
Copy link
Collaborator

emillon commented Aug 2, 2018

Oh and don't forget to update the changelog!

@rgrinberg rgrinberg changed the title Opaque Mode [WIP] Opaque Mode Aug 2, 2018
@rgrinberg
Copy link
Member Author

Here are some numbers:

$ rm -rf _build && time dune-dev build cohttp-lwt-unix.install
dune-dev build cohttp-lwt-unix.install  7.57s user 3.98s system 230% cpu 5.008 total
$ rm -rf _build && time dune-dev build cohttp-lwt-unix.install
dune-dev build cohttp-lwt-unix.install  7.61s user 3.98s system 265% cpu 4.363 total

The second run is with -opaque.

Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
@rgrinberg rgrinberg merged commit 7132e6e into ocaml:master Aug 2, 2018
@rgrinberg rgrinberg deleted the opaque branch August 2, 2018 14:30
rgrinberg added a commit to rgrinberg/opam-repository that referenced this pull request Aug 6, 2018
CHANGES:

- Fix lookup of command line specified files when `--root` is given. Previously,
  passing in `--root` in conjunction with `--workspace` or `--config` would not
  work correctly (ocaml/dune#997, @rgrinberg)

- Add support for customizing env nodes in workspace files. The `env` stanza is
  now allowed in toplevel position in the workspace file, or for individual
  contexts. This feature requires `(dune lang 1.1)` (ocaml/dune#1038, @rgrinberg)

- Add `enabled_if` field for aliases and tests. This field controls whether the
  test will be ran using a boolean expression language. (ocaml/dune#819, @rgrinberg)

- Make `name`, `names` fields optional when a `public_name`, `public_names`
  field is provided. (ocaml/dune#1041, fix ocaml/dune#1000, @rgrinberg)

- Interpret `X` in `--libdir X` as relative to `PREFIX` when `X` is relative
  (ocaml/dune#1072, fix ocaml/dune#1070, @diml)

- Add support for multi directory libraries by writing
  `(include_subdirs unqualified)` (ocaml/dune#1034, @diml)

- Add `(staged_pps ...)` to support staged ppx rewriters such as ones
  using the OCaml typer like `ppx_import` (ocaml/dune#1080, fix ocaml/dune#193, @diml)

- Use `-opaque` in the `dev` profile. This option trades off binary quality for
  compilation speed when compiling .cmx files. (ocaml/dune#1079, fix ocaml/dune#1058, @rgrinberg)

- Fix placeholders in `dune subst` documentation (ocaml/dune#1090, @emillon, thanks
  @trefis for the bug report)

- Add locations to errors when a missing binary in PATH comes from a dune file
  (ocaml/dune#1096, fixes ocaml/dune#1095, @rgrinberg)
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.

3 participants