Skip to content

[hd256] Improve forward kernel with exp2 FMA emulation (3% to 9% performance gain) - #2488

Merged
Johnsonms merged 2 commits into
Dao-AILab:mainfrom
Johnsonms:Johnsonms/exp2-emu-hd256-v2
Apr 28, 2026
Merged

Johnsonms merged 2 commits into
Dao-AILab:mainfrom
Johnsonms:Johnsonms/exp2-emu-hd256-v2

Conversation

@Johnsonms

@Johnsonms Johnsonms commented Apr 23, 2026 •

Copy link
Copy Markdown
Collaborator

Change

Replace a fraction of hardware exp2 (SFU) instructions with a polynomial FMA emulation (ex2_emulation_2) in the softmax P-tile computation. The key insight: SM100's SFU throughput is a bottleneck for hdim=256 due to the large tile size. By substituting 3 out of every 4 exp2 calls (ex2_emu_freq=4, ex2_emu_res=3) with packed FMA polynomial approximation, we shift pressure onto the underutilized FMA pipeline.

Additionally, the P write-slot acquisition is moved earlier to overlap any pipeline stall with the exp2 compute.

Kernel-only change; no API change. Backward is untouched.

Validation (this PR vs current origin/main)

[origin/main] : #2487

B200, bf16, hdim=256, MHA (32:32) and GQA (32:2), 3-run means, locked clocks @ 1755 MHz, seqlens 4k..128k.

FWD delta vs origin/main (TFLOPS mean, 3 runs each)

seqlen causal MHA 32:32 GQA 32:2
4k F -0.2% +0.7%
8k F +0.4% +0.3%
16k F +0.7% -1.0%
32k F +2.3% -0.3%
64k F +5.4% +5.2%
128k F +7.4% +7.3%
4k T +0.3% +0.9%
8k T +0.8% +0.9%
16k T +0.8% +1.1%
32k T +5.2% -0.4%
64k T +1.1% +1.1%
128k T +3.4% +2.6%
  • 19 of 24 cells positive; 4 slightly negative, all within the batch-quantization noise band that origin/main itself already showed in our 3-run regression sweep.
  • Peak gain +7.4% (MHA 128k non-causal) — exactly where softmax SFU pressure is worst, consistent with the theory above.
  • Averages: MHA fwd +2.3%, GQA fwd +1.5%.

Correctness smoke

pytest tests/cute/test_flash_attn.py::test_flash_attn_output -k "256-False-0-0.0-False-False" on B200:
78 passed, 78 skipped, 0 failed — identical pass/skip count to origin/main.

Co-authered by @Johnsonms and @wangsiyu

