跳到正文
Zyphra Research·· 32 分鐘前AI 評分53

ZYFA:AMD MI355X 上快速且準確的 FP8 Attention

ZYFA: Fast and Accurate FP8 Attention on AMD MI355X

AI 導讀

Zyphra Research 介紹 ZYFA,這套面向 AMD MI355X 的因果前向注意力 kernels 支援 BF16 和 FP8,並附有吞吐與數值誤差測試。

正文
ZYFA vs AITER Attention Throughput on MI355X
Figure 1. ZYFA’s FP8 kernels achieve 1.16–1.76× the throughput of AITER’s on activations captured from three models, and ZYFA’s BF16 kernels achieve 1.01–1.02×. Bars give the throughput of each kernel on one layer per model at 16K tokens and sustained clocks, and the label above each pair gives AITER’s kernel time divided by ZYFA’s. At the MLA shape of DeepSeek-V2-Lite, AITER’s default kernel is Composable Kernel (CK) in FP8 and OPUS, a kernel built with AITER’s C++ template library, in BF16. Values are in Appendix A1.

Key Results

  • Speed on real activations: On activations captured from Mistral-7B, Qwen3-8B, and DeepSeek-V2-Lite at 16K tokens and timed at sustained clocks, ZYFA’s FP8 kernels achieve 1.16–1.76× the throughput of AITER’s default kernels, and ZYFA’s BF16 kernels achieve 1.01–1.02×1. The FP8 speedup is smaller than on Gaussian inputs because the clock of the MI355X depends on the data. AITER lets more probabilities underflow to zero, and zeros switch fewer bits, so AITER’s kernel runs at a higher clock.
  • Speed on kernel benchmarks: On Gaussian inputs at 32K tokens, ZYFA’s FP8 kernels achieve 1.26–1.37× the throughput of AITER’s at head dimension 128 and 1.99× at the head shape of DeepSeek’s multi-head latent attention (MLA). At head dimension 128 the FP8 kernel reaches 53% of the FP8 datasheet peak.
  • Kernel accuracy: At head dimension 128 the error of ZYFA’s FP8 kernels is within a few percent of that of AMD’s Composable Kernel (CK) and Triton kernels and 62–78% lower than that of AITER’s.
  • Full-model accuracy: Most of the FP8 error in a full model comes from quantizing the queries, keys, and values. On Mistral-7B a Hadamard rotation of Q and K halves this error and roughly halves the KL divergence from the BF16 model, at almost no extra time2.

We describe our methodology and its limitations at the end of this post. In short, all results come from one MI355X node, the kernels cover causal prefill only, and both our kernels and the baselines change quickly, so all results are point-in-time.

What this post covers

  • Constraints of FP8 Attention: the vector-pipe cost of the softmax, the software ordering of matrix instructions, and the precision and underflow of FP8 probabilities.
  • Kernel Design: lean P, the two-wave ping-pong schedule, and per-block scaling with normalization by the rounded row sum.
  • Throughput and Numerical Accuracy: captured activations, Gaussian inputs, the dependence of the clock on operand values, and FP8 error in full models.
  • Remaining Performance Gaps and Recommendations, Future Work, and Methodology and Limitations.
  • Appendix A: tables of the values plotted in the figures.

Introduction

Prefill, the forward pass over the prompt before a model generates its first token, spends a growing share of its time in attention as the context grows. FlashAttention keeps the score matrix in on-chip memory instead of high-bandwidth memory (HBM), so the bytes that attention moves to and from HBM grow linearly with the sequence length while its arithmetic grows quadratically. Prefill attention at long contexts is therefore compute-bound, and its speed depends on how well the kernel keeps the GPU’s matrix units busy3.

The MI355X executes 8-bit floating-point (FP8) matrix instructions at twice the peak rate of BF16, so FP8 attention could in principle take half the time of BF16 attention. In practice the gain is much smaller, and AITER’s FP8 kernel reaches a lower share of its potential peak than its BF16 kernel. FP8 also raises a question of accuracy, since attention is a very numerically sensitive block. DeepSeek-V3, for example, trains its linear layers in FP8 but keeps attention in higher precision.

