<div dir="ltr">Hey all,<div><br></div><div>thanks a ton for the invaluable pointers. I’m now in the “I-dunno-what-I-am-doing” mode banging SCC annotations like there is no tomorrow, trying to spot any chance for some low-hanging-fruit algorithmic improvement (like using a sequence instead of a list, etc), and will come back to your suggestions as I will face the inevitable dead-end wall :D</div><div><br></div><div>Alfredo</div></div><div class="gmail_extra"><br><div class="gmail_quote">On 10 April 2017 at 01:54, Niklas Hambüchen <span dir="ltr"><<a href="mailto:mail@nh2.me" target="_blank">mail@nh2.me</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I have some suggestions for low hanging fruits in this effort.<br>
<br>
1. Make ghc print more statistics on what it spending time on<br>
<br>
When I did the linking investigation recently<br>
(<a href="https://www.reddit.com/r/haskell/comments/63y43y/liked_linking_3x_faster_with_gold_link_10x_faster/" rel="noreferrer" target="_blank">https://www.reddit.com/r/<wbr>haskell/comments/63y43y/liked_<wbr>linking_3x_faster_with_gold_<wbr>link_10x_faster/</a>)<br>
I noticed (with strace) that there are lots of interesting syscalls<br>
being made that you might not expect. For example, each time TH is used,<br>
shared libraries are loaded, and to determine the shared library paths,<br>
ghc shells out to `gcc --print-file-name`. Each such invocation takes 20<br>
ms on my system, and I have 1000 invocations in my build. That's 20<br>
seconds (out of 2 minutes build time) just asking gcc for paths.<br>
<br>
I recommend that for every call to an external GHC measures how long<br>
that call took, so that it can be asked to print a summary when it's done.<br>
<br>
That might give us lots of interesting things to optimize. For example,<br>
This would have made the long linker times totally obvious.<br>
<br>
At the end, I would love to know for each compilation (both one-shot as<br>
used in ghc's build system, and `ghc --make`):<br>
<br>
* What programs did it invoke and how long did they take<br>
* What files did it read and how long did that take<br>
* How long did it take to read all the `.hi` files in `ghc --make`<br>
* High level time summary (parsing, typechecking, codegen, .hi files, etc)<br>
<br>
That way we'll know at least what is slow, and don't have to resort to<br>
strace every time in order to obtain this basic answer.<br>
<br>
2. Investigate if idiotic syscalls are being done and how much<br>
<br>
There's this concept I call "idiotic syscalls", which are syscalls of<br>
which you know from before that they won't contribute anything<br>
productive. For example, if you give a linker N many `-L` flags (library<br>
dirs) and M many `-l` flags (library names to link), it will try to<br>
`stat()` or `open()` N*M many files, out of which most are total<br>
rubbish, because we typically know what library is in what dir.<br>
Example: You pass `-L/usr/lib/opencv -L/usr/lib/imagemagick<br>
-L/usr/lib/blas -lopencv -limagemagick -lblas`. Then you you will get<br>
things like `open("/usr/lib/opencv/<wbr>libimagemagick.so") = ENOENT` which<br>
makes no sense and obviously that file doesn't exist. This is a problem<br>
with the general "search path" concept; same happens for running<br>
executables searching through $PATH. Yes, nonexistent file opens fail<br>
fast, but in my typical ghc invocation I get millions of them (and we<br>
should at least measure how much time is wasted on them), and they<br>
clutter the strace output and make the real problems harder to investigate.<br>
We should check if we can create ways to give pass those files that do<br>
exist.<br>
<br>
3. Add pure TemplateHaskell<br>
<br>
It is well known that TH is a problem for incremental compilation<br>
because it can have side effects and we must therefore be more<br>
conservative about when to recompile; when you see a `[TH]` in your `ghc<br>
--make` output, it's likely that time again.<br>
<br>
I believe this could be avoided by adding a variant of TH that forbids<br>
the use of the `runIO` function, and can thus not have side effects.<br>
<br>
Most TH does not need side effects, for example any form of code<br>
generation based on other data types (lenses, instances for whatever).<br>
If that was made "pure TH", we would not have to recompile when inputs<br>
to our TH functions change.<br>
<br>
Potentially this could even be determined automatically instead of<br>
adding a new variant of TH like was done for typed TH `$$()`, simply by<br>
inspecting what's in the TH and if we can decide there's no `runIO` in<br>
there, mark it as clean, otherwise as tainted.<br>
<br>
4. Build ghc with `ghc --make` if possible<br>
<br>
This one might be controversial or impossible (others can likely tell<br>
us). Most Haskell code is built with `ghc --make`, not with the one-shot<br>
compilation system + make or Hadrian as as done in GHC's build system.<br>
Weirdly, often `ghc --make` scales much worse and has much worse<br>
incremental recompilation times than the one-shot mode, which doesn't<br>
make sense given that it has no process creation overhead, can do much<br>
better caching etc. I believe that if ghc or large parts of it (e.g.<br>
stage2) itself was built with `--make`, we would magically see --make<br>
become very good, simply we make the right people (GHC devs) suffer<br>
through it daily :D. I expect from this the solution of the `-j`<br>
slowness, GHC overhead reduction, faster interface file loads and so on.<br>
<br>
These are some ideas.<br>
<span class="HOEnZb"><font color="#888888"><br>
Niklas<br>
</font></span></blockquote></div><br></div>