API
Kernel language
KernelAbstractions.@kernel — Macro
@kernel function f(args) endTakes 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 ndrangeExample
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)@kernel config function f(args) endThis allows for two different configurations:
cpu={true, false}: Disables code-generation of the CPU function. This relaxes semantics such that KernelAbstractions primitives can be used in non-kernel functions.inbounds={false, true}: Enables a forced@inboundsmacro around the function definition in the case the user is using too many@inboundsalready in their kernel. Note that this can lead to incorrect results, crashes, etc and is fundamentally unsafe. Be careful!unsafe_indices={false, true}: Disables the implicit validation of indices, users must avoid@index(Global).
KernelAbstractions.@Const — Macro
@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.
KernelAbstractions.@index — Macro
@indexThe @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 theworkgroup.Local: The withinworkgroupindex.
Index kind
Linear: Produces anInt64that can be used to linearly index into memory.Cartesian: Produces aCartesianIndex{N}that can be used to index into memory.NTuple: Produces aNTuple{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)KernelAbstractions.@localmem — Macro
@localmem T dimsDeclare storage that is local to a workgroup.
KernelAbstractions.@private — Macro
@private T dimsDeclare 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.
@private mem = 1Creates a private local of mem per item in the workgroup. This can be safely used across @synchronize statements.
KernelAbstractions.@synchronize — Macro
@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.
@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 thecondevaluates.CPU: This synchronization will always occur.
KernelAbstractions.@print — Macro
@print(items...)This is a unified print statement.
Platform differences
GPU: This will reorganize the items to print via@cuprintfCPU: This will callprint(items...)
KernelAbstractions.@uniform — Macro
@uniform exprexpr is evaluated outside the workitem scope. This is useful for variable declarations that span workitems, or are reused across @synchronize statements.
KernelAbstractions.@groupsize — Macro
@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()).
KernelAbstractions.@ndrange — Macro
@ndrange()Query the ndrange on the backend. This function returns a tuple corresponding to kernel configuration.
Host language
Backends and arrays
KernelAbstractions.Backend — Type
BackendAbstract 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)KernelAbstractions.GPU — Type
Abstract type for all GPU based KernelAbstractions backends.
KernelAbstractions.CPU — Type
CPUType 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())KernelAbstractions.POCL.POCLKernels.POCLBackend — Type
POCLBackend()CPU backend that compiles kernels to OpenCL via POCL and executes them on the host. This is the concrete type behind the CPU alias.
KernelAbstractions.get_backend — Function
get_backend(A::AbstractArray)::BackendGet a Backend instance suitable for array A.
Backend implementations must provide get_backend for their custom array type. It should be the same as the return type of allocate
KernelAbstractions.allocate — Function
allocate(::Backend, Type, dims...; unified=false)::AbstractArrayAllocate 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.
KernelAbstractions.zeros — Function
zeros(::Backend, Type, dims...; unified=false)::AbstractArrayAllocate 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.
KernelAbstractions.ones — Function
ones(::Backend, Type, dims...; unified=false)::AbstractArrayAllocate 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.
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.
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)
endOn 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.
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.
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.
KernelAbstractions.functional — Function
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.versioninfo — Function
versioninfo(io::IO=stdout, backend::Backend)::NothingPrint information about backend to io. It is up to the backends to determine what is relevant.
KernelAbstractions.supports_unified — Function
supports_unified(::Backend)::BoolReturns whether unified memory arrays are supported by the backend.
KernelAbstractions.supports_atomics — Function
supports_atomics(::Backend)::BoolReturns whether @atomic operations are supported by the backend.
KernelAbstractions.supports_float64 — Function
supports_float64(::Backend)::BoolReturns whether Float64 values are supported by the backend.
Devices and execution
KernelAbstractions.synchronize — Function
synchronize(::Backend)Synchronize the current backend.
KernelAbstractions.device — Function
device(backend::Backend)::IntReturn the 1-based index of the currently active device for backend.
KernelAbstractions.ndevices — Function
ndevices(backend::Backend)::IntReturn the number of devices available to backend.
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 deviceKernelAbstractions.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.
Kernel handles
KernelAbstractions.Kernel — Type
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.
KernelAbstractions.workgroupsize — Function
workgroupsize(kernel::Kernel)Return the static workgroup size type parameter of kernel (StaticSize or DynamicSize).
KernelAbstractions.ndrange — Function
ndrange(ctx)Return the launch ndrange as a tuple.
KernelAbstractions.backend — Function
backend(kernel::Kernel)Return the Backend that kernel was constructed for.
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_typed — Macro
@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))KernelAbstractions.@ka_code_llvm — Macro
@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))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.partition — Function
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.
KernelAbstractions.@context — Macro
@context()Access the hidden context object used by KernelAbstractions.
function f(@context, a)
I = @index(Global, Linear)
a[I]
end
@kernel cpu=false function my_kernel(a)
f(@context, a)
endKernelAbstractions.argconvert — Function
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.
KernelAbstractions.NDIteration.DynamicSize — Type
DynamicSizeMarker type indicating that a kernel's workgroup size or ndrange is chosen at launch time.
KernelAbstractions.NDIteration.StaticSize — Type
StaticSize{S}Marker type encoding a compile-time workgroup size or ndrange as a tuple S.