Preserve sequence identity and global positions across tiles
Derive packed causal eligibility, unequal-length alignment, and empty-row behavior from logical positions.
On this page
The question for today
What you’ll understand
- Build a causal mask from sequence IDs and logical positions.
- Count the difference between packed and accidentally flattened attention.
- Check non-square causal conventions against pinned APIs.
A memory offset is not necessarily a token’s position in its own sequence. Packed batches concatenate different sequences to avoid padding, and attention tiles may begin in the middle of one sequence or cross a storage boundary. Applying a triangular rule to tile-local indices or to the entire packed array can therefore allow forbidden connections or suppress valid ones. The mask must be derived from the logical query/key identities and positions that define the model. Efficient layout is useful only if it preserves those identities through forward, recomputation, and backward accumulation.
Separate physical offsets from logical coordinates
For packed self-attention, attach a sequence identifier and an in-sequence position to every query and key. An allowed causal pair has matching sequence IDs and a key position no greater than the query position. A cumulative-length array can locate each sequence’s segment in storage, but the array’s global offset must not become the causal position by accident. Tiles should obtain their logical coordinates before comparing positions, including partial boundary tiles.
Padding is another exclusion rule rather than an ordinary token whose score happens to be small. A correct padded reference combines real-token validity with the causal relation. Packing should reproduce the same outputs on valid tokens after unpacking, subject to numerical tolerance. If a tile extends beyond a sequence’s end, its out-of-range lanes must not enter the softmax maximum, denominator, or gradient reductions. Checking only the final output shape cannot detect these internal leaks.
Non-square causal APIs need a declared alignment
During cached decoding, a short query segment can represent the final positions of a longer key sequence. A bottom-right alignment then allows key index j up to i+Nk−Nq for zero-based query index i. That differs from a top-left triangular rule j≤i. Both rules are coherent for different coordinate interpretations, so an API flag named causal is insufficient to establish which logical positions were intended.
FlashAttention 2.8.3 documents bottom-right alignment for unequal query and key lengths, with zero output for all-masked rows. PyTorch 2.8 scaled_dot_product_attention documents upper-left alignment for its non-square is_causal flag. Construct an explicit mask or choose the correct documented bias when translating between interfaces; do not assume matching flags imply matching operators. Test both Nq<Nk and Nq>Nk, since the latter can create completely empty rows under bottom-right alignment.
Test boundaries where layouts disagree
Use short ragged sequences with deliberately different value ranges so cross-sequence leakage becomes obvious. Compare a packed kernel with separate per-sequence references, then compare gradients after gathering them back to the original storage order. Include lengths just below and above tile boundaries, empty batches if the API permits them, and noncontiguous layouts where supported. These inputs target indexing mechanisms more directly than a large random square matrix with no padding.
Backward recomputation must rebuild the same logical mask, including position offsets from a cache and any positional biases. A forward-only test can miss a gradient that writes into a neighboring packed sequence. Likewise, dropout replay needs stable logical indexing if it is required to match across layouts. Preserve cumulative lengths, position metadata, and mask conventions with the test case so a numerical disagreement can be distinguished from an intentional change of operator.
Work it through
A flattened triangle leaks six cross-sequence pairs
Pack sequence A of length 2 and sequence B of length 3, with cumulative offsets (0,2,5). Their scalar values are (1,3) and (10,20,30). All allowed logits are zero. Compare correct per-sequence causal attention with an incorrect causal triangle over all five packed offsets.
Correct causal pair counts are 2×3/2=3 for A and 3×4/2=6 for B, totaling 9. A single five-token triangle contains 5×6/2=15 pairs, so it introduces 6 forbidden cross-sequence pairs from B queries to A keys.
The first query of B has in-sequence position zero and should attend only to its own first key, giving output 10. Its packed offset is 2; a flattened triangle instead admits values (1,3,10), averaging them to 14/3≈4.6667.
The last B query should average (10,20,30) to 20. Flattened attention averages all five values to 64/5=12.8. Equal zero logits make both errors pure mask/layout mistakes rather than floating-point or score-scale effects.
The packed array has the right total token count, but its global triangular mask defines a different operator. Sequence identity must survive packing.
The living notebook
Explore the model
Interactive lab · cache
Storage layout changes reuse, not sequence identity
Reset, set Cache capacity C to 4 and Request stride s to 4. Predict a four-address period, 12 hits in the initial 16 requests, and 13 after Step. Reset, set C to 4 and s to 1; predict a 16-address period and zero hits initially and after one Step. The stream caps at 4096 requests. This is only a layout/reuse analogy; it has no sequence IDs, causal mask, or attention computation.
- Hits / requests
- 0 / 16
- Hit rate
- 0%
- Address-stream period
- 16 addresses
A fully associative LRU cache starts empty and requests address (i·s) mod 16. Hits move an address to the newest position; misses evict the oldest when full. Playback starts with 16 requests and adds two per second, capped at 4096 explicitly simulated requests. This toy stream is not a processor-cache benchmark. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forA favorable storage-access pattern says nothing by itself about correct logical masking.
Pause & reason
Check your understanding
Your turn
Try it yourself
For one sequence with Nq=2 and Nk=5, count allowed pairs under the stated bottom-right rule and under top-left alignment. Which key indices are allowed in each bottom-right row?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Substitute Nk−Nq=3 into j≤i+3, using zero-based indices.
Compare with the solution
Bottom-right row 0 allows keys 0,1,2,3 and row 1 allows 0,1,2,3,4, totaling 4+5=9 pairs. Top-left row 0 allows only key 0 and row 1 keys 0,1, totaling 3 pairs. The difference comes from the query-position interpretation; it cannot be repaired by a tolerance adjustment.
Keep the idea
Take it with you
- Causality uses sequence identity and logical token position.
- Unequal-length causal alignment is API-specific and versioned.
- Packed forward and backward must reproduce the same per-sequence mask.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.