Passing a struct containing a vector to the GPU

Viewed 61

I'm making my first steps with GPU programming in Julia. I've been successful in passing a vector to the GPU through the use of CuDeviceVector. Now I'm trying to wrap that vector in a custom struct. Unfortunately I cannot get it to run due to my type not being isbits even though I am using Adapt.jl:

import Adapt
using CUDA

# Wraps the vector on the CPU side
struct Data{T}
    values::CuVector{T}
end

# Wraps the vector on the GPU side
struct DataOnGPU{T}
    values::CuDeviceVector{T}
end

# Convert CPU struct to GPU struct
function Adapt.adapt_structure(to, val::Data)
    v = Adapt.adapt_structure(to, val.values)
    DataOnGPU(v)
end

# GPU code
function kernel(data::DataOnGPU)
    # Do nothing for now
    return
end

# Create some random values and wrap them in the custom struct
values = rand(Int32, 10) |> CuVector
data = Data(values)

# Run the kernel
kernel = @cuda launch=false kernel(data)
config = launch_configuration(kernel.fun)
kernel(data; threads=config.threads, blocks=config.blocks)
synchronize()

Output:

ERROR: LoadError: GPU compilation of kernel #kernel(DataOnGPU{Int32}) failed
KernelError: passing and using non-bitstype argument

Argument 2 to your kernel function is of type DataOnGPU{Int32}, which is not isbits:
  .values is of type CuDeviceVector{Int32} which is not isbits.
    .ptr is of type Core.LLVMPtr{Int32} which is not isbits.
1 Answers

I can't claim to grasp all the nuances of CUDA.jl/Adapt.jl, but what sticks out to me is that you are wrapping your values inconsistently: Data turns into a different type DataOnGPU.

The 2 different but sort-of-the-same structs also stick out like a clear anti-pattern: you really just want to capture that distinction in T instead. In short:

struct Data{T<:AbstractArray}
    values::T
end

and your adapter simply:

function Adapt.adapt_structure(to, val::Data)
    v = Adapt.adapt_structure(to, val.values)
    Data(v)
end

(unrelated, you are also redefining kernel here)

Related