Skip to content

[Common] Bucket the batch size on the direct cu_seqlens fprop path - #3822

Draft
egilliam-nv wants to merge 4 commits into
NVIDIA:mainfrom
egilliam-nv:egilliam/thd-fprop-bucket-batch-direct
Draft

egilliam-nv wants to merge 4 commits into
NVIDIA:mainfrom
egilliam-nv:egilliam/thd-fprop-bucket-batch-direct

Conversation

@egilliam-nv

@egilliam-nv egilliam-nv commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

#3186 passes cu_seqlens directly to cuDNN SDPA fprop. Because cuDNN reads [actual_b+1] entries from the user's buffers, that PR builds the forward graph at the exact batch size instead of the bucketed one. For packed THD training the number of documents per batch varies, so every new count misses the graph cache and rebuilds a graph. This shows up as a large throughput regression in Llama-3 training (about -48% on H100 across #3186 in our bisect).

Change

  • Bucket the forward graph batch size again on the direct path (config_and_params.cpp).
  • Add pad_cu_seqlens_tails, a small kernel that extends the cu_seqlens and ragged-offset buffers to the bucketed batch by repeating the last value (zero-length sequences), so cuDNN never reads out of bounds. It is launched only when the bucketed batch differs from the actual one, uses one launch (vs. two conversion kernels before [Common] Pass cu_seqlens and token-unit ragged offsets directly to cuDNN SDPA fprop #3186), and needs 4 * (b_bucket+1) * 4 bytes of extra workspace.
  • Wired into both the f16/bf16 and FP8 forward launchers.

Status (draft)

Tested on H100 (sm90), cuDNN 9.24.0, bf16, built with NVTE_CUDA_ARCHS=90:

  • Fused THD forward with 17 distinct document counts (3 to 129, fixed total tokens) builds only 4 forward graphs (one per batch bucket) per NVTE_FUSED_ATTN_CACHE_DEBUG=1, and matches a per-document SDPA reference within bf16 noise (max abs error <= 0.0078).
  • pytest tests/pytorch/attention/test_attention.py -k thd: 79 passed, 1532 skipped (hardware), 0 failed.

TODO:

🤖 Generated with Claude Code

PR NVIDIA#3186 builds the forward graph at the exact batch size when cu_seqlens are
passed directly to cuDNN, because cuDNN reads [actual_b+1] entries from the
user's buffers. For packed THD training the number of documents per batch
varies, so every new count misses the graph cache and rebuilds a graph.

Bucket the forward graph batch size again on the direct path and launch one
small kernel (pad_cu_seqlens_tails) that extends the cu_seqlens and ragged
offset buffers to the bucketed size by repeating the last value, i.e. appending
zero-length sequences. The kernel is only launched when the bucketed batch
differs from the actual one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Emil Gilliam <egilliam@nvidia.com>
Signed-off-by: Emil Gilliam <215885072+egilliam-nv@users.noreply.github.com>
@github-actions github-actions Bot added the community-contribution PRs from external contributor outside the core maintainers, representing community-driven work. label Nov 5, 2026
pre-commit-ci Bot and others added 3 commits November 5, 2026 18:44
Move the pad_cu_seqlens_tails launch and its workspace sizing out of the f16
and FP8 forward launchers into pad_direct_cu_seqlens() in utils.cu. Also
include <cassert> in utils.cu, which uses assert() but relied on a transitive
include.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Emil Gilliam <215885072+egilliam-nv@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution PRs from external contributor outside the core maintainers, representing community-driven work.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant