hst337
Tria - the Elixir optimizer
Tria
An optimizing compiler for Elixir language
Public and alpha 
I’ve made this repository public to finally get some rest and find other developers who may be interested in participating in this project (it sounds so naive when I write it) before getting hands on the second iteration.
Current state
!!!Unstable!!!
Tria passes most of the tests in Phoenix, Ecto, Plug and other projects, while it may fail to compile other projects. You’re welcome to compile projects using Tria and fill an issue if something fails. This kind of help would be heavily appreciated.
Even without considering this, Tria compiler lacks some important features like incremental compilation, warning handling and stuff.
However, you can expect the stable version during 2023-ish
Current features
-
Compile-time evaluation.
Yes, as simple as it sounds, Elixir and Erlang were incapable of doing this, since they support runtime recompilation. Ahead-of-time nature of Tria allowed to evaluate pure statement in compile time -
Enum fusion
Join multipleEnum.maps, optimizeforloops to finally be efficient and other magic.
I haven’t benchmarked these yet, but these optimizations actually work and (more importantly) work correctly in most cases
Planned features
- Broad documentation (like pathex has)
- Inlining
- Hot-reloading support
- Peephole optimizations for common suboptimal code.
map.fieldhandling (did you know that it is 2-3 times slower thanMap.fetch!(map, :field)?)- Type-checker integration
Help wanted
If you’re interested in optimizing compiler development, you’re welcome to take a look around, poke some things, check some stuff. I know that some Brazilian universities are working in Elixir-related compiler development, and I hope we can collaborate on Tria someday
PS: Questions are appreciated
UPD: License is going to change in a near future
Most Liked
josevalim
Exciting to see your ideas come to life!
How do you know which functions are pure? Especially when anonymous functions are involved?
Could you please expand on the tricks here (or share a link)?
I assume some of those may be implementable in Elixir itself, since for is opaque (although it is unlikely we would do Enum fusion).
Curious: how would that be different from Erlang inlining via @compile :inlineand @compile {:inline, size}?
Any ideas here? We do compile map.field to a case statement but, last time I measured, the pattern matching was faster than invoking a BIF instruction (which is what Map.fetch! does). map.fieldshould be slower if the field does not exist though.
Other than that, nice work! I am sure the Erlang Compiler team would also be interested in further ideas to optimize. Some of the peephole and type-checker integration may already be implemented there. Although we don’t perform protocol related type optimizations and the Erlang compiler does not have enough information to do so, so maybe Elixir could/should.
hst337
sabiwara
Not directly although some elements of the discussions have actually been implemented to some extent.
We did move some pure function evaluations to compile-time, see Inline functions in Elixir -> Erlang pass · Issue #13475 · elixir-lang/elixir · GitHub.
So for instance, if you write t = to_timeout(hour: 1, minute: 30), it will be compiled as t = 90000, just as if it was a macro.
Regarding comprehensions and the fact Enum.map was faster than for in some cases as discussed above in the thread.
We did change it so it compiles as Enum.map rather than Enum.reduce when it made sense (PR).
josevalim
What we could do in Elixir itself is to keep a list of the functions in its standard library that are pure. Erlang also keeps a similar list for its standard library and inlines those. WDYT?
We can make this generally applicable but it is worth keeping in mind that each inlined modules becomes a compile-time dependency, and enlarging the compile time graphic can have huge impacts on performance. This is not a concern for Elixir stdlib itself though.
It is also not possible to know if something is pure or not when calling Erlang functions (unless we have an FFI layer where you also explicitly declare those).
Ah, thank you. We had a previous discussion about this. At the time, the cost of traversal in for comparing Enum.reduce vs Enum.map was roughly 13% faster. However, your benchmarks show the body recursive version can be even faster and we could emit Erlang AST with a body recursive version using Erlang’s “named anonymous functions”. Is this something you have an interest in contributing? Or should I open up an issue for someone else to pick?
josevalim
I would argue it is fine for Elixir to work under the assumption its root modules are not being changed. After all, if you replace any Elixir module and change its behavior, then any part of Elixir may break.







