Motoko, in the light of performance

Note: What follows is my very own opinion on a possible improvement direction that Motoko could take. I’d be happy to hear your (as in Motocoders) opinion what functionality you consider performance critical and where you wish to see improvements. Team, feel free to chime in too.

You might have noticed in the last few releases that some pretty performance wins were landed in the areas of memory allocation and switch on variants. These are mostly coming from just picking the low-hanging fruit, and not backed by the bigger initiatives, such as chasing IC features and Motoko trying to be an exemplar citizen in the ecosystem.

Motoko was never considered a high-performance language. It rather started out as an appealing way for beginners to program for the IC. Then some people chimed in, doing cycle measurements comparing it to Rust. To our amazement Motoko often came out as competitive performance-wise. This (in conjunction with the small binaries that we output) places us on the map of high-impact languages for the IC.

Our compilation model currently doesn’t really correlate neighbouring pieces of code and try to spot intelligent simplification opportunities. We have a middle end where such correlations could happen, but only have a very few performance-relevant passes in it.

The unique advantage of a dedicated team developing a small language is that we have high flexibility when choosing runtime data representation and intermediate language. Non-trivial performance wins can come from the engine being tightly coupled to the language and the team being intimately familiar with both.

I have the conviction that idiomatic usage of the language should not be penalised performance-wise, e.g. using pattern matching on tuples and records should be cheaper (or at least comparably costly) as using the .1 and .member accessors. Similarly, using Nats should be cheaper in average than the fixed-bitwidth alternatives with peculiar semantics.

Our team is small, so we need to focus on which blend of feature development and other improvements get our attention. For providing raw performance, neither our expertise nor our energy will suffice. This area we’d like to outsource to generic Wasm optimisers (looking at you wasm-opt, working hard in ic-wasm), as those teams are full of top-notch instruction-level hackers. You using such tools will add some risk (trusted code base) but can give stunning performance increases in return — with the flip of a switch.

So where are the meaningful performance tweaks that the Motoko team could (should) deliver? Since we have access to the inner workings of the compiler, we can optimise and tweak data layouts and interlocked features, so that the generic Wasm optimisers can clean up the resulting output code better. An example for such opportunities is coming from variants with just a few cases. Here we have inside knowledge that strictly less comparisons are necessary in generated code than written by the user. wasm-opt, as a generic optimiser will never know that.

We are actively collecting potential performance tweaks that originate from and depend on Motoko’s semantics and its runtime system to simplify and streamline output, but often don’t know whether such optimisations are useful at all for code in the wild. So we tend to speculate or eyeball our benchmarks. External input would help us. Which are common patterns you employ that compile to subpar code? Which code would you like to fly?

Progress on the performance front will be slow (as it is not our top priority) but steady. Sometime we’ll get there. Especially when you give us guidance.

And — not to forget — we are proud of what we have achieved so far!