hst337
Access optimizations from Hissssst's compiler
Funny thing
is that both map.key and map[:key] are examples of bad decisions in the language design:
The main problem is that the difference between these two ways of accessing a value in map is not just in a
map.keyworks even whenmapis an atom !!!
This is introduced with the ability of language to call functions without parentheses. And this ability actually introduces runtime penalty! So, if we take a look at the erlang term format, we’ll see thatmap.keyis actually compiled to
case map do
map when is_atom(map) ->
apply(map, :key, [])
%{^key => value} ->
value
... ->
raise KeyError
end
Which handles this extremely rare situation where we actually make a call without parentheses (by my analysis in all of the Ecto, Phoenix, Plug and EctoSQL it happens only once!!!) in the most common construct in the whole language (well, after the function call)! And it actually introduces performance penalties
-
While
map.keyis working with structs,map[:key]is not. And it is because Access has is a behaviour instead of a protocol (which was a good decision in the beginning, but just needed some polishing in the compiler). -
map[:key]is polymorphic, since it also works with keywords and everything Access-able, whilemap.keyworks with structs and maps. -
map.keyis the same asmap.key()and vice versa, parentheses syntax means nothing here. Live with it
The right way to do this
Just use pattern-matching. It is the most powerful and efficient construct in the language. For nested data use Pathex. And if you already have a codebase where everything is dotted, just use optimizing compiler called Tria.
Trending in Discussions
Other Trending Topics
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #performance










First 10 of 25 Posts
cmo
Come on mate. You can’t tell people to “just” use your experimental compiler instead of vanilla Elixir. That is not the “right way” to deal with a code base where the dots are used.
I think we’re safer just to pattern match ourselves or borrow some code from Membrane.
hst337
Why do you think so?
cmo
Risk. Given the choice between a) a minor performance penalty; b) writing code using pattern matchine style; or c) replacing the language compiler with one written by one bloke whose claims about the Elixir compiler and their own replacement are at times disproven and is not battled hardened, you’d be hard pressed to find many people advising option c. The time investment and risk is unlikely to be worth the cost.
hst337
Which makes key access 2-3 times slower
That’s cool, but it would require refactoring a lot of existing code, which sometimes is not that obvious.
I’d like to hear some real examples about my claims about compilation which were disproven.
That’s true, but it is true for every new technology.
Why is that? I mean, compiler is not 100% ready yet, but is in the state which shows that my ideas around performance optimization are working. It just requires some polishing. As soon as it will be ready, there will be no risks, my aim is to provide as-much-as-possible compatibility with existing compiler.
And I think that it is totally fine to advertise not-yet-ready solution, because it will provide some battle-testing by early adopters (one of whom is a member of already mentioned Membrane project), some useful feedback, and might even help me get some money for my work
sodapopcan
I guess since it’s a compiler so long as it compiles there is no risk, or does it produce code that could hit runtime exceptions?
That’s an interesting point, I never thought about that! I do feel you would almost have to be trying to write code that would ever hit this scenario, but it sure would be a subtle bug: You happened to pass in an atom instead of a map that also happened to be an erlang module that also happens to have a zero-arity function with the same name as the key you’re trying to access.
dimitarvp
And here I am, never using
map[:key]ormap.keyat all…hst337
Every compiler does.
sodapopcan
Sorry, terribly worded. I guess I’m asking how different it is from Elixir aside from the optimizations listed. I can always just look at the code.
hst337
There are several types of things that can be different
Unexpected. I am human, I make mistakes, so I tried to invest a lot of time into making debugging of the compiler as simple as possible.
Expected. And I’ve divided those into four parts:
2.1 Compatible. And I am trying to make most of the optimizations compatible as much as possible. So that is compatibility in terms of side-effects ordering, in terms of exceptions, and of course in terms of results returned by functions
2.2 Incompatible, but invisible. This means that my compiler generates different code from what vanilla compiler does, but nobody actually relies on this behaviour or this behaviour changes in Elixir from time to time. For example, my compiler slightly changes module compilation order, but it doesn’t affect anything, unless the user is relying on unspecified compiler behaviour (like reading
.beamfiles during compilation to affect the result).2.3 Incompatible, but with workarounds. This is runtime recompilation with appup or relup (
recompilein iex is compatible). I will provide a different interface to these problems, in case anybody will ever be using runtime recompilation in prod with Elixir.2.4 Incompatible, but which require Elixir patching. And there’s the only one of them, it is exception formatting. Tria regroups functions into modules, but not in a way a developer originally wrote. So function
Module.function/2can actually becomeTriaGenerated.Module__function/2. And stacktrace would have this ugly signature instead of the original one. I am just going to introduce the patch to the Elixir and make sure that it is accepted in the upstream, which will allow exceptions formatting.Plus, there is also this tiny case of Incompatible, but useful when my compiler detects errors that regular Elixir compiler wouldn’t. This is extremely rare and can be found only in cases like
benwilson512
Hey folks I moved these posts out of the other thread because we’re now squarely into a discussion about the readiness or not of @hst337’s compiler.
Thanks!