# Unexpectedly slow performance of iszero for BitVector

**URL:** <https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238>\
**Category:** Performance\
**Tags:** question\
**Created:** [August 15, 2024, 8:20pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238 "2024-08-15T20:20:55Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![brianguenter](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brianguenter/32/29519_2.png) [@brianguenter](https://discourse.julialang.org/u/brianguenter)\
**Post date:** [August 15, 2024, 8:20pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/1 "2024-08-15T20:20:55Z")

</div>

The builtin function `iszero` for BitVector seems unreasonably slow:

```julia
julia> a = BitVector(fill(0,10000));

julia> @benchmark iszero($a)
BenchmarkTools.Trial: 10000 samples with 8 evaluations.
 Range (min … max): 3.750 μs … 8.588 μs ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 3.775 μs ┊ GC (median): 0.00%
 Time (mean ± σ): 3.806 μs ± 95.423 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

    █ █
  ▂▁█▁█▁▁▅▁█▁▁▂▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▇▁▁▆▁▂▁▁▁▁▁▁▁▂▁▁▁▁▁▁▂▁▂▁▁▂▁▂▁▂ ▂
  3.75 μs Histogram: frequency by time 4.05 μs <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

I implemented my own version of this

```julia

is_zero(a::BitVector) = !any(a)

```

which is about 100x faster:

```julia
julia> using FastDifferentiation #has my implementation of is_zero for BitVector
Precompiling FastDifferentiation...
  1 dependency successfully precompiled in 2 seconds. 47 already precompiled.

julia> @benchmark FastDifferentiation.is_zero($a)
BenchmarkTools.Trial: 10000 samples with 993 evaluations.
 Range (min … max): 35.549 ns … 74.119 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 35.750 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 36.031 ns ± 0.875 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

    █
  ▂▇█▁▄▃▁▁▂▁▂▁▁▁▁▁▁▁▁▁▁▅▁▃▁▁▁▁▁▁▁▂▂▁▂▂▁▂▂▂▁▂▁▂▁▂▁▁▁▂▁▁▁▂▁▁▁▂▂ ▂
  35.5 ns Histogram: frequency by time 39.9 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

Am I making a benchmarking error? Or is `iszero(a::BitVector)` slower than it should be? I’d rather use the builtin `iszero` than my own, makes life simpler and less error prone.

Here’s my versioninfo:

```julia
Julia Version 1.11.0-rc2
Commit 34c3a63147 (2024-07-29 06:24 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: 32 × AMD Ryzen 9 7950X 16-Core Processor
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, znver4)
Threads: 1 default, 0 interactive, 1 GC (on 32 virtual cores)
Environment:
  JULIA_NUM_THREADS = 1
  JULIA_EDITOR = code.cmd

```

Results are the same for 1.10 so this doesn’t seem to be a performance regression:

```julia
julia> a = BitVector(fill(0,10000));

julia> @benchmark iszero($a)
BenchmarkTools.Trial: 10000 samples with 8 evaluations.
 Range (min … max): 3.750 μs … 6.800 μs ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 3.788 μs ┊ GC (median): 0.00%
 Time (mean ± σ): 3.810 μs ± 103.563 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

    ▄ █
  ▂██▁██▁▃▃▂▁▂▂▁▂▂▄▁▃▂▁▂▂▂▁▂▂▁▂▂▂▁▂▂▁▂▂▂▁▂▂▁▂▂▂▁▂▂▁▂▂▂▁▂▂▁▂▂▂ ▂
  3.75 μs Histogram: frequency by time 4.28 μs <

 Memory estimate: 0 bytes, allocs estimate: 0.

julia> versioninfo()
Julia Version 1.10.4
Commit 48d4fd4843 (2024-06-04 10:41 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: 32 × AMD Ryzen 9 7950X 16-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, znver3)
Threads: 16 default, 0 interactive, 8 GC (on 32 virtual cores)
Environment:
  JULIA_EDITOR = code.cmd
  JULIA_NUM_THREADS = 16

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [August 15, 2024, 8:31pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/2 "2024-08-15T20:31:10Z")

</div>

`any` is specialized for `BitArray` while `iszero` (implemented as `all(iszero, x)`) is not and will just iterate element by element.

---

<div class="post-metadata">

**Author:** ![brianguenter](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brianguenter/32/29519_2.png) [@brianguenter](https://discourse.julialang.org/u/brianguenter)\
**Post date:** [August 15, 2024, 9:07pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/3 "2024-08-15T21:07:31Z")

</div>

Is there a technical reason why `iszero` can’t be specialized for BitVector? Does the function

```julia
iszero(a::Bitvector) = !any(a)

```

fail under some circumstances? Or is it just that this particular operation never got optimized?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [August 15, 2024, 9:22pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/4 "2024-08-15T21:22:03Z")

</div>

Nope, this would be a great first pull request to the project!

There’s nothing fundamentally special about Julia’s “built-ins”. In fact, they’re no different than code you can write. BitArray has _some_ hand-written specializations that can operate on 64 elements (or more!) at a time… but if they don’t exist they fall back to generic implementations. That’s all that’s happening here.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [August 15, 2024, 10:31pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/5 "2024-08-15T22:31:58Z")

</div>

But what is the right approach? Is it to specialize `iszero(::BitArray)`, or is it to specialize `all(f, ::BitArray)`, or something else?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [August 15, 2024, 11:06pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/6 "2024-08-15T23:06:49Z")

</div>

specializing `all` seems good if possible, but if complicated, specializing iszero is definitely easy.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [August 15, 2024, 11:13pm UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/7 "2024-08-15T23:13:27Z")

</div>

I am surprised that `any` is not defined in terms of `all`, or vice-versa. It is literally the same check just with a condition negated.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [August 16, 2024, 2:50am UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/8 "2024-08-16T02:50:17Z")

</div>

For one reason at least, the early return/exit condition is different

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [August 16, 2024, 11:16am UTC](https://discourse.julialang.org/t/unexpectedly-slow-performance-of-iszero-for-bitvector/118238/9 "2024-08-16T11:16:19Z")

</div>

I mean, for the single-parameter method, yes. But for the one that takes a condition, `all(f, xs) = !any(!f, xs)` and vice-versa.
