Skip to content

YingboMa/Unityper.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Unityper

Stable Dev Build Status Coverage

Unityper's main capability is to "compactify" structures in static single inheritance. For instance

abstract type AA end
Base.@kwdef struct A2 <: AA
    common_field::Int = 0
    a::Bool = true
    b::Int = 10
end
Base.@kwdef struct B2 <: AA
    common_field::Int = 0
    a::Int = 1
    b::Float64 = 1.0
    d::Complex = 1 + 1.0im # not isbits
end
Base.@kwdef struct C2 <: AA
    common_field::Int = 0
    b::Float64 = 2.0
    d::Bool = false
    e::Float64 = 3.0
    k::Complex{Real} = 1 + 2im # not isbits
end
Base.@kwdef struct D2 <: AA
    common_field::Int = 0
    b::Any = "hi" # not isbits
end

can be compactified by

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

Note that the concrete types A, B, C, and D here are only conceptual, and Unityper compactifies these types into a single AT type. Hence, to check concrete types, ones needs to use Unityper's @compactified macro

foo!(xs) = for i in eachindex(xs)
    @inbounds x = xs[i]
    @inbounds xs[i] = @compactified x::AT begin
        A => D()
        B => A()
        C => B()
        D => A()
    end
end

The above code is equivalent with

goo!(xs) = for i in eachindex(xs)
    @inbounds x = xs[i]
    @inbounds xs[i] = x isa A2 ? D2() :
                      x isa B2 ? A2() :
                      x isa C2 ? B2() :
                      x isa D2 ? A2() : error()
end

Now, let's benchmark these implementations

using Random
rng = Random.MersenneTwister(123)
gs = map(x->rand(rng, (A2(), B2(), C2(), D2())), 1:10000);
rng = Random.MersenneTwister(123)
xs = map(x->rand(rng, (A(), B(), C(), D())), 1:10000);
using BenchmarkTools
@btime foo!($xs);
@btime goo!($gs);

On my laptop, the benchmark result is

julia> @btime foo!($xs);
  58.619 μs (0 allocations: 0 bytes)

julia> @btime goo!($gs);
  116.980 μs (10000 allocations: 312.50 KiB)

Keep in mind that the goo! function is optimal in the sense that it explicitly checks all the sub-types of AA. We can see that Unityper gives a 2x speed up even in the case where the ordinary Julia code is close to optimal.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages