When I compile my application, I see a lot of messages like:
Compiling some/module/file.ex (it’s taking more than 10s)
I understand that this message is appearing because the compilation of those modules is slow, but I don’t know why is this and where to start looking for possible causes. Any suggestions?
Modules are known to take long when you have a lot of macrocalls or functions with a lot of heads. Before 1.5.somewhat compile time of a function grew squared to its number of heads. I’m not sure when this was fixed (or if at all, maybe fix is master only)
Another reason for slow compilation is if given module depends on other slow modules. [1] Is a great resource for understanding compilation. In general, you want to avoid compile-time dependencies between modules as much as possible.
On our Phoenix app, switching from import MyApp.Router.Helpers to alias MyApp.Router.Helpers, as: Routes speed up compilation and limited re-compilations greatly, see: [2]
Elixir 1.6 will ship with improvements to mix xref which should make it easier to investigate this stuff.
Hm, we may have something here
Running mix xref graph --source on those slow modules usually shows the router as a compile time dependency, which in turn depends on lots of controllers, which depend on models, etc.
I am going to try aliasing the router module instead of importing it and check the result.
Thank you @wojtekmach
I know this is an old thread but I came across it, because I also got the it's taking more than 10s message during compilation. In my case I do know exactly the reason for that long compilation time and there is nothing really I can do about it (I did try a lot of things to eliminate it, but it wasn’t possible).
Thus, I can either just accept the message or try to disable that (meaningful) warning. I didn’t find any real answer on how to disable the warning. And yes, you should first try to fix your dependencies as mentioned above, but if you might want to disable it. Hence the reason for my comment and to share what I found out
By going through the elixir code I could figure out that there is actually a compiler setting (:long_compilation_threshold) that can disable it (or be adjusted). In your mix.exs you just add the following line into your project/0 function:
def project do
[
...
elixirc_options: [long_compilation_threshold: 60_000],
...
]
end
This removed it for me . Don’t ask me as from which version this flag is available, thus, it might not work if you are on an old Elixir version.
If you want to see where I used it, feel free to play around with my repo, you can find it here:
you can help the compiler by making it one function head with embedded case statement:
def fun (a, b) do
case b do
:foo -> :alpha
:bar -> :beta
end
Routex was not able to build helper functions for 400 (generated) routes causing “too complex” issues during compilation due to the amount of generated function heads. In the upcoming version 1.2 those are rewritten to case statement variants and it now handles more than 2.000 routes.
I would say this is a great optimisation if you are generating the code with metaprogramming.
Otherwise I would almost always recommend to take the most readable approach. There was a similar discussion on similar topics such as how map.key is very inefficient compared to pattern-matching.
This is a implementation detail that can be changed anytime in the future and should not be a concern of the end-user that is the developer.
It can happen and I’ve worked in a codebase where we had such files that had many function heads, maintained by hand.
The thing is that, while most of those function heads were just returning constants, at some point, a new set of features introduced processing. Doing that kind of thing using case is much more inconvenient from readability standpoint.
Yes, this works for those cases where you don’t mind too much about the function signature, but in my case I do want to provide an easier/more convenient interface for the user. It’s just so much nicer to write:
leds(20) |> red |> green |> blue
Than to write the alternative (even though my library does support both):
At the end it’s the libraries decision. I didn’t want to start a discussion on what the right way is (and yes, there are many reasons to not hide issues by using this trick), but only that, if you want to get rid of the warning, you could do so with the compile flag.
Yeah, and I wonder if this is an issue in the Elixir compiler, since a reasonable simple recursive function shouldn’t take this long for the compiler to unroll it self.