Efficient way to get indices of true elements from BitArray

I usually use the following:

x = rand(Bool, 10)
(1:length(x))[x]

Result:

julia> x = rand(Bool, 10)
10-element Vector{Bool}:
 0
 1
 0
 0
 0
 1
 1
 0
 0
 0

julia> (1:length(x))[x]
3-element Vector{Int64}:
 2
 6
 7

But i think this is inefficient and slow, is there better way?

findall is how I’d do this and where I’d expect an optimization to appear if one existed… but there’s not a BitArray specific optimization there now.

2 Likes

Why do you think it’s slow? I agree with Matt that it’s a slightly unusual way to write findall, but it seems to give the same performance.

julia> x = rand(Bool, 1_000_000);

julia> using BenchmarkTools

julia> @btime findall($x);
  4.927 ms (2 allocations: 3.81 MiB)

julia> @btime (1:length($x))[$x];
  4.479 ms (2 allocations: 3.81 MiB)

Side note: this is not a BitArray, it is a Vector{Bool}, where each element takes up one byte.

If you want an actual BitArray, with 8 elements per byte, you can do

rand(10) .< 0.5

or, probably more efficiently:

using Random
bitrand(10)
2 Likes

What you’re benchmarking there is Vector{Bool}. I had thought there might be some performance left on the table without a BitArray-specific bit fiddling method… but this looks pretty good: the BitArray is beating a Vector{Bool} by 10x.

julia> using BenchmarkTools

julia> x = rand(Bool, 1_000_000);

julia> y = BitArray(x);

julia> @btime findall($x);
  4.126 ms (2 allocations: 3.82 MiB)

julia> @btime findall($y);
  460.060 μs (2 allocations: 3.82 MiB)
1 Like

I don’t see quite such a large discrepancy (more like 5x, might be CPU dependent?), but I do see findall outperforming the index-into-a-range solution for BitArray:

julia> @btime (1:length($y))[$y];
  905.300 μs (2 allocations: 3.82 MiB)

julia> @btime findall($y);
  687.100 μs (2 allocations: 3.82 MiB)
3 Likes