ZYFA consists of causal forward attention kernels in BF16 and FP8 for the MI355X (CDNA4, gfx950). The kernels support head dimension 128 and the head shape of MLA in DeepSeek-V2, with a query-key dimension of 192 and a value dimension of 1284. Constraints of FP8 Attention on the MI355X describes the three obstacles that limit FP8 attention, and Kernel Design describes the technique in ZYFA’s FP8 kernels that addresses each one. We provide the full analysis in our technical report.

Constraints of FP8 Attention on the MI355X

Vector-Pipe Cost of the Softmax

FP8 halves the cycles of the two matrix products of attention, QKT and PV, which run on the matrix pipe5. The softmax between them runs on the vector pipe, which executes elementwise arithmetic at the same speed in FP8 and BF16, and an FP8 kernel must also convert every probability to 8 bits. Both pipes execute the instructions of waves, groups of 64 threads that issue instructions together. For one wave computing 32 queries against a block of 128 keys, the vector pipe needs 0.44× the cycles of the matrix pipe in BF16 but 0.94× in FP8 (Figure 2). An FP8 kernel therefore keeps the matrix pipe busy only if the softmax overlaps the matrix products almost perfectly.

Matrix and Vector Work of One Attention Block on MI355X
Figure 2. In FP8 with exact P, the vector pipe needs almost as many cycles as the matrix pipe, and with lean P about half. Bars give the cycles that one wave needs on each pipe for 32 queries against one block of 128 keys at head dimension 128, and the label above each panel gives the vector-pipe cycles divided by the matrix-pipe cycles. The matrix pipe computes the two matrix products and, in FP8, the row sum, and the vector pipe computes the row maximum and the probabilities P. Exact P evaluates the exponential, and lean P, ZYFA’s method, replaces it with one multiply-add.

Software Ordering of Matrix Instructions

On the MI355X a matrix instruction (MFMA) keeps running while the wave that issued it goes on to other instructions, and the hardware does not track when the MFMA finishes. NVIDIA’s Hopper and Blackwell GPUs track the completion of asynchronous matrix instructions in hardware. On the MI355X the order of the instructions alone must overlap the softmax with the MFMAs and keep every instruction from reading an MFMA result too early. In thread traces of AITER’s FP8 kernel, the matrix pipe is idle for 31.7% of the cycles while the softmax runs.

Precision and Underflow of FP8 Probabilities

The FP8 format of the probabilities, e4m3, keeps all 3 mantissa bits only for values of at least 2−6, the normal values. Smaller values lose precision, and values below 2−10 underflow to zero. Most probabilities of a long row lie far below the largest one. In layer 20 of Mistral-7B at 16K tokens, the largest probability of a 64-key block lies more than 8 binades (factors of two) below the row maximum for 91% of the pairs of a row and a block. Underflow also biases the output if the row sum adds up the unrounded probabilities, as in AITER’s FP8 kernel, because every probability that underflows to zero still counts in the row sum6.

Kernel Design

ZYFA’s FP8 kernels address each obstacle with one technique, which we call Lean P.

Lean P: Linear Approximation of the FP8 Code

A standard FP8 kernel evaluates each probability p = 2x, with x the score minus the row maximum in base-2 units, with an exponential instruction and then rounds it to e4m3. We call this exact P, and it takes 14 vector-pipe cycles per score. Lean P uses the fact that the 8-bit pattern of a positive e4m3 number, read as an integer and called its code, grows by 8 each time the value doubles7. The code of 2x is therefore close to 8x plus a constant, so lean P computes the code with one multiply-add and one conversion to an integer, in 6 cycles per score.

Lean P is an approximation. On the 68 captured layers at head dimension 128 it raises the error of an attention call to 1.22–1.36× that of exact P, which is still 0.26× the error of AITER’s kernel in geometric mean. ZYFA uses lean P by default at head dimension 128 and exact P at the MLA shape.

Two-Wave Ping-Pong Schedule in Assembly

Each SIMD, one of the four execution units of a compute unit, runs two waves of the FP8 kernel. In each phase one wave issues the matrix instructions of its block while the other computes FP8 probabilities, and the two waves swap roles after a barrier (Figure 3). FlashAttention-3 calls this arrangement ping-pong scheduling. Each phase lasts until both waves reach the barrier, so a phase takes as long as the slower of its two waves. With exact P the softmax wave arrives last, and the matrix pipe is busy for 60% of the cycles. With lean P the softmax wave arrives first, and the matrix pipe is busy for 86%.

