-
Notifications
You must be signed in to change notification settings - Fork 25
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
Question about Compilation Time #110
Comments
🤔 Compilation time being affected this badly is not something that is expected nor intended. A potential solution would then be to add an option to TypeCheck to not inline certain kinds of checks. But before looking into this, we first need to find out whether this indeed is the problem. One thing you could try, is to compare the difference in compile times between enabling/disabling runtime checks in the various modules, to see whether the problem indeed originates from the runtime checks being generated (but I'm quite certain that it does). One more thing you could try, just to test the difference in compile-time, is to see how fast/slow compilation is with version 0.9.x of the codebase (vs the current, 0.10.4 version): Because we want to support type-checking passed-in functions, a number of the type-checks that are generated were rewritten to more complex forms in 0.10.x (and I have not gotten around to switch between the two based on whether there actually is a function value inside). And besides this, I'll try to come up with a minimal example as a separate public repo, so it's easier to profile and reason about. As for the general strategy w.r.t. type-checking 'large' structs: I think that 'check them fully everywhere' is a good default. Being able to override it is however important for performance (moreso runtime performance, but as we see now, likely compile-time as well). For many structs in our codebases we can (up to a point) guarantee that they are only constructed by e.g. a
Even nicer might be to perform checks based on the opaqueness of a type's typespec. Unfortunately, in practice this leads into a mire where Dialyzer and Elixir's defaults don't work well together as checking whether a value even is a particular struct is strictly speaking not allowed on opaque types. (c.f. elixir-lang/elixir#8463). Some discussion about this also happened in the past in the ElixirForum topic, but it was not the only thing we talked about, so it might be a difficult to look through the conversation. |
Which Elixir and OTP versions are you using by the way? |
Apologies for the delay (CodeBEAM travel, etc.). I was on OTP 24.3.3 with Elixir 1.12.3 for the original test. Haven't yet tested the items you mentioned, but will put it in my mental queue. |
I created a smaller test case repository at aj-foster/type_check_example. With it, here are some test results:
All of these tests were run on Elixir 1.13.3 and OTP 24.3.2. |
Thank you for this example! I can reproduce the slow compilation times on my machine. A 'simple' thing that I will spend some time on in the near future, is to re-introduce the optimization to the checking-code that version |
Leaving this here for myself to continue debugging later. Clearly, the
results in an Erlang source file that is 310MB large. 😲 |
@aj-foster Could you re-run your benchmarks on the new master branch (on any commit post 2e0cc95), as well as on the |
Here's another round of tests, first on latest The main module in the example is egregious in terms of the density of its type specifications, but probably normal in terms of the total number of references to the struct types. While the 10 seconds would add up across multiple such modules, I'd have no problem turning it on, for example, in CI while running tests. Less problematic modules could easily stay on in all environments.
All of these tests were run on Elixir 1.13.3 and OTP 24.3.2. |
Hello there!
I enjoyed your talk at the Amsterdam Elixir meetup, and decided to give TypeCheck a try on our codebase. We have a few structs with a relatively large number of fields, and their old
@type t
definitions fully specified each field. Upon changing these to@type!
and implementing a few@spec!
s on functions that used them, we quickly found that our compilation time rose to 3–4 minutes.For reference, the rough setup is:
With six function specifications on module
C
that reference all three types in various combinations.So, the question: is this expected? I can imagine that the sheer number of fields on each struct, especially when nested, creates an explosion of code generation. In the documentation you mention:
Could this be true for compile time as well?
More generally, do you have any recommendations on strategies for type-checking structs? Perhaps the full specification of each field would make sense on a constructor function, but the canonical
t
type should stop at%__MODULE__{}
with no fields.Thanks for your work! Excited for more people to see it at ElixirConf EU.
The text was updated successfully, but these errors were encountered: