Momo · Get Back Some Compile Time From Monomorphization

18 May 2019

Monomorphization has one problem (apart from being a ridiculous word that I’ll probably spell wrong every time): It generates rather a lot of code, bloating binary size and potentially pessimizing execution cache usage. Often, generics aren’t really needed for speed, but for ergonomics: Library code might want to present an easy-to-use generic interface that will automate some conversions. However, this often means that almost each user gets their own version of the code, leading to the aforementioned bloat (case in point: Earlier clap versions were notorious for adding hundreds of kilobytes to the binary size – for a simple command line parser).

The canonical solution is to factor out an inner method that contains all of the code minus the generic conversions, and leave the outer method as a shell. For example:

pub fn this < I : Into < String >> ( i : I ) -> usize { // do something really complicated with `i.into()` // potentially spanning multiple pages of code .. }

becomes

#[inline] pub fn this < I : Into < String >> ( i : I ) -> usize { _ this_inner ( i .into ()) } fn _ this_inner ( i : String ) -> usize { // same code as above without the conversion .. }

This ensures only the conversion gets monomorphized, leading to leaner code and compile-time performance wins. The downside is that this burdens the code with accidential complexity, because readers have to follow the indirection. Can we do better?

How about this:

use momo :: momo ; #[momo] pub fn this < I : Into < String >> ( i : I ) -> usize { // do something really complicated with `i.into()` // potentially spanning multiple pages of code .. }

This does the whole inner function dance for you, letting you concentrate on your code while enjoying speedy compiles without getting in the way. momo does handle three conversions:

Into<T> : the inner function receives a plain T

: the inner function receives a plain AsRef<T> : the inner function receives a &T

: the inner function receives a AsMut<T> : the inner function receives a &mut T

Note that momo for now works only on plain functions. Methods are on the roadmap, however. Also we may be able to allow further conversions in the future. For example, I’d like to add TryFrom which would use .try_from()? to bail early in the outer conversion method, but I wanted to get a working prototype out as soon as possible.

PS.: The name ‘Momo’ is taken from the protagonist of the eponymous Michael Ende book – a girl that fights (by mere existence) against the “gray misters” who try to take time away from her peers. It’s also a wordplay on “MOnoMOrphization”.