Rebased cherry-pick of `e122e67` from `Johnsonms/exp2-emu-hd256` on top
of merged main (hd256 PR Dao-AILab#2412, `27b4eb9`). The original branch was
based on a pre-merge snapshot; the other five commits in that branch
were absorbed into the squash-merge, leaving this one novel change.

## Change

Replace a fraction of hardware `exp2` (SFU) instructions with a
polynomial FMA emulation (`ex2_emulation_2`) in the softmax P-tile
computation. The key insight: SM100's SFU throughput is a bottleneck
for hdim=256 due to the large tile size. By substituting 3 out of
every 4 `exp2` calls (`ex2_emu_freq=4`, `ex2_emu_res=3`) with packed
FMA polynomial approximation, we shift pressure onto the underutilized
FMA pipeline.

Additionally, the P write-slot acquisition is moved earlier to overlap
any pipeline stall with the `exp2` compute.

Kernel-only change; no API change. Backward is untouched.

## Validation (this PR vs `origin/main` @ `b21e204` — includes Dao-AILab#2412 hd256 base and Dao-AILab#2487 post-merge cleanup)

B200, bf16, hdim=256, MHA (32:32) and GQA (32:2), 3-run means, locked
clocks @ 1755 MHz, seqlens 4k..128k.

### FWD delta vs `origin/main` (TFLOPS mean, 3 runs each)

| seqlen | causal | MHA 32:32 | GQA 32:2 |
|-------:|:------:|:---------:|:--------:|
| 4k     |   F    |  -0.2%    |  +0.7%   |
| 8k     |   F    |  +0.4%    |  +0.3%   |
| 16k    |   F    |  +0.7%    |  -1.0%   |
| 32k    |   F    |  +2.3%    |  -0.3%   |
| 64k    |   F    |  **+5.4%** |  **+5.2%** |
| 128k   |   F    |  **+7.4%** |  **+7.3%** |
| 4k     |   T    |  +0.3%    |  +0.9%   |
| 8k     |   T    |  +0.8%    |  +0.9%   |
| 16k    |   T    |  +0.8%    |  +1.1%   |
| 32k    |   T    |  **+5.2%** |  -0.4%   |
| 64k    |   T    |  +1.1%    |  +1.1%   |
| 128k   |   T    |  **+3.4%** |  **+2.6%** |

- 19 of 24 cells positive; 4 slightly negative, all within the
  batch-quantization noise band that `origin/main` itself already
  showed in our 3-run regression sweep.
- Peak gain **+7.4%** (MHA 128k non-causal) — exactly where softmax
  SFU pressure is worst, consistent with the theory above.
- Averages: **MHA fwd +2.3%**, **GQA fwd +1.5%**.

### Correctness smoke

`pytest tests/cute/test_flash_attn.py::test_flash_attn_output -k
"256-False-0-0.0-False-False"` on B200:
**78 passed, 78 skipped, 0 failed** — identical pass/skip count to
`origin/main`.

## Caveat

Exp2 FMA emulation introduces small numerical differences vs hardware
`exp2`. The existing test tolerances accept the delta.
@tridao

tridao commented Apr 24, 2026

Copy link
Copy Markdown
Member

I'm a bit surprised that ex2 emulation helps here because the ratio of ex2 vs MMA is lower for large headdim, so ex2 emulation is less necessary.
Did you try 50:50 ratio between ex2 and ex2_emulation?

@Johnsonms

Johnsonms commented Apr 25, 2026 •

Copy link
Copy Markdown
Collaborator Author

This

I'm a bit surprised that ex2 emulation helps here because the ratio of ex2 vs MMA is lower for large headdim, so ex2 emulation is less necessary. Did you try 50:50 ratio between ex2 and ex2_emulation?

On the 50:50 question
The original PR values (freq=4, res=3) actually produce exactly 50:50 hw/emu for hd256 (TBH unintended 😊) — since the inner loop steps k by 2, k % 4 only takes values {0, 2}, and res=3 triggers emulation for k%4 ≥ 1, which is every other step.

Using tune_ex2_emu tuned with locked frequency and found freq=14/res=6 (~43% emulation) slightly outperforms that as following.

freq=4/res=3
image
freq=14/res=6
image

  • Config C (freq=14/res=6) clearly outperforms baseline Config A, especially on long sequences (32k+) with +4% to +9% gains. Short sequences (4k–8k) also improve by +1% to +3%, with almost no regressions.
  • Config B (freq=4/res=3) is unstable; causal results are mostly negative, suggesting the 50:50 emulation ratio hurts the causal path.
  • The C vs. B gap is mainly driven by causal performance: Config C is ~+12% faster than B on causal 32k.

On why it helps at all
I checked the key NCU metrics:
image

  1. In the baseline config A, FMA pipeline utilization is only ~24.5%, suggesting spare FMA capacity.
  2. Emulation shifts ~28% of SFU work onto FMA, increasing FMA utilization from ~24.5% to ~36% and better balancing the two pipelines.
  3. SM throughput improves by ~4% (94% → 97–98%), which is consistent with the +2–8% benchmark gains.

So the benefit is likely not simply that “SFU is the bottleneck,” but that FMA had unused capacity. Moving part of the work from SFU to FMA gives a modest but measurable improvement.

Thanks @tridao for leading me to explore this more deeply.

The exp2 emulation knobs (ex2_emu_freq, ex2_emu_res, ex2_emu_start_frg)
and softmax register counts for the hd256 forward kernel were hardcoded
in BlackwellFusedMultiHeadAttentionForward.__init__, invisible to the
central _TUNING_CONFIG table used by all other kernel configs.

- flash_fwd_sm100.py: add hd256 entries to _TUNING_CONFIG (causal and
  non-causal; always 2cta, no sm103 variant). New ex2_emu_res field is
  hd256-specific; existing entries are unaffected. hd256 uses a fixed
  num_regs_other=32 (not derived from the 512-budget formula).
- sm100_hd256_2cta_fmha_forward.py: replace hardcoded self.* assignments
  with a _TUNING_CONFIG lookup.

Tuned values (B200, bf16, locked clocks): freq=14, res=6, start_frg=0
for both causal and non-causal. The inner loop steps k by 2, so k%freq
only takes even values; freq=14/res=6 gives ~43% emulation (3 out of 7
even k%14 steps), replacing the previous 50:50 split (freq=4/res=3).
@Johnsonms
Johnsonms force-pushed the Johnsonms/exp2-emu-hd256-v2 branch from 5b6ad0c to 80fb1ec Compare April 25, 2026 00:41
@tridao

tridao commented Apr 28, 2026

Copy link
Copy Markdown
Member

This is great, thanks @Johnsonms

@Johnsonms Johnsonms changed the title [hd256] Improve forward kernel with exp2 FMA emulation [hd256] Improve forward kernel with exp2 FMA emulation (+3% to +9% performance gain) Apr 28, 2026
@Johnsonms
Johnsonms merged commit 6c73fb5 into Dao-AILab:main Apr 28, 2026
@Johnsonms Johnsonms changed the title [hd256] Improve forward kernel with exp2 FMA emulation (+3% to +9% performance gain) [hd256] Improve forward kernel with exp2 FMA emulation (3% to 9% performance gain) Apr 28, 2026
ussoewwin pushed a commit to ussoewwin/flash-attention that referenced this pull request May 13, 2026
…rformance gain) (Dao-AILab#2488)

* [hd256] Improve forward kernel with exp2 FMA emulation

Rebased cherry-pick of `e122e67` from `Johnsonms/exp2-emu-hd256` on top
of merged main (hd256 PR Dao-AILab#2412, `28faa77`). The original branch was
based on a pre-merge snapshot; the other five commits in that branch
were absorbed into the squash-merge, leaving this one novel change.

## Change

Replace a fraction of hardware `exp2` (SFU) instructions with a
polynomial FMA emulation (`ex2_emulation_2`) in the softmax P-tile
computation. The key insight: SM100's SFU throughput is a bottleneck
for hdim=256 due to the large tile size. By substituting 3 out of
every 4 `exp2` calls (`ex2_emu_freq=4`, `ex2_emu_res=3`) with packed
FMA polynomial approximation, we shift pressure onto the underutilized
FMA pipeline.

Additionally, the P write-slot acquisition is moved earlier to overlap
any pipeline stall with the `exp2` compute.

Kernel-only change; no API change. Backward is untouched.

## Validation (this PR vs `origin/main` @ `5bc2d52` — includes Dao-AILab#2412 hd256 base and Dao-AILab#2487 post-merge cleanup)

B200, bf16, hdim=256, MHA (32:32) and GQA (32:2), 3-run means, locked
clocks @ 1755 MHz, seqlens 4k..128k.

### FWD delta vs `origin/main` (TFLOPS mean, 3 runs each)

| seqlen | causal | MHA 32:32 | GQA 32:2 |
|-------:|:------:|:---------:|:--------:|
| 4k     |   F    |  -0.2%    |  +0.7%   |
| 8k     |   F    |  +0.4%    |  +0.3%   |
| 16k    |   F    |  +0.7%    |  -1.0%   |
| 32k    |   F    |  +2.3%    |  -0.3%   |
| 64k    |   F    |  **+5.4%** |  **+5.2%** |
| 128k   |   F    |  **+7.4%** |  **+7.3%** |
| 4k     |   T    |  +0.3%    |  +0.9%   |
| 8k     |   T    |  +0.8%    |  +0.9%   |
| 16k    |   T    |  +0.8%    |  +1.1%   |
| 32k    |   T    |  **+5.2%** |  -0.4%   |
| 64k    |   T    |  +1.1%    |  +1.1%   |
| 128k   |   T    |  **+3.4%** |  **+2.6%** |

- 19 of 24 cells positive; 4 slightly negative, all within the
  batch-quantization noise band that `origin/main` itself already
  showed in our 3-run regression sweep.
- Peak gain **+7.4%** (MHA 128k non-causal) — exactly where softmax
  SFU pressure is worst, consistent with the theory above.
- Averages: **MHA fwd +2.3%**, **GQA fwd +1.5%**.

### Correctness smoke

`pytest tests/cute/test_flash_attn.py::test_flash_attn_output -k
"256-False-0-0.0-False-False"` on B200:
**78 passed, 78 skipped, 0 failed** — identical pass/skip count to
`origin/main`.

## Caveat

Exp2 FMA emulation introduces small numerical differences vs hardware
`exp2`. The existing test tolerances accept the delta.

* [hd256] Wire ex2_emu params through _TUNING_CONFIG with tuned values

The exp2 emulation knobs (ex2_emu_freq, ex2_emu_res, ex2_emu_start_frg)
and softmax register counts for the hd256 forward kernel were hardcoded
in BlackwellFusedMultiHeadAttentionForward.__init__, invisible to the
central _TUNING_CONFIG table used by all other kernel configs.

- flash_fwd_sm100.py: add hd256 entries to _TUNING_CONFIG (causal and
  non-causal; always 2cta, no sm103 variant). New ex2_emu_res field is
  hd256-specific; existing entries are unaffected. hd256 uses a fixed
  num_regs_other=32 (not derived from the 512-budget formula).
- sm100_hd256_2cta_fmha_forward.py: replace hardcoded self.* assignments
  with a _TUNING_CONFIG lookup.

Tuned values (B200, bf16, locked clocks): freq=14, res=6, start_frg=0
for both causal and non-causal. The inner loop steps k by 2, so k%freq
only takes even values; freq=14/res=6 gives ~43% emulation (3 out of 7
even k%14 steps), replacing the previous 50:50 split (freq=4/res=3).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants