This Month in Mun - March 2020

April 02, 2020, The Mun Team

A lot of things that we cannot fully control are currently going on in the world. The Mun community and Core Team are trying to make the best of the situation and have once again made great strides; the recently obtained MOSS grant giving us an additional productivity boost!

Community

Our community has made great progress on the integration of the annotate-snippets crate. Hopefully we will be able to merge their PR soon.

We have added new good first issues on Github, so if you want to get involved with Mun please check them out:

v0.2 progress

We still had two major features left on our v0.2 roadmap: garbage collection and struct hot reloading. This month we were able to finish the former and started on foundational work for the latter; with a projected release date of Mun v0.2 by early May:

  • feat(runtime): add marshalling of value structs [PR#93]

    struct(value) Foo {
        x: int,
        y: int
    }
    
    pub fn foo_new(x: int, y: int): Foo {
        Foo { x: x, y: y, }
    }
    
    pub fn foo_add(foo: Foo): int { foo.x + foo.y }
    fn main() {
        let lib_dir = env::args().nth(1).expect("Expected path to a Mun library.");
        let mut runtime = RuntimeBuilder::new(lib_dir)
            .spawn()
            .expect("Failed to spawn Runtime");
    
        // The Mun compiler automatically generates a wrapper for `pub fn`s that
        // contain `struct(value)` arguments or return types, which allocates the
        // marshalled data in the runtime's allocator.
        let mut foo: StructRef = invoke_fn!(runtime, "foo_new", 1i64, 2i64).wait();
        let x = foo.get::<i64>("x").unwrap();
        let y = foo.replace("y", x + 1).unwrap();
        foo.set("x", y).wait();
    }
  • feat: extern functions [PR#96]

    /// This is a function that does not have an implementation in Mun but instead is linked 
    /// dynamically when loading the assembly
    extern fn tick_the_system();
    
    fn main() {
      // The extern functions can be called like any other Mun function
      tick_the_system()
    }
  • feat: object ptr indirection [PR#97]
  • feat: size and alignment in type info [PR#98]
  • feat: garbage collector (defaults to mark&sweep) [PR#99]

Quality Assurance

Our unceasing efforts to improve code coverage have resulted in several PRs focussed on new tests (and consequent fixes) - including our 100th PR!! 🎉

  • Replace grcov with tarpaulin for test coverage [PR#100]
  • fix(code_gen): incremental compilation [PR#101]
  • test(code_gen): add incremental compilation test [PR#102]
  • fix: name of type table global [PR#108]

We are always trying to improve metrics for objectively tracking quality as we progress. We’ve previously talked about how unit and integration tests are a big part of our development process. This month we’ve added performance benchmarks using Criterion, allowing us to do our first optimisations in the Mun Runtime.

Violin plot of an `empty` function call

The violin plot above compares function invocation overhead of embedded languages (Mun, LuaJIT, Wasm) with the raw performance of Rust by invoking an empty function that merely returns the input argument. The Wasmer runtime was used to execute Wasm.

Please note that Rust takes around 675 ps and is thus not visible on the above scale. For clarity, below you can see the respective PDFs of function call times in order of speed (less is better); i.e. for Rust, Mun, Wasm, LuaJIT (from left to right, top to bottom).

PDF of an `empty` function call time using RustPDF of an `empty` function call time using Mun

PDF of an `empty` function call time using Wasm (with wasmer runtime)PDF of an `empty` function call time using LuaJIT

To test arithmetic and logic performance, we needed to minimise the function invocation overhead; enter Fibonacci. The line chart below shows the mean measured time for each language as the input argument increases (100, 200, 500, 1000, 4000, 8000). Even at this early stage, Mun’s dependence on LLVM as compiler backend allows us to achieve performance comparable to Rust.

Line plot of `fibonacci` function calls

All benchmarks were run on an Intel(R) Core(TM) i7-6700HQ CPU @ 2.6 GHz with 16 GB DDR4 RAM.

The benchmark source code is included with the Mun repository. Feel free to give it a try, add additional benchmarks, and share your findings with us on Discord, Twitter, or Github.

Last but not least, some miscellaneous quality of life improvements were also merged:

  • feat: only create symbols for public functions [PR#92]
  • fix: removed the codecov token [PR#94]
  • improvement: bump parking_lot dependency to 0.10 [PR#103]
  • improvement: bump failure dependency to 0.1.7 [PR#107]