Can someone explain why broadcast is promoting Array{Bool}, and if this has been done for a good reason, does that mean I should favor using BitArray over Array{Bool}?
Depends, since extracting elements from a BitArray requires bit manipulation, so it may be slower. Consider this example:
using BenchmarkTools
function sum_indices(a::AbstractArray{<:Bool}, ixs)
total = 0
for ix in ixs
total += a[ix]
end
total
end
a = rand(Bool,1000)
b = BitArray(a)
ixs = rand(1:length(a), 10000)
@benchmark sum_indices($a, $ixs)
@benchmark sum_indices($b, $ixs)
So both are useful, depends on your context. If unsure, benchmark, however, you are best off if you write your code to work with all options and only pay attention to these choices when they affect performance.
Thanks guys, that’s very helpful background. But I’m not sure I’ve fully understood why broadcast is promoting Array{Bool} to BitArray. At best it seems a bit inconsistent, and at worst like it could induce type instability. I think this simple (pointless/contrived but hopefully helpful) example illustrates my point:
function flipme(foo, flip)
if flip
return .!foo
else
return foo
end
end
I think most people, myself included, would be surprised that this function is type unstable.
I’m not a huge fan of returning special array type for Bool eltype but this example is irrelevant. Whatever the return type is there are always input types that make this function type unstable.
Thanks, fair point. I guess I was just trying to highlight the somewhat surprising (to me) result of returning a special array type from broadcast.
For reference, for anyone else who comes across this, this change was introduced by #17623. The relevant commentary is (#17623 comment):
One “new” behavior is that broadcast now always produces a BitArray if the output is a Bool array. This preserves the BitArray-producing behavior of expressions like A .== B .> 3.
I must confess I don’t love this changed behavior, but if it’s necessary for fusion, it seems like a very reasonably trade-off. I guess all I need in the corner cases where I want to preserve Array{Bool} is map(!, foo).