Repository navigation
KernelInterface 1.0: a better API for writing kernels #810
Description
KernelInterface right now is mostly about the contract between KernelAbstractions and the back-ends. KI now validates and sizes launches itself, so back-ends implement KI.launch with checked 3-D sizes instead of the whole launch path. The semantics that differed between back-ends are written down (work-group limits, typed index queries, sub-groups, capability defaults), and a testsuite checks them. On top of that, #801 lets KA launch @kernel kernels on any KI back-end, which shrinks CUDA's KA extension from 127 to 37 lines.
KI is also meant for users who want to write kernels without KA's macros, with more control or less overhead. 0.3 doesn't serve them as well yet. Some of the fixes are breaking, but since we know of no direct KI users outside the back-ends yet, I propose releasing KI 0.4 in a couple of weeks. It should be out before KA 0.10 is registered, so that KA 0.10 only ever targets 0.4.
Proposed for 0.4
-
ndrangehas different semantics in KA and KI. KA masks work-items outside thendrangeby default. KI rounds it up to whole work-groups and leaves bounds checks to the kernel. That is the right choice at this level: the kernel can guard its memory accesses while all work-items still reach barriers. But without an explicit bounds check, a kernel ported from KA and launched withKI.@launch ndrange=...can access memory out of bounds when the range is padded. Rename the keyword, or at least make the difference hard to miss in the docs. -
No private-memory primitive in KI. KI has
localmemory, but per-work-item scratch storage is only exposed throughKA.Scratchpad(ctx, T, Val(dims)), which takes KA's hidden context. Back-ends override it and ignore the context (CUDA returns anMArray, PoCL a stack allocation). Add a portable KI primitive without the context, with defined lifetime and initialization, and lower@privateto it. Back-ends then no longer need a KA hook for it. -
@Consthas no KI equivalent.@Constpromises that the memory an argument refers to is neither written by the kernel nor aliased by other arguments. Back-ends implement it with anAdaptrule forKA.ConstAdaptor. KI users could use the same, e.g. to getld.global.ncloads on CUDA. This could become a KI primitive with the same promise, leaving the annotation and the argument traversal in KA. It is less clear-cut than private memory, so it could also wait. -
supports_subgroupsimplies a fixed width. It covers the sub-group queries andsub_group_barrier, and also promises that every compiled kernel uses the width reported bysub_group_size(backend). A back-end that can't guarantee the width has to report no sub-group support at all, which is what happens on OpenCL with NVIDIA. Kernels that read the width at run time don't need that guarantee. Split it into support for sub-group operations and a separate guarantee of a fixed width. -
Launch keywords. Keywords that KI doesn't know go to
KI.launchon aKernelcall, but tokernel_functiononKI.@launch. So back-end launch options such as CUDA'sstreamneedlaunch=falseand a second call. Adding a KI keyword later also changes what happens to a back-end keyword of the same name: KernelInterface 0.3: a tighter back-end contract #800 proposes dynamic local memory as aKernelkeyword, and calling itshmemwould take over a keyword that is currently forwarded to CUDA. Either separate compiler and launch options explicitly (e.g.launch_options=(; stream)), or add a rule that new KI keywords only come in breaking releases.
Non-breaking (can go in any release)
- An example that uses KI without KA. Both KI examples load KA, and
examples/histogram.jltakes its atomics from KA. - Make clear that
supports_atomicsdoes not guarantee local-memory atomics, even though the histogram example uses them. - A guide for kernel authors that brings the existing contracts together: for each function, who calls it and who implements it, and which behavior is unspecified (shuffles from lanes that don't exist, how work-items map onto sub-groups). It should also point out that the index queries default to
Int. Since Launch @kernel kernels on N-d grids, indexing in 32 bits #797, KA picksInt32by itself when the indices fit, so a KI port of a KA kernel can end up with 64-bit index arithmetic where KA used 32-bit.
Activity
maleadt commentedon Sep 30, 2026
cc @christiangnrd
christiangnrd commentedon Sep 30, 2026
Proposal points:
@private? Don't variables always survive across barriers now? Edit: I forgot that this is also used for thread-local containers.COMPILER_ARGSin 0.3?Non-breaking stuff:
vchuravy commentedon Oct 5, 2026
Yeah CUDA, should also now move to an
alloca[-]KernelInterface 0.4: a better API for writing kernels[/-][+]KernelInterface 1.0: a better API for writing kernels[/+]