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.