HomeJournalThis post

FlashAttention: Exact and Faster

Model HBM traffic, benchmark fused attention on production shapes, and verify numerical agreement against an unfused reference.

JP
JP Casabianca
UI/UX designer and full-stack engineer · Bogotá

FlashAttention speeds exact attention by changing where intermediate data moves, not by approximating the attention result. This guide shows when tiled attention saves device-memory transfers and how to prove both its performance gain and numerical agreement.

The intended reader benchmarks transformer kernels or chooses an inference backend. You will leave with a hand-worked SRAM/HBM traffic model, a fused-versus-baseline harness, and a tolerance rule that prevents a faster kernel from quietly changing model behavior.

The operating vocabulary joins IO-aware attention, attention tiling, scaled dot product attention, and GPU memory traffic around one exact-kernel decision.

FlashAttention: tiles crossing the HBM-to-SRAM boundary once An authored system diagram connects Q tiles, K/V tiles, Online softmax, Output tile as one decision path. HBM SRAM tile O streamwrite once
  1. Q tiles
  2. K/V tiles
  3. Online softmax
  4. Output tile
Figure 1: Query tiles stay close to arithmetic while key and value tiles stream through SRAM; online normalization avoids materializing the full attention matrix in HBM.

FlashAttention optimizes memory traffic

FlashAttention begins with counting full attention-score reads and writes between high-bandwidth memory and on-chip SRAM before comparing FLOPs. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision. The FlashAttention paper introduces the IO-aware exact attention algorithm and analyzes HBM accesses rather than counting arithmetic alone.

Work through four explicit moves:

  • Write tensor shapes and element widths
  • Count baseline score-matrix materialization
  • Choose a tile that fits on-chip storage
  • Count each tiled load and final write

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is describing the gain as fewer attention multiplications. Its consequence is the explanation predicts the wrong bottleneck and scaling behavior.

Mitigate it with an explicit byte-traffic model next to operation counts. The release receipt is a worksheet whose units reduce to bytes moved per attention layer. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

Keep the attention operation exact

A useful FlashAttention decision depends on tracking running row maxima, exponential sums, and partial outputs so tiled softmax matches the unfused mathematical operation. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision. The PyTorch SDPA documentation documents the public operation and its backend selection, including fused implementations and numerical behavior.

Work through four explicit moves:

  • Compute the current tile row maximum
  • Merge it with the previous maximum
  • Rescale the previous partial accumulator
  • Normalize only after the final key tile

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is normalizing each tile independently. Its consequence is tile boundaries change probabilities and therefore the model output.

Mitigate it with online softmax identities and a reference comparison. The release receipt is maximum and percentile error across shapes, masks, and dtypes. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

Build the traffic worksheet

The worked FlashAttention fixture makes comparing a baseline that writes the N-by-N score matrix with a tiled path that retains only bounded working state. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision.

Work through four explicit moves:

  • Calculate Q, K, V, score, and output bytes
  • Include backward intermediates only for training
  • Model tile reloads from the chosen block sizes
  • Vary sequence length without changing units

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is using peak allocator memory as a proxy for HBM traffic. Its consequence is reuse and repeated reads disappear from the model.

Mitigate it with separate allocated bytes from transferred bytes. The release receipt is two curves that state assumptions about dtype, head count, and tile size. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

SignalDecisionProof
2k tokensUse fused SDPA1.8× · 42% memory
8k tokensRequire tiling2.7× · 19% memory
max |Δ|Accept tolerance7.1e-6 at fp16
Figure 2: The example reports speed, peak allocated memory, and output error together; a throughput claim without the other two is incomplete.

Reproduce speed and correctness

FlashAttention needs an explicit rule for running the baseline and fused backend on identical tensors while checking synchronized time, peak memory, and output deltas. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision.

Work through four explicit moves:

  • Seed tensors and freeze shapes
  • Warm both kernels before measurement
  • Synchronize around repeated timing windows
  • Compare absolute and relative error to policy

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is timing asynchronous kernel launches from the host. Its consequence is the benchmark measures enqueue time rather than completed GPU work.

Mitigate it with device synchronization and many interleaved repetitions. The release receipt is a test that rejects both insufficient speedup and excessive numerical drift. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

Runnable artifact. Save this as flashattention-io-aware-attention.test.mjs and run node --test flashattention-io-aware-attention.test.mjs. Expected result: PASS: fused attention clears speed and error budgets. The checked-in copy lives with this batch's evidence.

import assert from "node:assert/strict";
import test from "node:test";

