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:

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

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()

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()

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.