Figure 3. Two waves on one SIMD take turns issuing matrix instructions and computing FP8 probabilities, with a barrier after each phase. With exact P the softmax wave reaches the barrier last, and the matrix pipe idles at the end of each phase. With lean P the MFMA wave reaches the barrier last, and the matrix pipe idles much less. Bar widths are mean cycles over 40 steady-state phases in thread traces, and the busy cycles of the matrix pipe are drawn together at the start of each phase. Exact P was traced in an earlier build of the same kernel.

Because both waves issue from the same SIMD, the overlap depends on the exact placement of every instruction, so a Python generator emits the FP8 kernels as assembly, which fixes the position of every instruction. On captured activations a version of the same kernel written in HIP, AMD’s C++ kernel language, needs 43–46% more cycles, and in the paper’s step-by-step comparison the assembly schedule gives the largest gain of any design step.

Per-Block Scaling and Normalization by the Rounded Row Sum

The FP8 kernels multiply the probabilities of each 64-key block by a power of two so that the largest probability of the block lies close to 1. The scaled FP8 matrix instruction of CDNA4 divides the same power of two back out in the PV product at no extra cost. The kernels also divide by the sum of the rounded probabilities, which one extra MFMA per block computes from the same values that multiply V. Each output is then a weighted average with weights that sum to one, and the extra MFMA adds 6% to the matrix cycles per block (Figure 4).

Figure 4. Per-block scaling keeps small probabilities representable in FP8. Without scaling, the probabilities of blocks far below the row maximum round to subnormal values or zero. ZYFA scales each 64-key block by a power of two before rounding, divides the factor back out in the PV product, and normalizes by the sum of the same rounded probabilities. The row of blocks is a schematic. The P error is the relative RMS error that rounding p adds to the attention output of Mistral-7B, over 12 PG-19 documents and all layers, heads, and rows.

Throughput

Throughput on Captured Activations at Sustained Clocks

We time each kernel on the queries, keys, and values that one layer each of Mistral-7B, Qwen3-8B, and DeepSeek-V2-Lite produces at 16K tokens, which we call captured activations. Under sustained load the MI355X lowers its shader clock (SCLK), so each run launches one kernel back to back for seconds and is timed only after the clock has settled. ZYFA’s FP8 kernels achieve 1.16×, 1.17×, and 1.76× the throughput of AITER’s on the three layers (Figure 1). The ratio is largest on DeepSeek-V2-Lite because CK, AITER’s default FP8 kernel at the MLA shape, issues an older matrix instruction with half the throughput per clock. Inside full forward passes at 16K tokens, the median FP8 ratio over all layers is 1.26× on Mistral-7B and 1.25× on Qwen3-8B.

Throughput on Gaussian Inputs

With kernel-only timing on Gaussian inputs, as in typical kernel benchmarks, ZYFA’s FP8 kernels achieve a higher throughput than AITER’s at every sequence length from 1K to 32K tokens, and ZYFA’s BF16 kernels achieve 1.00–1.03× the throughput of AITER’s at 16K and 32K tokens (Figure 5). In BF16 the matrix products take more than twice as many cycles as the softmax, so they limit the kernel time of both libraries. In FP8 with exact P the softmax wave reaches each barrier after the MFMA wave, so the softmax limits the kernel time instead.

ZYFA Throughput Relative to AITER on Gaussian Inputs
Figure 5. ZYFA’s FP8 kernels achieve a higher throughput than AITER’s at every sequence length on Gaussian inputs. Each line gives ZYFA’s throughput divided by AITER’s, kernel-only, for one head shape and attention type. Solid lines with filled circles are multi-head attention, and dotted lines with open squares are grouped-query attention. Dark orange lines are head dimension 128, and light orange lines are the MLA shape, with a query-key dimension of 192. The dashed line at 1× is AITER, whose kernel at the MLA shape is CK in FP8 and OPUS in BF16. Values are in Appendix A2.

Dependence of the Shader Clock on Operand Values

The sustained clock of the MI355X depends on the values in its arithmetic units. Each bit that switches between 0 and 1 draws current, so the clock falls as more operand bits change from one instruction to the next (Figure 6)8. A loop of FP8 MFMAs runs at 2.34 GHz with constant operands and at 2.07 GHz with operands that change every instruction.

