3 min read
How they made WebAssembly function calls faster
The improvements fall into two groups:
- Reducing bookkeeping: This means getting rid of unnecessary work to organize stack frames
- Cutting out intermediaries: This means taking the most direct path between functions
When it does these jumps, it needs to have some information in place, like the place from where it needs to resume. The engine also must separate the frames that it needs. To organize its work, the engine gets a folder and puts this information in it.
This was unnecessarily costly in two ways:
- An unnecessary folder is created which adds up setup and teardown costs
- It requires trampolining through C++ to create the folder and do other setup
To keep track of the types of values, the engine attaches a tag to the value. This turns one simple operation into four operations. This is the reason why WebAssembly expects parameters to be unboxed and doesn’t box its return values. Since it is statically typed, it doesn’t need to add this overhead.
So, before the engine gives the parameters to the WebAssembly function, the engine needs to unbox the values and put them in registers. It has to go through C++ again to prepare the values when going from JS to WebAssembly. Going to this intermediary step is a huge cost, especially for something that’s not that complicated.
Along with these calls, they have also optimized monomorphic and built-in calls.
To understand the optimizations well, check out Lin Clark’s official announcement on Mozilla’s website.