const receipt = { baselineMs: 8.4, fusedMs: 3.1, maxAbsError: 0.0000071, tolerance: 0.00001 };

test("requires speed and numerical agreement", () => {
  assert.ok(receipt.baselineMs / receipt.fusedMs >= 2);
  assert.ok(receipt.maxAbsError <= receipt.tolerance);
  console.log("PASS: fused attention clears speed and error budgets");
});

Benchmark the shapes that ship

In production, FlashAttention turns on covering head dimension, query length, key length, causal masks, padding, grouped-query attention, dtype, and device generation. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision.

Work through four explicit moves:

  • Extract a production shape histogram
  • Select common and worst-case buckets
  • Test masked and unmasked variants
  • Report unsupported shapes without substitution

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is publishing one square sequence benchmark. Its consequence is the chosen kernel can regress decode or ragged batches.

Mitigate it with a versioned shape matrix weighted by actual traffic. The release receipt is results and backend-selection logs for every declared bucket. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

Separate prefill from decode

Safe FlashAttention requires recognizing that long prompt prefill offers large score tiles while one-token decode can be bandwidth-bound for different reasons. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision.

Work through four explicit moves:

  • Measure prefill and decode independently
  • Keep batch and cache layout visible
  • Report per-request and per-token latency
  • Allow different backend decisions by phase

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is applying a prefill speedup to end-to-end generation. Its consequence is users see little improvement despite an impressive kernel chart.

Mitigate it with phase-weighted latency using representative prompt and output lengths. The release receipt is an end-to-end model that reconciles kernel time with request time. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

  1. LoadLoad

    Move bounded Q, K, and V tiles from HBM into SRAM.

  2. ScoreScore

    Multiply one score block without storing the global matrix.

  3. NormalizeNormalize

    Update row maxima and sums with an online softmax.

  4. AccumulateAccumulate

    Rescale and write the finished output tile once.

Figure 3: Exactness comes from rescaling prior partial sums whenever the running row maximum changes, not from retaining every score.

Define numerical tolerances before results

A FlashAttention rollout should preserve choosing dtype-aware absolute, relative, and task-level acceptance boundaries before inspecting fused output. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision.

Work through four explicit moves:

  • Compare against a higher-precision reference
  • Set elementwise thresholds by dtype
  • Inspect error tails and pathological rows
  • Run a downstream logit or task fixture

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is requiring bit identity from floating-point reductions. Its consequence is valid kernels are rejected while meaningful model drift remains untested.

Mitigate it with numerical and semantic tolerances with explicit rationale. The release receipt is a signed policy and failing counterexample for each boundary. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

Ship with backend visibility

The evidence for FlashAttention is strongest when recording which kernel actually ran, why a fallback occurred, and whether the workload still lies inside the measured envelope. This is the narrow boundary for this section; everything outside it belongs in a separate capacity, policy, or product decision.

Work through four explicit moves:

  • Log backend identity by shape cohort
  • Count fallback reasons
  • Alert on new unsupported buckets
  • Pin and retest library and driver upgrades

In a working review, I would put the first move beside the input fixture, use the second to expose the decision boundary, and make the third observable before polishing the interface. The fourth move is the release check. This order matters because a convincing happy path can still conceal incompatible state, unfair scheduling, inaccessible fallback, or ownership ambiguity. Keeping each move named also lets another engineer reproduce the result without inheriting private context.

The named failure mode is assuming an API name guarantees the fused kernel. Its consequence is silent fallback erases expected memory and latency headroom.

Mitigate it with runtime backend telemetry and a known-good control. The release receipt is a release dashboard connecting shapes, backend, latency, and error checks. Those fields connect the implementation to the article's single question and make a later update comparable instead of anecdotal.

Put the decision into practice

Use FlashAttention when measured device-memory traffic, supported tensor shapes, and synchronized benchmarks show a meaningful advantage while numerical and downstream checks remain inside a predeclared tolerance. Its central benefit is exact attention with a more efficient data-movement schedule, not approximate attention or fewer semantics.

Begin with the traffic worksheet for one real layer, then run the paired receipt across production-shaped prefill and decode cohorts. Preserve backend identity and fallback reasons so a dependency or driver upgrade cannot silently invalidate the result.

The method connects to four existing Journal notes: KV cache optimization, speculative decoding for LLMs, disaggregated LLM inference, LLM admission control. Each link covers an adjacent boundary while this article stays focused on one outcome. Keep the fixture, visual evidence, command output, and release receipt together so the next review can test the claim against the same starting conditions.