I think we should probably only compare the Union{Bool,Missing} story with anything else on julia 0.7. Arrays of small unions were never meant to be fast on 0.6, so the advice there is pretty much simply don’t use them on 0.6 if you need speed.
Having said that, here is my attempt to speed up the BitVector version:
using BenchmarkTools
using Missings
n = 100_000
bool_data = rand(Bool, n)
bool_missing = rand(Bool, n);
bitvector_data = BitVector(bool_data);
bitvector_missing = BitVector(bool_missing)
data_union = Union{Bool,Missing}[i[2] ? missing : i[1] for i in zip(bool_data, bool_missing)]
function count_vector_bool(A::Vector{Bool}, B::Vector{Bool})
t,m = 0, 0
for i in 1:length(A)
B[i] && (m += 1; continue)
A[i] && (t += 1)
end
return t, length(A) - t - m, m
end
function count_missing(A::Vector{Union{Bool,Missing}})
t, m = 0, 0
for v in A
ismissing(v) && (m += 1; continue)
v && (t += 1)
end
return t, length(A) - t - m, m
end
function count_bitmap(A::BitVector, B::BitVector)
m = count(B)
t = 0
for i in 1:length(A.chunks)
t += count_ones(A.chunks[i] & ~B.chunks[i])
end
return t, length(A) - t - m, m
end
println("BitVector")
display(@benchmark count_bitmap($bitvector_data, $bitvector_missing))
println()
println()
println("Bool")
display(@benchmark count_vector_bool($bool_data, $bool_missing))
println()
println()
println("Union{Bool,Missing}")
display(@benchmark count_missing($data_union))
On julia 0.6 I get these results:
BitVector
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 5.005 μs (0.00% GC)
median time: 5.385 μs (0.00% GC)
mean time: 6.092 μs (0.00% GC)
maximum time: 194.123 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 6
Bool
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 621.902 μs (0.00% GC)
median time: 673.981 μs (0.00% GC)
mean time: 736.019 μs (0.00% GC)
maximum time: 6.392 ms (0.00% GC)
--------------
samples: 6750
evals/sample: 1
Union{Bool,Missing}
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 1.483 ms (0.00% GC)
median time: 1.654 ms (0.00% GC)
mean time: 1.753 ms (0.00% GC)
maximum time: 3.022 ms (0.00% GC)
--------------
samples: 2840
evals/sample: 1
On julia 0.7 I get:
BitVector
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 1.901 μs (0.00% GC)
median time: 2.053 μs (0.00% GC)
mean time: 2.298 μs (0.00% GC)
maximum time: 60.252 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 10
Bool
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 712.375 μs (0.00% GC)
median time: 775.098 μs (0.00% GC)
mean time: 848.171 μs (0.00% GC)
maximum time: 2.547 ms (0.00% GC)
--------------
samples: 5858
evals/sample: 1
Union{Bool,Missing}
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 965.166 μs (0.00% GC)
median time: 1.062 ms (0.00% GC)
mean time: 1.143 ms (0.00% GC)
maximum time: 2.021 ms (0.00% GC)
--------------
samples: 4355
evals/sample: 1
Long story short: the bitvector is way, way, way faster than anything else. What is a bit surprising is that the Union{Bool,Missing} thing is not faster on julia 0.7, I would have hoped that it would at least be competitive with the two Vector{Bool} version. But maybe not all the optimizations for that case have made it into base, or maybe the way we’ve coded up that function is not ideal. Not sure.
EDIT: Initial version had an error, now corrected.