# A safe inbounds use with great performance effect

**URL:** https://discourse.julialang.org/t/a-safe-inbounds-use-with-great-performance-effect/81319
**Category:** Performance
**Created:** [May 19, 2022, 5:42pm UTC](https://discourse.julialang.org/t/a-safe-inbounds-use-with-great-performance-effect/81319 "2022-05-19T17:42:44Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [May 19, 2022, 5:42pm UTC](https://discourse.julialang.org/t/a-safe-inbounds-use-with-great-performance-effect/81319/1 "2022-05-19T17:42:44Z")

</div>

As a followup from this:

> [@OffsetArrays, inbounds, and confusion](https://discourse.julialang.org/t/offsetarrays-inbounds-and-confusion/81295/31):
>
> Not too tough: julia\> function f!(A) for (count, idx) in enumerate(eachindex(A)) A[idx] = count\*2 end end f! (generic function with 1 method) julia\> f!(A); @allocated f!(A) 0 Or this works as a slightly less-quick sanity check, too: julia\> using BenchmarkTools julia\> function g!(A) i = 0 for idx in eachindex(A) i += 1 A[idx] = i\*2 end end julia\> @btime f!($A) 11.029 ns (0 all…

Matt has shown that `enumerate(eachindex(a))` is quite fast:

```julia
julia> function f!(A)
           for (count, idx) in enumerate(eachindex(A))
               A[idx] = count*2
           end
       end

julia> function g!(A)
           i = 0
           for idx in eachindex(A)
               i += 1
               A[idx] = i*2
           end
       end

julia> a = rand(1:10,10^5);

julia> @btime f!($a)
  31.433 μs (0 allocations: 0 bytes)

julia> @btime g!($a)
  75.225 μs (0 allocations: 0 bytes)

```

But all the difference disappears by using `@inbounds`, which has quite an important performance effect for both functions:

```julia
julia> function f2!(A)
           for (count, idx) in enumerate(eachindex(A))
               @inbounds A[idx] = count*2
           end
       end
f2! (generic function with 1 method)

julia> @btime f2!($a)
  18.272 μs (0 allocations: 0 bytes)

julia> function g2!(A)
           i = 0
           for idx in eachindex(A)
               i += 1
               @inbounds A[idx] = i*2
           end
       end
g2! (generic function with 1 method)

julia> @btime g2!($a)
  18.063 μs (0 allocations: 0 bytes)

```

This would be one case where disabling bounds checking seems perfectly safe. (The llvm codes do not appear identical, though).

In this case the `inbounds` could be deduced by the compiler in both cases. Shouldn’t it?

---

<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: [May 19, 2022, 6:05pm UTC](https://discourse.julialang.org/t/a-safe-inbounds-use-with-great-performance-effect/81319/2 "2022-05-19T18:05:53Z")

</div>

It should! And it does on the v1.8-beta. You can see this because removing the bounds checks enables SIMD — and that’s why there’s such a big 2x (or more) speedup.

```julia
julia> @code_llvm debuginfo=:none f!(a)
# ... skipping ...
vector.body: ; preds = %vector.body, %vector.ph
  %index = phi i64 [0, %vector.ph], [%index.next, %vector.body]
  %vec.ind = phi <4 x i64> [<i64 1, i64 2, i64 3, i64 4>, %vector.ph], [%vec.ind.next, %vector.body]
# ... skipping ...

```

That `vector.body` and those `<4 x i64>`s mean SIMD.
