Jashwanth Bamidi

DSP & GPU Coursework

CUDA Attention Kernels

GPU kernels that move attention tensors between layouts. They do almost no arithmetic, so speed comes down to how threads touch memory.

Role
Kernel implementation
When
2026
Tools
CUDA, C++

4 tokens, 2 heads, 3 features each, in memory order.

Before: [token][head][feature]

After: [head][token][feature]

Token 2, head 1, feature 0: input address (2·2 + 1)·3 + 0 = 15, output address (1·4 + 2)·3 + 0 = 18.

Head 1 is shaded. After the permutation each head's values sit together.

Illustrative. Where an element moves. Select a value to see where it moves. A tiny illustrative tensor, not the real shapes or kernel. A tiny tensor shown in memory order before and after permutation; selecting a value highlights both positions.

What I did

I implemented permute and unpermute kernels for Q, K and V, choosing thread mappings for coalesced access, staging through shared memory and sizing blocks with occupancy in mind.

Result

  • Permute and unpermute kernels implemented with coalescing, tiling and warp divergence considered.
  • No GPU model, tensor shapes, timings or speedup are given, so none are claimed.
  • Layout work around attention, not a fused or FlashAttention-style kernel; no Tensor Cores.

Design decision

Coalesce the expensive side

One side of a permutation is always strided. Mapping threads so a warp's accesses share transactions, and staging through shared memory, keeps both sides contiguous.

[head][seq][feat]Q, K, V[seq][head][feat]Permutemy kernelPer-head attentionnot part of this workUnpermutemy kernelNext layer[seq][head][feat]
  1. Q, K, V[seq][head][feat]
  2. Permutemy kernel
  3. Per-head attentionnot part of this work
  4. Unpermutemy kernel
  5. Next layer[seq][head][feat]
Illustrative. Where the kernels sit. The middle block is context, not part of this work. Q, K and V are permuted to head-major layout, the per-head attention runs, and the result is unpermuted for the next layer.

More detail

Why permute

Attention splits each token's features into heads. Q, K and V move from token-major to head-major layout for the per-head work, and the output moves back. Values stay the same; addresses change.

Index arithmetic

A [sequence][head][feature] array becomes [head][sequence][feature]: input index (s·H + h)·D + d, output index (h·S + s)·D + d.

Other design decisions
  • Occupancy is a means

    Size blocks against register and shared-memory use. Instead of: maximising threads per block.

    More resident warps only help if memory traffic is the bottleneck.