Skip to content

Benchmarks

We benchmark the performance of Moshi against other packages for the a typical use case from the README of Unityper. This benchmark has been the baseline benchmark for all the packages that we have compared against.

Results

We see that Moshi is the fastest among all the packages that is almost equivalent to the baseline implementation (~1x comparing to baseline speed) in both allocation size and matching speed.

Benchmark

Setup

The benchmark was run on a machine with the following configuration:

julia> versioninfo()
Julia Version 1.10.4
Commit 48d4fd48430 (2024-06-04 10:41 UTC)
Build Info:
Official https://julialang.org/ release
Platform Info:
OS: macOS (arm64-apple-darwin22.4.0)
CPU: 10 × Apple M1 Pro
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-15.0.7 (ORCJIT, apple-m1)
Threads: 8 default, 0 interactive, 4 GC (on 8 virtual cores)
Environment:
JULIA_EDITOR = code

Benchmarking Code

The benchmarking code uses the same convention and are wrapped in a module. The detailed implementations are as follows:

This is with the normal @match macro.

module MoshiMatchBench

using Random
using Moshi.Data: Data, @data, isa_variant
using Moshi.Match: @match

@data AT begin
    struct A
        common_field::Int = 0
        a::Bool = true
        b::Int = 10
    end
    struct B
        common_field::Int = 0
        a::Int = 1
        b::Float64 = 1.0
        d::Complex = 1 + 1.0im # not isbits
    end
    struct C
        common_field::Int = 0
        b::Float64 = 2.0
        d::Bool = false
        e::Float64 = 3.0
        k::Complex{Real} = 1 + 2im # not isbits
    end
    struct D
        common_field::Int = 0
        b::Any = "hi" # not isbits
    end
end

function generate(len::Int)
    return rand(Random.MersenneTwister(123), (AT.A(), AT.B(), AT.C(), AT.D()), len)
end

function main!(xs)
    @inbounds for i in eachindex(xs)
        xs[i] = @match xs[i] begin
            AT.A(cf, a, b) => AT.B(cf + 1, a, b, b)
            AT.B(cf, a, b, d) => AT.C(cf - 1, b, isodd(a), b, d)
            AT.C(cf) => AT.D(cf + 1, isodd(cf) ? "hi" : "bye")
            AT.D(cf, b) => AT.A(cf - 1, b == "hi", cf)
        end
    end
end

end # module