Figure 6. On the MI355X the sustained clock depends on the data. Operands that switch more bits draw more current and lower the clock, so a kernel that needs fewer cycles can still lose time to a lower clock.

On the captured Mistral-7B layer, ZYFA’s FP8 kernel needs fewer cycles than AITER’s but runs at 1,628 MHz, against 2,108 MHz for AITER (Figure 7). AITER writes subnormal values or zeros for the blocks far below the row maximum, and these values switch few bits, while ZYFA’s per-block scaling keeps them normal. On Gaussian inputs, which make attention nearly uniform, both kernels write only normal values, and ZYFA’s speedup on this layer grows from 1.16× to 1.30×. Throughput on random inputs therefore does not reliably predict throughput on real model activations.

Cycles and Clock of FP8 Attention on Mistral-7B Layer 20
Figure 7. ZYFA’s FP8 kernel needs fewer cycles than AITER’s but runs at a lower clock on captured activations. In the left panel, each point gives the cycles per launch and the sustained shader clock of one FP8 kernel on Mistral-7B layer 20 at 16K tokens. Filled markers are captured activations, open markers are Gaussian inputs of the same shape, and points on a dashed line take the same time. The right panel gives ZYFA’s cycles, clock, and kernel time divided by AITER’s, with filled bars for captured activations and outlined bars for Gaussian inputs. Values are in Appendix A1.

Numerical Accuracy

Error of a Single Attention Call

To isolate the error that a kernel itself adds, we compare its output with fp64 attention on the same quantized inputs. In FP8 at head dimension 128 the errors of ZYFA, CK, and Triton differ by a few percent, while ZYFA’s error is 0.22× and 0.38× that of AITER’s FP8 kernel on Mistral-7B and Qwen3-8B (Figure 8). Most of AITER’s extra error comes from dividing by the sum of the unrounded probabilities. In a full Mistral-7B model, the KL divergence from a model with fp64 attention is 3.4 × 10−3 with ZYFA’s FP8 kernel and 5.9 × 10−3 with AITER’s.

FP8 Output Error of One Attention Call in Every Layer
Figure 8. ZYFA’s FP8 error is close to that of CK and Triton at head dimension 128 and below that of AITER’s default kernel in 94 of 95 layers. Each point gives the relative RMS error, on a log scale, of one kernel’s attention output in one layer against fp64 attention on the same FP8 inputs, captured on WikiText-2 at 4K tokens. At the MLA shape of DeepSeek-V2-Lite, AITER’s default kernel is CK, and Triton does not support FP8. Means over layers are in Appendix A3.

FP8 Error in Full-Model Inference

Most of the FP8 error in a full model comes from quantizing the inputs (Figure 9). On Mistral-7B, quantizing Q, K, and V adds 8 times the error of quantizing the probabilities with lean P, and Q and K add more error than V. A Hadamard rotation of Q and K over the head dimension leaves QKT unchanged but spreads large channels over all channels, which roughly halves the input error. On Mistral-7B the rotation lowers the KL divergence from the BF16 model from 45.7 to 21.9 times the upper end of the BF16 noise band9, and attention with the rotation takes 1.001× the all-FP8 time. Keeping the most sensitive heads in BF16 is a worse use of time. On Mistral-7B and Qwen3-8B even 90% of heads in BF16 does not reach the noise band, and that mix takes longer than all-BF16 attention.

Error Added by Each Stage of One FP8 Attention Call
Figure 9. Quantizing Q, K, and V adds most of the FP8 error of one attention call, and a Hadamard rotation of Q and K roughly halves it. Bars give the relative RMS error that each stage adds to the output of ZYFA’s FP8 kernel, pooled over all layers, without rotation (dark orange) and with rotation (light orange), and the total combines the stages in quadrature. Values are in Appendix A4.

Remaining Performance Gaps and Recommendations

