Iβm happy to announce MixedStructTypes.jl which allows to compactify multiple mutable and immutable types in a single one with a similar syntax to the one of structs. They work identical to structs for many operations because many Base
functions are implemented to match the interface for normal structs. The point of working with a unique type instead of many is to avoid dynamic dispatch and abstract containers which have big performance hits.
Two different macros are available, having different memory and speed performance characteristics. The macro based on SumTypes.jl is also a bit more general because it allows to mix mutable and immutable structs where fields belonging to different structs can also have different types. Nonetheless, both already can contain parametric types and allow default values for fields. I think that the fact that the syntax is so similar to the one of structs should help integrate this package into other ones.
An example of usage and a little performance comparison between the two macros are already present in the ReadMe.
Letβs also explore here how do these two macros compare with the one from Unityper.jl. While these macros have also more features than Unityper because they allow for parametric mutable and immutable structs, while Unityper only allows for non-parametric immutable structs, they are also good performance-wise, to show that I repeat the benchmark on the ReadMe but with fewer types for the sake of brevity:
using MixedStructTypes, Unityper, BenchmarkTools
@compactify begin
@abstract struct AT end
struct A <: AT
a::Int = 1
end
struct B <: AT
a::Int = 2
b::Complex = 1 + 1im
end
end
@compact_struct_type @kwdef CT begin
struct C
a::Int = 1
end
struct D
a::Int = 2
b::Complex = 1 + 1im
end
end
@sum_struct_type @kwdef ET begin
struct E
a::Int = 1
end
struct F
a::Int = 2
b::Complex = 1 + 1im
end
end
vec_a = AT[rand((A,B))() for _ in 1:10^6];
vec_c = CT[rand((C,D))() for _ in 1:10^6];
vec_e = ET[rand((E,F))() for _ in 1:10^6];
We look both to time and memory:
julia> @btime sum(x.a for x in $vec_a);
937.911 ΞΌs (0 allocations: 0 bytes)
julia> @btime sum(x.a for x in $vec_c);
715.359 ΞΌs (0 allocations: 0 bytes)
julia> @btime sum(x.a for x in $vec_e);
3.936 ms (0 allocations: 0 bytes)
julia> Base.summarysize(vec_a)
35791632
julia> Base.summarysize(vec_c)
35487008
julia> Base.summarysize(vec_e)
12820240
As you can see, in this very simple (and so not too informative) benchmark @compact_sum_type
is both faster and nearly as memory efficient, while @sum_struct_type
is much more memory efficient that the other two macros.
For those interested, the package is already available in the general registry. Issues and PRs are really welcomed