As mention I think the results might be misleading. We actually measure only the case of dispatching and not really acting on any “Abstract” data.
I tried the following simple code and it seems that the bottleneck is still there for the general case:
using BenchmarkTools
using Random
using GLMakie
abstract type MyAbstractType end
struct MyInt1 <: MyAbstractType
x::Int
end
struct MyInt2 <: MyAbstractType
x::Int
end
struct MyInt3 <: MyAbstractType
x::Int
end
struct MyInt4 <: MyAbstractType
x::Int
end
struct MyInt5 <: MyAbstractType
x::Int
end
struct MyInt6 <: MyAbstractType
x::Int
end
struct MyInt7 <: MyAbstractType
x::Int
end
struct OtherInt4
x::Int
end
function mysum(mv::Vector{T}) where T
sum([v.x for v in mv])
end
bab = Vector{Float64}()
bany = Vector{Float64}()
bus = Vector{Float64}()
vsref = fill(MyInt1(1), 1000);
# bvs = mean(@benchmark(mysum(vsref))).time
vsab = Vector{MyAbstractType}(fill(MyInt1(1), 1000));
push!(bab , mean(@benchmark(mysum(vsab))).time)
vsany = Vector{MyAbstractType}(fill(MyInt1(1), 1000));
push!(bany , mean(@benchmark(mysum(vsany))).time)
vsus = Vector{Union{MyInt1}}(fill(MyInt1(1), 1000));
push!(bus , mean(@benchmark(mysum(vsus))).time)
# 2 types
#
vs2ab = Vector{MyAbstractType}(vcat(fill(MyInt1(1), 500),fill(MyInt2(1), 500)));
push!(bab , mean(@benchmark(mysum(vsany))).time)
vs2any = Vector{Any}(vcat(fill(MyInt1(1), 500),fill(MyInt2(1), 500)));
push!(bany , mean(@benchmark(mysum(vs2any))).time)
vs2us = Vector{Union{MyInt1, MyInt2}}(vcat(fill(MyInt1(1), 500),fill(MyInt2(1), 500)));
push!(bus, mean(@benchmark(mysum(vs2us))).time)
# 3 types
#
vs3ab = Vector{MyAbstractType}(vcat(fill(MyInt1(1), 334),fill(MyInt2(1), 333), fill(MyInt3(1), 333)));
push!(bab , mean(@benchmark(mysum(vs3any))).time)
vs3any = Vector{Any}(vcat(fill(MyInt1(1), 334),fill(MyInt2(1), 333), fill(MyInt3(1), 333)));
push!(bany , mean(@benchmark(mysum(vs3any))).time)
vs3us = Vector{Union{MyInt1, MyInt2, MyInt3}}(vcat(fill(MyInt1(1), 334),fill(MyInt2(1), 333), fill(MyInt3(1), 333)));
push!(bus , mean(@benchmark(mysum(vs3us))).time)
# 4 types
#
vs4ab = Vector{MyAbstractType}(vcat(fill(MyInt1(1), 250),fill(MyInt2(1), 250), fill(MyInt3(1), 250), fill(MyInt4(1), 250)));
push!(bab , mean(@benchmark(mysum(vs4any))).time)
vs4any = Vector{Any}(vcat(fill(MyInt1(1), 250),fill(MyInt2(1), 250), fill(MyInt3(1), 250), fill(MyInt4(1), 250)));
push!(bany , mean(@benchmark(mysum(vs4any))).time)
vs4us = Vector{Union{MyInt1, MyInt2, MyInt3, MyInt4}}(vcat(fill(MyInt1(1), 250),fill(MyInt2(1), 250), fill(MyInt3(1), 250), fill(MyInt4(1), 250)));
push!(bus , mean(@benchmark(mysum(vs4us))).time)
# 5 types
#
vs5ab = Vector{MyAbstractType}(vcat(fill(MyInt1(1), 200),fill(MyInt2(1), 200), fill(MyInt3(1), 200), fill(MyInt4(1), 200), fill(MyInt5(1), 200)));
push!(bab , mean(@benchmark(mysum(vs4any))).time)
vs5any = Vector{Any}(vcat(fill(MyInt1(1), 200),fill(MyInt2(1), 200), fill(MyInt3(1), 200), fill(MyInt4(1), 200), fill(MyInt5(1), 200)));
push!(bany , mean(@benchmark(mysum(vs5any))).time)
vs5us = Vector{Union{MyInt1, MyInt2, MyInt3, MyInt4, MyInt5}}(vcat(fill(MyInt1(1), 200),fill(MyInt2(1), 200), fill(MyInt3(1), 200), fill(MyInt5(1), 200), fill(MyInt4(1), 200)));
push!(bus , mean(@benchmark(mysum(vs5us))).time)
# 6 types
#
vs6ab = Vector{MyAbstractType}(vcat(fill(MyInt1(1), 170),fill(MyInt2(1), 166), fill(MyInt3(1), 166), fill(MyInt4(1), 166), fill(MyInt5(1), 166), fill(MyInt6(1), 166)));
push!(bab , mean(@benchmark(mysum(vs6ab))).time)
vs6any = Vector{Any}(vcat(fill(MyInt1(1), 170),fill(MyInt2(1), 166), fill(MyInt3(1), 166), fill(MyInt4(1), 166), fill(MyInt5(1), 166), fill(MyInt6(1), 166)));
push!(bany , mean(@benchmark(mysum(vs6any))).time)
vs6us = Vector{Union{MyInt1, MyInt2, MyInt3, MyInt4, MyInt5, MyInt6}}(vcat(fill(MyInt1(1), 170),fill(MyInt2(1), 166), fill(MyInt3(1), 166), fill(MyInt4(1), 166), fill(MyInt5(1), 166), fill(MyInt6(1), 166)));
push!(bus , mean(@benchmark(mysum(vs6us))).time)
# Plot
# 7 types
#
vs7ab = Vector{MyAbstractType}(vcat(fill(MyInt1(1), 148),fill(MyInt2(1), 142), fill(MyInt3(1), 142), fill(MyInt4(1), 142), fill(MyInt5(1), 142), fill(MyInt6(1), 142), fill(MyInt7(1), 142)));
push!(bab , mean(@benchmark(mysum(vs7ab))).time)
vs7any = Vector{Any}(vcat(fill(MyInt1(1), 148),fill(MyInt2(1), 142), fill(MyInt3(1), 142), fill(MyInt4(1), 142), fill(MyInt5(1), 142), fill(MyInt6(1), 142), fill(MyInt7(1), 142)));
push!(bany , mean(@benchmark(mysum(vs7any))).time)
vs7us = Vector{Union{MyInt1, MyInt2, MyInt3, MyInt4, MyInt5, MyInt6, MyInt7}}(vcat(fill(MyInt1(1), 148),fill(MyInt2(1), 142), fill(MyInt3(1), 142), fill(MyInt4(1), 142), fill(MyInt5(1), 142), fill(MyInt6(1), 142), fill(MyInt7(1), 142)));
push!(bus , mean(@benchmark(mysum(vs7us))).time)
colors=[:blue,:green,:yellow]
f,a,p = barplot(repeat(1:length(bab), outer=3),
vcat(bab, bany,bus),
dodge= repeat(1:3, inner=length(bab)),
color= repeat(1:3, inner=length(bab)),
axis = (title="Summation of struct fields",
xticks = (1:length(bab), repr.(1:length(bab))),
ylabel="nanosecs",
xlabel="types contained in vector"))
axislegend(a, [PolyElement(polycolor = colors[i]) for i in 1:3],
["Vector{Abstract}","Vector{Any}", "Vector{Union{...}}"],
position = :lt, orientation = :vertical)
So basically just adding together the fields of different structs belonging to the same abstract type.
Getting the following results which speak for themselves:
The red line is the reference, i.e. time for concrete single type vector.