ctx.threads, ctx.warps, ctx.lanes
@launch(256): 256 threads in 8 warps of 32 lanes. All three counts are compile-time constants.
- not a whole warp
ctx.lanes threads; ctx.warps is
ctx.threads / ctx.lanes. The error is the compiler's own, from the example below.
A CTA is a fixed number of threads, and the hardware runs them in warps of a
fixed number of lanes. Three ctx fields give those numbers. The figure shows
a CTA regrouping into warps as its thread count changes.
| Field | Value | Known |
|---|---|---|
ctx.threads |
The CTA’s thread count, from @launch. 256 when the kernel has no @launch. |
At compile time |
ctx.lanes |
Lanes in a warp on the target: 32 on NVIDIA. | At compile time |
ctx.warps |
ctx.threads / ctx.lanes |
At compile time |
All three are uniform: every thread of every CTA sees the same value. They are also compile-time constants. The compiler folds them into the generated code as literals, so a loop bound or a stride written with them costs nothing at run time.
The normative definitions are in the spec under Indexing and synchronization.
Setting the thread count
Section titled “Setting the thread count”@launch sets ctx.threads. Its first argument is
the CTA size:
@launch(128)kernel counts(O: tensor<i32>[1, 4]) { O[0, 0] = i32(ctx.threads) // 128 O[0, 1] = i32(ctx.warps) // 4 O[0, 2] = i32(ctx.lanes) // 32 O[0, 3] = i32(ctx.blocks.y) // a runtime value, read from the launch}Every thread runs those four stores and every thread writes the same values,
so the result is well defined. ctx.blocks.y is there for contrast: it is the
grid’s extent, which is only known when the kernel is launched (see
ctx.block and ctx.blocks).
A whole number of warps
Section titled “A whole number of warps”ctx.warps requires ctx.threads to be a whole number of warps. On NVIDIA
that means a multiple of 32, and @launch enforces it directly: a thread count
that is not a positive multiple of 32 is rejected before any code is generated.
@launch(48)kernel half_a_warp(O: tensor<i32>[1, 1]) { O[0, 0] = i32(ctx.warps)}Since the check happens at @launch, a kernel can always rely on
ctx.threads == ctx.warps * ctx.lanes.
Where the counts show up
Section titled “Where the counts show up”The counts are what a kernel uses to split work over its threads without hard-coding the launch size:
| Pattern | Written as |
|---|---|
| One warp per output column | let col = ctx.block.x * ctx.warps + ctx.warp |
| A loop whose lanes split a row | for k in range(ctx.lane, K, ctx.lanes) |
| A loop whose threads split a row | for k in range(ctx.thread, K, ctx.threads) |
| A thread’s run of four, CTA-strided | let e = 4 * ctx.thread + i * 4 * ctx.threads |
The indices in those patterns, ctx.thread, ctx.warp and ctx.lane, are
the subject of the next page.