# OffsetArrays vs Arrays speed

**URL:** <https://discourse.julialang.org/t/offsetarrays-vs-arrays-speed/100477>\
**Category:** New to Julia\
**Created:** [June 17, 2023, 1:35am UTC](https://discourse.julialang.org/t/offsetarrays-vs-arrays-speed/100477 "2023-06-17T01:35:14Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![gte465f](https://avatars.discourse-cdn.com/v4/letter/g/9dc877/32.png) [@gte465f](https://discourse.julialang.org/u/gte465f)\
**Post date:** [June 17, 2023, 1:35am UTC](https://discourse.julialang.org/t/offsetarrays-vs-arrays-speed/100477/1 "2023-06-17T01:35:14Z")

</div>

Hello Julia wizards!

I’ve been exploring this language for about a week, and so far I’m very very intrigued. I have a simulation code I’m writing that would greatly benefit from using OffsetArrays, but everything I try results in a massive slowdown.

I’ve boiled down the pattern to a simple example, and all of the documentation I can understand suggests that the Array version and the OffsetArray version should be the same speed. The benchmark result is:

Arrays: 730.958 μs (0 allocations: 0 bytes)  
OffsetArrays: 129.026 ms (9998470 allocations: 167.82 MiB)

My guess is all of those unexpected allocations are the problem, but I have no idea where they are coming from. Help, please?

```julia
using BenchmarkTools
using OffsetArrays

mutable struct MyArray
    a::Array{Float64, 1}
    b::Array{Float64, 1}

    MyArray() = new()
    MyArray(a, b) = new(a,b)
end

mutable struct MyOffsetArray
    a::OffsetArray{Float64, 1}
    b::OffsetArray{Float64, 1}

    MyOffsetArray() = new()
    MyOffsetArray(a, b) = new(a,b)
end

function myoperate(arg::Union{MyArray, MyOffsetArray})
    for i = 1:length(arg.b)
        arg.b[i] = arg.b[i] + 0.5 * (arg.a[i] + arg.a[i+1])
    end
end

function timeit()
    nx = 1000000

    a = rand(nx+1)
    b = rand(nx)

    array = MyArray(a, b)
    offset = MyOffsetArray(a, b)

    @btime myoperate($array)
    @btime myoperate($offset)
end

timeit()

```

---

<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:** [June 17, 2023, 1:54am UTC](https://discourse.julialang.org/t/offsetarrays-vs-arrays-speed/100477/2 "2023-06-17T01:54:48Z")

</div>

The problem is that `OffsetArray{Float64, 1}` is an abstract type (since the offset is also part of the type). You probably want to use

```julia
mutable struct MyParametricArray{T<:AbstractArray}
    a::T
    b::T
    MyParametricArray() = new()
    MyParametricArray(a, b) = new(a,b)
end

```

which will specialize correctly.

---

<div class="post-metadata">

**Author:** ![gte465f](https://avatars.discourse-cdn.com/v4/letter/g/9dc877/32.png) [@gte465f](https://discourse.julialang.org/u/gte465f)\
**Post date:** [June 17, 2023, 2:11am UTC](https://discourse.julialang.org/t/offsetarrays-vs-arrays-speed/100477/3 "2023-06-17T02:11:25Z")

</div>

Wow, thanks! That led me the right way (and it would have been a long time before I figured that out…)

Arrays: 734.166 μs (0 allocations: 0 bytes)  
OffsetArrays: 128.470 ms (9998470 allocations: 167.82 MiB)  
OffsetArrays (parametric): 1.276 ms (0 allocations: 0 bytes)

Final code:

```julia
using BenchmarkTools
using OffsetArrays

mutable struct MyArray
    a::Array{Float64, 1}
    b::Array{Float64, 1}
end

mutable struct MyOffsetArray
    a::OffsetArray{Float64, 1}
    b::OffsetArray{Float64, 1}
end

mutable struct MyParametricArray{T<:AbstractArray}
    a::T
    b::T
end

function myoperate(arg::Union{MyArray, MyOffsetArray, MyParametricArray})
    for i = 1:length(arg.b)
        arg.b[i] = arg.b[i] + 0.5 * (arg.a[i] + arg.a[i+1])
    end
end

function timeit()
    nx = 1000000

    a = rand(nx+1)
    b = rand(nx)

    ao = OffsetArrays.Origin(1)(a)
    bo = OffsetArrays.Origin(1)(b)

    array = MyArray(a, b)
    offset = MyOffsetArray(ao, bo)
    parameter = MyParametricArray(ao, bo)

    @btime myoperate($array)
    @btime myoperate($offset)
    @btime myoperate($parameter)
end

timeit()

```

---

<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:** [June 17, 2023, 7:04am UTC](https://discourse.julialang.org/t/offsetarrays-vs-arrays-speed/100477/4 "2023-06-17T07:04:06Z")

</div>

> [@gte465f](#):
>
> ```julia
> for = 1:length(arg.b)
> arg.b[i] = arg.b[i] + 0.5 * (arg.a[i] + arg.a[i+1])
> end
> 
> ```

This seems highly risky. If `a` and `b` are OffsetArrays, those will likely not be valid indices (that’s the point of OffsetArrays, really). Use `eachindex(a, b)` instead.

Also, consider whether you need your type to be `mutable`. Immutable types are generally more efficient, and you can still mutate mutable fields of an immutable type, so your array code would still work.

---

<div class="post-metadata">

**Author:** ![gte465f](https://avatars.discourse-cdn.com/v4/letter/g/9dc877/32.png) [@gte465f](https://discourse.julialang.org/u/gte465f)\
**Post date:** [June 17, 2023, 2:12pm UTC](https://discourse.julialang.org/t/offsetarrays-vs-arrays-speed/100477/5 "2023-06-17T14:12:01Z")

</div>

Yes, I use eachindex in the real code. I was just trying to boil the problem down to the minimal case here.

Thanks for the tip about mutable.
