API

Kernel language

KernelAbstractions.@kernelMacro
@kernel function f(args) end

Takes a function definition and generates a Kernel constructor from it. The enclosed function is allowed to contain kernel language constructs. In order to call it the kernel has first to be specialized on the backend and then invoked on the arguments.

Kernel language

Kernel constructor

After defining a kernel function f, call f(backend[, workgroupsize[, ndrange]]) to obtain a Kernel specialized for that backend. Workgroup size and ndrange can be fixed at construction time (enabling size-specific compile-time optimizations and fewer runtime checks, at the cost of recompilation when the sizes change) or supplied at launch:

f(backend)                    # dynamic workgroup size and ndrange
f(backend, 64)                # static workgroup size of 64
f(backend, 64, 1024)          # static workgroup size and ndrange
f(backend, 64, (128, 128))    # multi-dimensional ndrange

Example

using KernelAbstractions

@kernel function vecadd(A, @Const(B))
    I = @index(Global)
    @inbounds A[I] += B[I]
end

dev = CPU()
A = ones(1024)
B = rand(1024)
vecadd(dev, 64)(A, B, ndrange=length(A))
synchronize(dev)
source
@kernel config function f(args) end

This allows for two different configurations:

  1. cpu={true, false}: Disables code-generation of the CPU function. This relaxes semantics such that KernelAbstractions primitives can be used in non-kernel functions.
  2. inbounds={false, true}: Enables a forced @inbounds macro around the function definition in the case the user is using too many @inbounds already in their kernel. Note that this can lead to incorrect results, crashes, etc and is fundamentally unsafe. Be careful!
  3. unsafe_indices={false, true}: Disables the implicit validation of indices, users must avoid @index(Global).
Warning

This is an experimental feature.

Note

cpu={true, false} is deprecated for KernelAbstractions 1.0

source
KernelAbstractions.@ConstMacro
@Const(A)

@Const is an argument annotiation that asserts that the memory reference by A is both not written to as part of the kernel and that it does not alias any other memory in the kernel.

Danger

Violating those constraints will lead to arbitrary behaviour.

As an example given a kernel signature kernel(A, @Const(B)), you are not allowed to call the kernel with kernel(A, A) or kernel(A, view(A, :)).

source
KernelAbstractions.@indexMacro
@index

The @index macro can be used to give you the index of a workitem within a kernel function. It supports both the production of a linear index or a cartesian index. A cartesian index is a general N-dimensional index that is derived from the iteration space.

Index granularity

  • Global: Used to access global memory.
  • Group: The index of the workgroup.
  • Local: The within workgroup index.

Index kind

  • Linear: Produces an Int64 that can be used to linearly index into memory.
  • Cartesian: Produces a CartesianIndex{N} that can be used to index into memory.
  • NTuple: Produces a NTuple{N} that can be used to index into memory.

If the index kind is not provided it defaults to Linear, this is subject to change.

Examples

@index(Global, Linear)
@index(Global, Cartesian)
@index(Local, Cartesian)
@index(Group, Linear)
@index(Local, NTuple)
@index(Global)
source
KernelAbstractions.@privateMacro
@private T dims

Declare storage that is local to each item in the workgroup. This can be safely used across @synchronize statements. On a CPU, this will allocate additional implicit dimensions to ensure correct localization.

For storage that only persists between @synchronize statements, an MArray can be used instead.

See also @uniform.

source
@private mem = 1

Creates a private local of mem per item in the workgroup. This can be safely used across @synchronize statements.

source
KernelAbstractions.@synchronizeMacro
@synchronize()

After a @synchronize statement all read and writes to global and local memory from each thread in the workgroup are visible in from all other threads in the workgroup.

Note

@synchronize() must be encountered by all workitems of a work-group executing the kernel or by none at all.

source
@synchronize(cond)

After a @synchronize statement all read and writes to global and local memory from each thread in the workgroup are visible in from all other threads in the workgroup. cond is not allowed to have any visible sideffects.

Platform differences

  • GPU: This synchronization will only occur if the cond evaluates.
  • CPU: This synchronization will always occur.
Warning

This variant of the @synchronize macro violates the requirement that @synchronize must be encountered by all workitems of a work-group executing the kernel or by none at all. Since v0.9.34 this version of the macro is deprecated and lowers to @synchronize()

source
KernelAbstractions.@printMacro
@print(items...)

This is a unified print statement.

Platform differences

  • GPU: This will reorganize the items to print via @cuprintf
  • CPU: This will call print(items...)
source
KernelAbstractions.@uniformMacro
@uniform expr

expr is evaluated outside the workitem scope. This is useful for variable declarations that span workitems, or are reused across @synchronize statements.

source
KernelAbstractions.@groupsizeMacro
@groupsize()

Query the workgroupsize on the backend. This function returns a tuple corresponding to kernel configuration. In order to get the total size you can use prod(@groupsize()).

source