At 32K tokens on Gaussian inputs, ZYFA’s FP8 kernel at head dimension 128 reaches 53% of the datasheet peak10. Three gaps remain. The sustained clock stays well below the 2.4 GHz that the datasheet peak assumes, the matrix pipe idles at the transitions between query tiles, and with lean P the MFMA wave also computes the row maximum and the rescale of the output, so the MFMA wave now takes longer than the softmax wave and limits the length of each phase. For kernel developers we recommend the following:

  • Measure on real activations at sustained clocks, since the clock speed is dependent on input data.
  • Target the quantization of the inputs, for example with a Hadamard rotation of Q and K, before the quantization of the probabilities.
  • Reduce the vector work per score before adding matrix throughput. A faster matrix format alone does not help while the softmax is the bottleneck.
  • Keep packed FP32 instructions out of a softmax that runs next to MFMAs. A packed instruction applies one operation to two values per thread, and a packed FP32 instruction stalls while an MFMA executes on the same SIMD.

Future Work

We will continue to improve ZYFA with a focus on the following areas:

  • Closing the remaining gaps above.
  • Extending lean P to other formats whose code grows by a fixed step each time the value doubles, such as e5m2 and FP4.
  • Supporting decoding, paged KV caches, and batches of variable length.

The source code is available at github.com/Zyphra/zyfa, and the paper is on arXiv.

Methodology and Limitations

  • All experiments run on one node with eight MI355X GPUs, with the default automatic clock policy and a total board power (TBP) limit of 1,400 W, on ROCm 7.2.3. Our baselines are the AITER 0.1.19 default kernel selections.
  • Kernel-only timing on Gaussian inputs reports the mean device time of 100 launches after 500 warm-up launches. Sustained timing runs 1 s of warm-in launches and then times a window of at least 3 s.
  • The full-model study covers three models, PG-19 text, and no downstream tasks.
  • The kernels cover the causal forward pass for equal query and key lengths. They do not cover decoding, paged KV caches, or batches of variable length.
  • The data-dependent clock may differ with other firmware or power settings, and our kernels and the baselines change quickly, so all results are point-in-time.

Appendix A: Data Tables

The tables below contain the values plotted in the figures. Find the full results in our technical report.

A1. Captured activations at sustained clocks (Figures 1 and 7)

One layer per model at S = 16K (Mistral-7B layer 20, Qwen3-8B layer 18, DeepSeek-V2-Lite layer 13). The time ratio is AITER’s time divided by ZYFA’s time, so values above 1 mean that ZYFA takes less time. The Gaussian ratio is the same ratio on Gaussian inputs of the same shape.

ModelPrecisionZYFA (TFLOP/s)AITER (TFLOP/s)Time ratioGaussian ratio
Mistral-7BFP82,5372,1911.161.30
Qwen3-8BFP82,4952,1301.171.29
DeepSeek-V2-LiteFP82,3371,3261.761.84
Mistral-7BBF161,2231,1961.021.03
Qwen3-8BBF161,2021,1911.011.02
DeepSeek-V2-LiteBF161,2851,2761.011.01

Kernel times and sustained shader clocks (SCLK) of the same runs.

ModelPrecisionZYFA (µs)AITER (µs)ZYFA SCLK (MHz)AITER SCLK (MHz)
Mistral-7BFP88671,0031,6282,108
Qwen3-8BFP88811,0321,6292,048
DeepSeek-V2-LiteFP85881,0361,7422,219
Mistral-7BBF161,7981,8381,5901,446
Qwen3-8BBF161,8301,8471,5861,438
DeepSeek-V2-LiteBF161,0691,0771,4491,403

A2. Gaussian kernel benchmarks (Figure 5)

Throughput of ZYFA divided by that of AITER’s default kernel, kernel-only on Gaussian inputs at B = 1. At the MLA shape (query-key dimension 192) the FP8 baseline is CK. The last column gives ZYFA’s and AITER’s throughput at 32K.

AttentionPrecisionHead shape2K16K32KTFLOP/s at 32K (ZYFA / AITER)
MHAFP81281.441.341.372,672 / 1,944
MHAFP8MLA (192)2.021.891.992,464 / 1,237
GQAFP81281.421.281.262,563 / 2,027
GQAFP8MLA (192)1.941.821.882,374 / 1,262
MHABF161281.051.031.021,265 / 1,236
MHABF16MLA (192)1.041.011.001,330 / 1,329
GQABF161281.071.021.021,310 / 1,283
GQABF16MLA (192)1.071.001.001,376 / 1,378

A3. Error of one attention call (Figure 8)

Relative RMS error against fp64 attention on the same quantized inputs, mean over the layers of each model. In parentheses, each kernel’s error divided by AITER’s error in the same layer, averaged over layers, so a ratio can differ from the ratio of the two means. AITER’s Triton kernel does not support FP8 at the MLA shape.

