Are there idioms in Julia for fast Algebraic Data Types (ADT)?

I ran the following benchmark to compare the cost of doing dispatch on closed unions with the cost of doing dispatch on abstract types:

using BenchmarkTools

abstract type SignedInteger end

struct Pos <: SignedInteger
    abs :: UInt64
end

struct Neg <: SignedInteger
    abs :: UInt64
end

const AnySignedInteger = Union{Pos, Neg}

const posvec = [Pos(i) for i in 1:100]
const negvec = [Neg(i) for i in 1:100]

value(x::Pos) = Int64(x.abs)
value(x::Neg) = -Int64(x.abs)

function test_open()
    return sum(value(x) for x in SignedInteger[posvec; negvec])
end

function test_closed()
    return sum(value(x) for x in AnySignedInteger[posvec; negvec])
end

println("Testing open version")
@btime test_open()
println("Testing closed version")
@btime test_closed()

The result:

Testing open version
  1.792 μs (202 allocations: 4.92 KiB)
Testing closed version
  697.020 ns (2 allocations: 2.02 KiB)

Conclusion: it is about 2.3x faster to do dispatch on a closed union type. I actually expected more of a difference, which makes me think that my naive solution would actually not be prohibitively slow compared to something smarter.