Host language

Backends and arrays

KernelAbstractions.BackendType
Backend

Abstract supertype for all KernelAbstractions backends.

Concrete backends (for example CUDABackend from CUDA.jl or CPU from this package) determine where arrays are allocated and where kernels execute. Use get_backend to obtain the backend for an array and allocate to create storage on a backend.

Example

backend = get_backend(A)
kernel = my_kernel(backend, 256)
kernel(A, ndrange=length(A))
synchronize(backend)
source
KernelAbstractions.GPUType

Abstract type for all GPU based KernelAbstractions backends.

Note

New backend implementations must sub-type this abstract type.

Note

GPU will be removed in KernelAbstractions v1.0

source
KernelAbstractions.CPUType
CPU

Type alias for POCLBackend, the CPU execution backend.

Construct with CPU() (equivalent to POCLBackend()). Kernels run on the host via POCL/OpenCL using the same programming model as GPU backends, which is useful for debugging and for running kernel code without a GPU.

Example

A = ones(Float32, 1024)
mul2_kernel(CPU(), 64)(A, ndrange=length(A))
synchronize(CPU())
source
KernelAbstractions.get_backendFunction
get_backend(A::AbstractArray)::Backend

Get a Backend instance suitable for array A.

Note

Backend implementations must provide get_backend for their custom array type. It should be the same as the return type of allocate

source
KernelAbstractions.allocateFunction
allocate(::Backend, Type, dims...; unified=false)::AbstractArray

Allocate a storage array appropriate for the computational backend. unified=true allocates an array using unified memory if the backend supports it and throws otherwise. Use supports_unified to determine whether it is supported by a backend.

Note

Backend implementations must implement allocate(::NewBackend, T, dims::Tuple) Backend implementations should implement allocate(::NewBackend, T, dims::Tuple; unified::Bool=false)

source
KernelAbstractions.zerosFunction
zeros(::Backend, Type, dims...; unified=false)::AbstractArray

Allocate a storage array appropriate for the computational backend filled with zeros. unified=true allocates an array using unified memory if the backend supports it and throws otherwise.

source
KernelAbstractions.onesFunction
ones(::Backend, Type, dims...; unified=false)::AbstractArray

Allocate a storage array appropriate for the computational backend filled with ones. unified=true allocates an array using unified memory if the backend supports it and throws otherwise.

source
KernelAbstractions.copyto!Function
copyto!(::Backend, dest::AbstractArray, src::AbstractArray)

Perform an asynchronous copyto! operation that is execution ordered with respect to the back-end.

For most users, Base.copyto! should suffice, performance a simple, synchronous copy. Only when you know you need asynchronicity w.r.t. the host, you should consider using this asynchronous version, which requires additional lifetime guarantees as documented below.

Warning

Because of the asynchronous nature of this operation, the user is required to guarantee that the lifetime of the source extends past the completion of the copy operation as to avoid a use-after-free. It is not sufficient to simply use GC.@preserve around the call to copyto!, because that only extends the lifetime past the operation getting queued. Instead, it may be required to synchronize(), or otherwise guarantee that the source will still be around when the copy is executed:

arr = zeros(64)
GC.@preserve arr begin
    copyto!(backend, arr, ...)
    # other operations
    synchronize(backend)
end
Note

On some back-ends it may be necessary to first call pagelock! on host memory to enable fully asynchronous behavior w.r.t to the host.

Note

Backends must implement this function.

source
KernelAbstractions.pagelock!Function
pagelock!(::Backend, dest::AbstractArray)

Pagelock (pin) a host memory buffer for a backend device. This may be necessary for copyto! to perform asynchronously w.r.t to the host/

This function should return nothing; or missing if not implemented.

Note

Backends may implement this function.

source
KernelAbstractions.unsafe_free!Function
unsafe_free!(x::AbstractArray)

Release the memory of an array for reuse by future allocations and reduce pressure on the allocator. After releasing the memory of an array, it should no longer be accessed.

Note

On CPU backend this is always a no-op.

Note

Backend implementations may implement this function. If not implemented for a particular backend, default action is a no-op. Otherwise, it should be defined for backend's array type.

source
KernelAbstractions.functionalFunction
functional(::Backend)

Queries if the provided backend is functional. This may mean different things for different backends, but generally should mean that the necessary drivers and a compute device are available.

This function should return a Bool or missing if not implemented.

KernelAbstractions v0.9.22

This function was added in KernelAbstractions v0.9.22

source
KernelAbstractions.versioninfoFunction
versioninfo(io::IO=stdout, backend::Backend)::Nothing

Print information about backend to io. It is up to the backends to determine what is relevant.

Note

Backend implementations may implement this function. If they do so, they should implement versioninfo(io::IO, ::Backend)::Nothing

source
KernelAbstractions.supports_unifiedFunction
supports_unified(::Backend)::Bool

Returns whether unified memory arrays are supported by the backend.

