Need a basic example on using custom structs in CUDA.jl with Adapt.jl

I’m having a hard time understanding both the purpose of and how to use Adapt.jl to pass custom structs to CUDA kernels (that might not even be right). I’ve tried reading the CUDA.jl documentation and implementing a trivial example (below) but it errors as the passed struct isn’t a bitstype.

using CUDA
using Adapt

struct TestStruct
    testField
end

Adapt.@adapt_structure TestStruct

test = TestStruct([1,2,3] |> cu)

function squareGPU!(test)
    testField = test.testField
    index = threadIdx().x
    testField[index] = testField[index]^2
    return nothing
end

@cuda threads=3 squareGPU!(test)

ERROR: LoadError: GPU compilation of MethodInstance for squareGPU!(::TestStruct) failed
KernelError: passing and using non-bitstype argument
Argument 2 to your kernel function is of type TestStruct, which is not isbits:
.testField is of type Any which is not isbits.

To try to address the second part of the error, I tried restricting testField to CuArray:

using CUDA
using Adapt

struct TestStruct
    testField :: CuArray
end

Adapt.@adapt_structure TestStruct

test = TestStruct([1,2,3] |> cu)

function squareGPU!(test)
    testField = test.testField
    index = threadIdx().x
    testField[index] = testField[index]^2
    return nothing
end

@cuda threads=3 squareGPU!(test)

But this still results in an (seemingly better?) error.

LoadError: This function is not intended for use on the CPU

I appreciate y’alls advice as I might just have a fundamental misunderstanding of how to use Adapt.

As far as I understand it, it is important here to use parametric types.

using CUDA, Adapt


struct TestStruct{A}
    testField::A
end

Adapt.@adapt_structure TestStruct

test = TestStruct([1,2,3]) |> cu
# but this also works: test = TestStruct([1,2,3] |> cu) 

function squareGPU!(test)
    testField = test.testField
    index = threadIdx().x
    testField[index] = testField[index]^2
    return nothing
end

@cuda threads=3 squareGPU!(test)

Notice that you can first construct the CPU type TestStruct{Vector{Int64}} and then cu will automatically convert it to the fully specified GPU type TestStruct{CuArray{Int64, 1, CUDA.DeviceMemory}}.

The error message about the is bits type indicated that TestStruct as a type itself does not specify the type precise enough, also CuArray is not precise enough as it could be different types, dimension, etc.

2 Likes

Huh, simple enough! Amazing that it “just works”; Julia really is a wonderful thing.

1 Like