ModelPrecisionZYFAAITERCKTriton
Mistral-7BFP81.20e-02 (0.22)6.41e-021.16e-02 (0.22)1.14e-02 (0.21)
Qwen3-8BFP89.11e-03 (0.38)3.17e-028.94e-03 (0.37)8.86e-03 (0.37)
DeepSeek-V2-LiteFP88.36e-03 (0.82)1.02e-021.02e-02 (1.00)—
Mistral-7BBF161.81e-03 (1.02)1.77e-031.98e-03 (1.12)1.81e-03 (1.02)
Qwen3-8BBF161.75e-03 (1.01)1.73e-031.94e-03 (1.12)1.75e-03 (1.01)
DeepSeek-V2-LiteBF161.94e-03 (1.00)1.94e-031.96e-03 (1.01)1.77e-03 (0.91)

A4. Error of one FP8 call by stage (Figure 9)

Relative RMS error of ZYFA’s FP8 kernel, split into input quantization (Q, K, and V), probability quantization (P), and the rest of the kernel’s arithmetic. ZYFA uses lean P on Mistral-7B and Qwen3-8B and exact P at the MLA shape of DeepSeek-V2-Lite.

ModelRotationInputPRestTotal
Mistral-7BNone8.3e-021.0e-025.0e-038.3e-02
Mistral-7BHadamard4.4e-021.0e-024.8e-034.5e-02
Qwen3-8BNone1.1e-011.0e-024.7e-031.1e-01
Qwen3-8BHadamard5.6e-029.9e-034.5e-035.7e-02
DeepSeek-V2-LiteNone9.5e-028.1e-034.4e-039.5e-02
DeepSeek-V2-LiteHadamard5.6e-028.1e-034.3e-035.6e-02

Footnotes

1 Mistral-7B-v0.3 layer 20, Qwen3-8B layer 18, and DeepSeek-V2-Lite layer 13, with the queries, keys, and values after the rotary position embedding (post-RoPE) on WikiText-2 text at 16K tokens. The baseline is the kernel that AITER 0.1.19 selects by default, which is Composable Kernel (CK) for FP8 at the MLA shape. ↩

2 The rotation is fused into the pass that quantizes Q, and K and V are quantized as they are written to the KV cache. On Mistral-7B, attention with the rotation takes 1.001× the time of all-FP8 attention. ↩

3 Our Wave 2 inference post measures the rooflines of the MI355X, which give the highest throughput that a kernel can reach at each arithmetic intensity (FLOPs per byte moved to or from HBM), and the peak matrix throughput of each data type. ↩

4 MLA caches one compressed latent vector per token instead of per-head keys and values. Prefill expands the latent vectors into per-head keys and values and runs standard attention with these dimensions. Decoding uses a different form of MLA that ZYFA does not cover. ↩

5 The matrix pipe is the matrix unit of a SIMD, and the vector pipe is its vector unit. Each compute unit of the MI355X has four SIMDs. The matrix pipe executes matrix instructions, and the vector pipe executes elementwise arithmetic such as the exponentials and FP8 conversions of the softmax. ↩

6 Dividing by the unrounded sum treats every probability that underflowed to zero as a key whose value vector is zero, so each output shrinks slightly toward zero. Dividing by the sum of the rounded probabilities instead gives a weighted average, with weights that sum to one, of the values whose probabilities survive rounding. ↩

7 For example, the codes of 1, 2, and 4 are 56, 64, and 72, and the code 60, halfway between 56 and 64, stands for 1.5. ↩

8 Each bit that switches charges or discharges a small capacitance, so the current that the chip draws grows with the number of bits that change per cycle. Total board power stays below its 1,400 W limit in these runs, and the telemetry points to a fast-acting limit on current rather than on average power. ↩

9 The BF16 noise band is the range of KL divergences no larger than that between two models with equally correct BF16 kernels, ZYFA’s and AITER’s, which round in different orders. ↩

10 The datasheet peak assumes that a matrix instruction issues on every cycle at the 2.4 GHz peak clock. Our Wave 2 inference post compares the datasheet peak of each data type with the throughput that the MI355X reaches in practice. ↩

來源:Zyphra Research · zyphra.com