Note

Backend implementations should implement this function only if they do support unified memory.

source
KernelAbstractions.supports_atomicsFunction
supports_atomics(::Backend)::Bool

Returns whether @atomic operations are supported by the backend.

Note

Backend implementations must implement this function only if they do not support atomic operations with Atomix.

source
KernelAbstractions.supports_float64Function
supports_float64(::Backend)::Bool

Returns whether Float64 values are supported by the backend.

Note

Backend implementations must implement this function only if they do not support Float64.

source

Devices and execution

KernelAbstractions.deviceFunction
device(backend::Backend)::Int

Return the 1-based index of the currently active device for backend.

Note

The default implementation assumes a single device. Backends supporting multiple devices must implement device(backend::Backend)::Int, ndevices, and device!.

source
KernelAbstractions.ndevicesFunction
ndevices(backend::Backend)::Int

Return the number of devices available to backend.

Note

The default implementation assumes a single device. Backends supporting multiple devices must implement ndevices(backend::Backend)::Int, device, and device!.

source
KernelAbstractions.device!Function
device!(backend::Backend, id::Int)

Select the active device for backend. id is a 1-based device index and must satisfy 1 <= id <= ndevices(backend).

Example

device!(CUDABackend(), 2)  # use the second CUDA device
Note

The default implementation assumes a single device. Backends supporting multiple devices must implement device!(backend::Backend, id::Int), ndevices, and device.

source
KernelAbstractions.priority!Function
priority!(::Backend, prio::Symbol)

Set the priority for the backend stream/queue. This is an optional feature that backends may or may not implement. If a backend shall support priorities it must accept :high, :normal, :low. Where :normal is the default.

Note

Backend implementations may implement this function.

source

Kernel handles

KernelAbstractions.KernelType
Kernel{Backend, WorkgroupSize, NDRange, Func}

Host-side handle for a kernel specialized on a backend, workgroup size, and ndrange.

Kernels are created by calling a @kernel function on a backend, for example my_kernel(CUDABackend(), 256). The returned object is callable:

kernel = my_kernel(backend, 64)
kernel(A, B, ndrange=length(A))   # launch asynchronously
synchronize(backend)

Use workgroupsize, ndrange, and backend to inspect a kernel's static configuration.

Note

Backend implementations must implement:

(kernel::Kernel{<:NewBackend})(args...; ndrange=nothing, workgroupsize=nothing)

As well as the on-device functionality.

source

Reflection

These macros help inspect the generated kernel code. LLVM IR reflection via @ka_code_llvm is only supported on the CPU backend.

KernelAbstractions.@ka_code_typedMacro
@ka_code_typed [kwargs...] kernel(args...; ndrange=..., workgroupsize=...)

Return the typed IR for a kernel's device function, similar to InteractiveUtils.code_typed.

Pass interactive=true to descend into the IR with Cthulhu (must be loaded in the session). If ndrange is fixed at kernel construction time, it can be omitted at the call site.

Examples

@ka_code_typed my_kernel(backend)(A, ndrange=length(A))
@ka_code_typed my_kernel(backend, 64)(A, ndrange=length(A))
@ka_code_typed optimize=false my_kernel(backend)(A, ndrange=length(A))
@ka_code_typed interactive=true my_kernel(CPU())(A, ndrange=length(A))
source
KernelAbstractions.@ka_code_llvmMacro
@ka_code_llvm [kwargs...] kernel(args...; ndrange=..., workgroupsize=...)

Return the LLVM IR for a kernel's device function, similar to InteractiveUtils.code_llvm.

Only supported on the CPU backend. GPU kernels will throw an error.

Examples

@ka_code_llvm my_kernel(CPU())(A, ndrange=length(A))
@ka_code_llvm my_kernel(CPU(), 64)(A, ndrange=length(A))
source

Internal

The functionalities in this section are considered internal and not part of the public API contract. They are only documented here for developers and contributors of KernelAbstractions.jl, but should not be used by end users (and if they do, they should expect breakage without notice).

KernelAbstractions.partitionFunction
partition(kernel, ndrange, workgroupsize)

Partition the iteration space of kernel into workgroups.

Returns the blocked iteration space and whether dynamic bounds-checking is required for the last (possibly partial) workgroup. Primarily used by backend implementations and tests.

source
KernelAbstractions.@contextMacro
@context()

Access the hidden context object used by KernelAbstractions.

Warning

Only valid to be used from a kernel with cpu=false.

Note

@context will be supported on all backends in KernelAbstractions 1.0

function f(@context, a)
    I = @index(Global, Linear)
    a[I]
end

@kernel cpu=false function my_kernel(a)
    f(@context, a)
end
source
KernelAbstractions.argconvertFunction
argconvert(kernel::Kernel, arg)

Convert arg to the device-side representation expected by kernel's backend.

Backend implementations define methods for their array and scalar types. This is called automatically when a kernel is launched.

source