Hacker News new | past | comments | ask | show | jobs | submit login
Implement BeamAsm – A JIT for Erlang/OTP (github.com/erlang)
233 points by di4na on Sept 11, 2020 | hide | past | favorite | 32 comments



This is amazing. Huge props to Lukas Larsson and the OTP team!

Some background on the journey to BeamJIT: https://drive.google.com/file/d/1hHCs90kDX_wJ9AbLzGNu5bZKdIV...


So this is not quite beamjit, but a sort of rewrite?


I understood it as a completely new, simplified approach that Lukas Larsson started beginning of the year.

It's not a tracing or optimising JIT, but instead at load time translates the BEAM opcodes to native code using asmjit.


I love how much thought and polish went into this, just like so many other Erlang features in recent years: counters, persistent_term, thread progress, ETS scaling, process groups...

There's been talks of JIT for quite a few years, but they didn't want to merge it in until they had something that's easy to maintain, and doesn't introduce significant performance regressions. The end result looks really solid.


What do you mean by "thread progress"?



So this JIT supports everything but HiPE and will be included in OTP-24 and is production ready(???) and dramatically improves performance across almost everything? Seems too good to be true. What has been the bug finding process so far in this PR?


This is far from the first JIT effort for BEAM. I'm sure the authors have rolled many years of learnings and experience into this one. Still, you make a good point, it will need a lot of testing by the community before I'd trust it in production.


Probably a case of the low hanging fruit. With most projects you start off with a bunch of improvements with very few side effects - at present - but quickly start to encounter trade offs. Both in the form of new improvements introducing more development overhead, and the fact that open ended things are generally hard to make fast.

To make something faster may mean calcifying some part of the architecture; this code handles every scenario that the design defines now, but it can’t handle other scenarios we might have wanted to consider later. Those are now harder to implement or counter to our assumptions. People don’t like it when you take back things you previously gave them, meaning no new feature or a huge opportunity cost for that feature (because it’s reimplementing other features as well as adding a new one).


I don’t think this is at all true... I mean the PR changes 350-odd files and adds some crazy JIT engine. It’s hardly low hanging fruit... I don’t know enough about how this is implemented to say it will make future features harder, they even state in the PR maintenance is very important to the design here so I’ll assume it won’t be a weight around their necks.


Very exciting.

Also Facebook open-sourcing its static typed Erlang prototype in November.


It seems like 2021 is going to be a big year for BEAM languages.


Any idea how they handled static typing for messages? As I remember from previous conversations, that was an issue.


I may be missing some context but can't it be done similar to how we handle data from outside world in TS ?

A runtime type guard that asserts that the shape of data conforms to a particular type and from that point forward compiler will trust the guard and assume that the message is of particular type.

In a dynamically typed world, I doubt one could do much better.


The thing is; messages for Erlang are far more part of your programming/architecture than in other languages. You are basically writing 'nanoservices' all the time. I saw quite a lot of people mentioning it in discussions, including here, for instance [0].

[0] https://news.ycombinator.com/item?id=22573535


I only read the slides so I'm not sure but they mention gen_servers so I guess there will be something about messages.


I'm soooooo excited about this.

Question: how does performance compare to HiPE. I see reference to improvements over the Interpreter but what about performance vs HiPE?


Can someone break this down and explain what this means in practical runtime performance gains?

I know it's probably "it depends", but in Elixir let's say you were doing a lot of string parsing or mucking around with a bunch of Enum functions or doing a bit of math. Will this put Elixir's performance on par with at least Python and Ruby in those cases?

I know this feature is meant for Erlang but I'm assuming it applies back to Elixir too?


It does apply to Elixir. It will help all around performance but more so it does this on a per-instruction level by reducing dispatch cost in that the interpreter pays (as well as managing to specialize things a little better than what the fixed instruction tables can express).

To get an idea of the instruction stream of the BEAM (not the same as .beam asm), you can use the erts_debug module:

    iex> :erts_debug.df(String)
This will dump a BEAM machine instruction stream to a file named Elixir.String.dis in your current working directory. You'll see things like:

    000000001B81AFB0: i_func_info_IaaI 0 `'Elixir.String'` `at` 2 
    000000001B81AFD8: is_integer_fx f(000000001B81AFB0) x(1) 
    000000001B81AFE8: is_ge_literal_fxc f(000000001B81B008) x(1) `0` 
    000000001B81B000: i_call_only_f loc(`'Elixir.String'`:`do_at`/2) 
    000000001B81B008: allocate_tt 2 2 
    000000001B81B010: move_window2_xxy x(1) x(0) y(0) 
    000000001B81B018: i_call_f loc(`'Elixir.String'`:`length`/1) 
    000000001B81B020: i_plus_xyjd x(0) y(0) j(0) x(0) 
    000000001B81B030: is_ge_literal_fxc f(000000001B81B060) x(0) `0` 
    000000001B81B048: move_shift_yxx y(1) x(0) x(1) 
    000000001B81B050: i_call_last_fQ loc(`'Elixir.String'`:`do_at`/2) 2 
    000000001B81B060: move_deallocate_return_cQ `nil` 2
Each of those instructions are what the .beam file loader currently generates. With the JIT, these will be replaced by machine code.


I wish they'd kept it C-only instead of introducing C++ (plenty of runtime code generation libraries written in C).

There is something to be said about internal consistency and for a codebase as established as this, C++ is more of a detriment than a gain.


I imagine developers as experienced as the creators of this evaluated the C options and decided C++ was in fact not more of a detriment than a gain.

It has its problems but blindly being anti C++ is silly.


Hardly blindly.

It's been a C codebase so far. Introducing C++ makes this no longer the case. This has strong implications for both development and deployment. I'm perplexed since there exist battle-tested code generation engines written in C (e.g. dynasm). Correct me if I'm wrong but it doesn't look like asmjit is anything special in that regard.

I can't imagine Joe Armstrong being happy about this.


Dynasm depends on Lua for pre-processing into pure C. I guess you could write an Erlang version? There's a Ruby meta-assembler in Safari JavaScriptCore!


Dynasm can be compiled with minilua.c (a single file), which can simply be embedded into the Erlang build.

https://corsix.github.io/dynasm-doc/tutorial.html


"We also considered using dynasm, but found the tooling that comes with asmjit to be better."

https://github.com/erlang/otp/pull/2745#issuecomment-6914821...


Already exists, written by none other than Virding himself: https://github.com/rvirding/luerl


He might roll over in his grave...

I do agree with you though.


AsmJit was designed to be able to integrate well with C code bases. It uses C-style error handling (no exceptions) and provides easy to use API. I don't think it's a big deal to use it in a C project - there are other C projects that use AsmJit without any issues.


They clearly thought there was some benefit to doing it with C++. I will say I generally agree with you on the consistency point.

Then again I spent none of my time working on this project so I refuse to criticize the result. That would be obnoxious.


Introducing C++ doesn't mean they have to allow every pull request to use whichever C++ features it wants to.


Given that all major C compilers are now written in C++, this is yet another prof where the world is going.

Using C++ doesn't mean one needs to use everything from it, and improving C while keeping its semantics will hardly lead to anything much different from what C++ already offers.


C++ generally compiles orders of magnitude slower, has far worse interop and platform support and makes it really easy to unwittingly add all sorts of horrendous bloat. I'd personally still rather write code in C++ than C, but I agree that Joe Armstrong would probably be horrified, and for good reasons.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: