Custom Type for lazy kernel operations on AbstractArrays, ho can i improve it?

Hi guys is there any way to make this performant? it looks like a super useful type see Example of usage

Type Definition

struct KernelOp{T,N,S} <: AbstractArray{T,N}
    parent::S
    op::Function
    opdims::CartesianIndex{N}
    lowbound::CartesianIndex{N}
    upbound::CartesianIndex{N}
    @inline function KernelOp{T,N,S}(A::S,operation::Function,dims::CartesianIndex{N}) where {T,N,S <: AbstractArray{T,N}}
        l=minimum(CartesianIndices(A))
        h=maximum(CartesianIndices(A))
        new(A,operation,dims,l,h)
    end
end
@inline function KernelOp(A::AbstractArray{T,N},op::Function,dims::NTuple{N,Int}) where {T,N}
    KernelOp{eltype(A),ndims(A),typeof(A)}(A,op,CartesianIndex(dims))
end
import Base.size, Base.getindex, Base.IndexStyle

@inline IndexStyle(::KernelOp) = IndexCartesian()
@inline function size_parent(A::KernelOp)
    size(A.parent)
end
@inline function size(A::KernelOp)
    return size_parent(A)
end
@inline function getindex(A::KernelOp{T,N,S},Index::Vararg{Int,N}) where {T,N,S}
    I=CartesianIndex(Index)
    @boundscheck checkbounds(A,I)
    @inbounds begin
        lowI=max(I-A.opdims,A.lowbound)
        upI=min(I+A.opdims,A.upbound)
        return A.op(A.parent,lowI:upI,I)
    end
end

Example

take a julia array, mean filter it (f1),
take maximum filter of the mean filtered array (f2) then compare for equality f1 and f2

@inline function op_mean(A,r,c)
    m=zero(eltype(A))
    @inbounds begin
        @simd for i in r
            m+=A[i]
        end
    end
    m/length(r)
end
@inline function op_max(A,r,c)
    m=A[r[1]]
    @inbounds begin
        @simd for i in r
            m=max(m,A[i])
        end
    end
    m
end


M=rand(1000,1000)
f1=KernelOp(M,op_mean,(1,1))
f2=KernelOp(f1,op_max,(2,2))

f1.==f2 # here all the computations happen, but it allocates a lot even though it doesnt build explicit  copies of the original array

to me this looks super beautiful, but unfortunately it allocates a lot, i would like some advice on making it more performant

I haven’t tested it and I’m taking a guess, but I suspect you want your op to be concretely typed (Function is abstract).
Why don’t you try defining the struct as following:

struct KernelOp{T,N,S,F<:Function} <: AbstractArray{T,N}
    parent::S
    op::F
    opdims::CartesianIndex{N}
    lowbound::CartesianIndex{N}
    upbound::CartesianIndex{N}
    @inline function KernelOp{T,N,S}(A::S,operation::Function,dims::CartesianIndex{N}) where {T,N,S <: AbstractArray{T,N}}
        l=minimum(CartesianIndices(A))
        h=maximum(CartesianIndices(A))
        new{T,N,S,typeof(operation)}(A,operation,dims,l,h)
    end
end
2 Likes

I will try very soon thanks :slight_smile:

it worked!

2 Likes