Skip to content

[Triton/Gluon] [FlyDSL] [CI] Refactor PA decode, direct native autotune, and BF16 KV - #5809

Merged
coderfeli merged 32 commits into
mainfrom
refactor/pa-decode-oop
Oct 10, 2026
Merged

coderfeli merged 32 commits into
mainfrom
refactor/pa-decode-oop

Conversation

@fsx950223

@fsx950223 fsx950223 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Refactor FlyDSL PA decode into operation classes with explicit GPU work plans, add native BF16 KV, and separate tuning from decode execution in aiter/ops/flydsl/pa_decode_tuning.py.

  • Separate scheduling/traits, context, memory movement, GEMM, softmax, epilogue, and pipeline components. Retain packed work plans, query splitting, K/V prefetch scheduling, and compact/window-bounded reductions.
  • Require an explicit work_plan for decode and graph replay. Prepare or refresh plans before execution; max_context_length supplies a host scheduling bound independently of block-table padding. Decode does not import or invoke the tuning module.
  • Provide a standalone tuning CLI with batch/head dimensions, query and context lengths, page size, FP8/BF16 K/V, scale mode, V layout, sliding windows, sinks, GPU selection, and output-directory options. One invocation searches the requested shape and saves the native configuration while retaining results for other shapes.
  • Keep native FlyDSL candidate benchmarking, fastest-config selection, validation hooks, and JSON persistence. Deduplicate equivalent capacities and compare candidates with the 2*CU baseline. Candidate allocation, compilation, and graph capture happen before timed replay.
  • Export prepare_pa_decode_plan from aiter.ops.flydsl.pa_decode_tuning and aiter.ops.flydsl. Ordinary cache misses use 2*CU without preparing or timing candidates; tune=True explicitly searches without modifying environment variables. config_dir selects per-architecture result directories.
  • Remove tuning pytest cases and their environment/function monkeypatch helpers from op_tests/test_flydsl_pa_decode.py. Retain decode correctness, invalid-input, work-plan, and graph-replay coverage.

Standalone tuning example:

python -m aiter.ops.flydsl.pa_decode_tuning \
    --batch-size 32 --num-query-heads 16 --num-kv-heads 1 --head-dim 128 \
    --query-length 4 --context-length 16384 --block-size 128 \
    --kv-dtype fp8 --scale-mode per-token --trans-v 1 \
    --output-dir /tmp/pa-configs

Results are saved to /tmp/pa-configs/<arch>/_pa_decode_autotuner.json. Without --output-dir, use aiter/ops/flydsl/configs/pa/<arch>/, or the native FLYDSL_AUTOTUNE_CACHE_DIR override. Python callers can use the same result root with config_dir="/tmp/pa-configs".

Example with actual BF16 query/KV tensors and page64/D192:

from aiter.ops.flydsl.pa_decode import pa_decode
from aiter.ops.flydsl.pa_decode_tuning import prepare_pa_decode_plan

plan = prepare_pa_decode_plan(
    query, key_cache, value_cache, context_lengths, block_tables,
    softmax_scale=192**-0.5, query_length=4,
    max_context_length=max_context_length,
)
pa_decode(
    output, query, key_cache, value_cache, context_lengths, block_tables,
    softmax_scale=192**-0.5, query_length=4,
    work_plan=plan, max_context_length=max_context_length,
)

BF16 KV follows ROCm/FlyDSL#1065: require BF16 queries, unscaled vector8 K/V, and compute_type=torch.bfloat16. Support page sizes 16/64/128 and BF16 head dimensions in multiples of 64 through 1024, including D192. Use BF16 Q/K/P/V operands, FP32 softmax/accumulation, and query-position splitting to fit 64 KiB LDS. Allocate caller-owned graph scratch from plan.capacity; refresh plans when context lengths change.

Cache keys include attention geometry, execution modes, CU count, PA kernel/tuning source contents, and native toolchain/environment/device fingerprints. Tensor strides, allocated KV pages, block-table padding, scalar scale values, and max_context_length do not partition the tuning cache. Restart inference processes after updating cache files and use separate directories for parallel tuning processes.

Validation for the standalone-script extraction: Ruff, Black, Python syntax compilation, and diff checks pass. Tests and GPU tuning were not run for this follow-up, as requested.

The previously observed FP32-reference failure for varlen_b81_q4_c1027 occurred on both monolithic and refactored kernels (maximum absolute error 0.00712435 with atol=rtol=0.005). This extraction does not change its numerical behavior.

Use the unaligned MTP window union to bound reducer specialization,
while retaining the CU-count plan cap, scratch layout and query selector.
Cover partition-boundary crossings during refresh in the generic test.

Validated on dzm-mi355-gpu34-dev: 196 unit tests passed, with two
70-case native accuracy/performance sweeps. W1024/B200/QL4 improves
from 80.18 us to 29.14 us, excluding plan construction and refresh.

Signed-off-by: fsx950223 <fsx950223@outlook.com>
Include pending varlen KV buffer loads, prefetch scheduling and compact reduction. Bound dense planned work for native QL2/QL4 query splitting and cover the selector and address boundaries in the generic unit test.

Add offline model/batch budget sweeps with fixed-plan timing, strict FP32 validation and static recommendation keys. Keep context distributions in benchmark metadata only, and preserve failed numerical cases without recommendations.

Validate with MI355 native A/B and generic GPU tests, 22 CPU tuner tests, Black 26.5.1 and Ruff 0.16.0. The existing short-context FP8 numerical limitation is not changed.

Signed-off-by: fsx950223 <fsx950223@outlook.com>
…de-oop

Preserve planned query splitting, unsigned KV buffer loads and prefetch ordering in the PA operation classes. Track the full helper source closure in offline tuning keys and test persistent-cache separation for wide/buffer specializations.
@fsx950223
fsx950223 requested a review from a team September 24, 2026 04:02
@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every PR:

  • ✅ Pre-checks (submodule verification, code formatting)
  • ✅ Aiter op tests (gfx942 + gfx950)
  • ✅ Triton tests on MI35X (only when aiter/ops/triton/** or related paths are changed)

Extended tests (opt-in via labels):

Label Tests
ci:gfx1250-ffm-triton Run the five-shard gfx1250 FFM Triton test suite
ci:triton-300x Run an additional Triton test job on MI300X in PRs; main branch always runs both MI35X and MI300X
multigpu Aiter multi-GPU tests on the 8-GPU runner
ci:sglang SGLang integration tests: DeepSeek-R1-MXFP4 accuracy, Qwen 3.5 accuracy
ci:atom ATOM benchmark: DeepSeek-R1-0528, GPT-OSS-120B
ci:atom_full ATOM accuracy suite for PR and main models from ATOM models_accuracy.json
ci:vllm vLLM benchmark: GPT-OSS-120B, DeepSeek-R1-0528, Kimi-K2.5
ci:all All standard extended tests (excludes ci:atom_full)

Only add ci:atom_full for FlyDSL or Triton upgrades.
Add labels via the sidebar or gh pr edit 5809 --add-label <label>

PR title tags & labels:
Component tags ([Triton/Gluon], [HIP], [CK], [ASM], ...) are added to the PR title and as PR labels automatically from the changed files and re-synced on every push — change-type tags like [fix]/[Perf], op tags like [MLA], and human labels (ci:*) are left untouched. Add the no-auto-title label to opt this PR out.

@fsx950223 fsx950223 changed the title [FlyDSL] Refactor PA decode and add offline budget tuning [FlyDSL] Refactor PA decode and load CSV tuning results Sep 24, 2026
Comment thread aiter/ops/flydsl/pa_decode_tuning.py Outdated
Always normalize Q rows by absmax and scale P before FP8 conversion, compensating both scales in the QK and output stages. This prevents small queries and probability tails from underflowing on the per-tensor KV path.

Add four regression cases with quantized FP8 KV caches and non-unit scales. Validated on MI355: all 264 native PA decode tests pass.
Use one parameterized correctness test with shared input construction and an independent FP32 reference. Cover query dtypes, FP8 KV quantization, cache layouts, partitioning, plans, windows and sinks, including the small-Q and probability-tail regressions.

Remove the benchmark CLI, tuning CSV tests and internal scheduling assertions from this file. Run the unit test once through pytest in CI.

Validated on MI355: all 80 cases pass. The four original scaling regressions still fail against the old shortcut implementation.
@github-actions github-actions Bot changed the title [FlyDSL] Refactor PA decode and load CSV tuning results [FlyDSL] [CI] Refactor PA decode and load CSV tuning results Sep 28, 2026
@github-actions github-actions Bot added the CI label Sep 28, 2026
Remove static tile dispatch, direct output, uniform scratch layouts, and
the static reducer branches. All FlyDSL decode calls now use work plans,
including a partition cap of one, with sinks applied once by the reducer.

Configure partition caps on plan_pa_decode and make the optional decode
arguments keyword-only. Update the tuner and generic unit test, and adapt
Gluon's existing FlyDSL reducer fallback to provide reduction metadata.

Validate on MI355 with 80 generic cases, six Gluon reducer cases, tuner
smoke, and ten graph/eager replays. All replay outputs match the previous
planned implementation bit for bit, including its two-token FP8 error.
@github-actions github-actions Bot changed the title [FlyDSL] [CI] Refactor PA decode and load CSV tuning results [Triton/Gluon] [FlyDSL] [CI] Refactor PA decode and load CSV tuning results Sep 28, 2026
@Boss2002n
Boss2002n requested a lite review from Copilot September 28, 2026 09:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The architecture-independent specialization cache is a critical correctness risk, with additional tuning, performance, and validation concerns.

Review effort: Lite
Findings: 1 High severity · 2 Medium severity · 2 Low severity

Open (5)
What changed in this PR

Refactors FlyDSL paged-attention decoding into modular kernel components and adds CSV-based tuning and budget selection.

Changes:

  • Splits scheduling, memory movement, GEMM, softmax, pipeline, and reduction logic.
  • Adds validated CSV tuning, caching, lookup, and automatic planning.
  • Updates Gluon reduction integration, tests, and CI execution.
File Summary
op_tests/​test_flydsl_pa_decode.py Replaces broad coverage with a smaller eager test; graph replay and planning-contract paths remain untested (nit).
aiter/​ops/​triton/​gluon/​pa_decode_gluon.py Adds FlyDSL reducer dispatch, but rebuilds metadata on every call, creating a moderate performance concern.
aiter/​ops/​flydsl/​pa_decode.py Adds CSV loading, caching, lookup, and automatic planning; committed API tests are missing (nit).
aiter/​ops/​flydsl/​pa_decode_tuning.py Implements tuning and CSV I/O; timing failures can discard valid candidates (moderate), and tuner/API scope is combined with the refactor (nit).
aiter/​ops/​flydsl/​kernels/​pa_decode/​traits.py Defines specialization traits; the cache key omits GPU architecture, causing a critical cross-architecture reuse risk.
aiter/​ops/​flydsl/​kernels/​pa_decode/​pipeline.py Implements the modular decode pipeline.
aiter/​ops/​flydsl/​kernels/​pa_decode/​op_softmax.py Implements masking and softmax operations.
aiter/​ops/​flydsl/​kernels/​pa_decode/​op_lds.py Handles LDS and KV movement.
aiter/​ops/​flydsl/​kernels/​pa_decode/​op_gemm.py Encapsulates GEMM/MFMA operations.
aiter/​ops/​flydsl/​kernels/​pa_decode/​op_epilog.py Stores normalized partial outputs.
aiter/​ops/​flydsl/​kernels/​pa_decode/​context.py Encapsulates CTA context and mappings.
aiter/​ops/​flydsl/​kernels/​pa_decode/​__init__.py Provides implementation organization and caching support.
aiter/​ops/​flydsl/​kernels/​pa_decode_reduce.py Updates packed and compact reduction paths.
.github/​scripts/​aiter_test.sh Runs pytest only; the tuning CLI paths are not exercised in CI (nit).

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +168 to +172
cache_key = (
head_dim,
query_group_size,
block_size,
num_partitions,
Comment thread aiter/ops/flydsl/pa_decode_tuning.py Outdated
Comment thread aiter/ops/triton/gluon/pa_decode_gluon.py Outdated
'
_ "$file"
)
test_cmd=(timeout 60m python3 -m pytest -q "$file")
Comment thread op_tests/test_flydsl_pa_decode.py Outdated
Use the existing C++ PS reducer first and fall back directly to Triton.
Remove FlyDSL imports, dispatch checks, and reduction-plan metadata from
the Gluon wrapper.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unresolved moderate correctness and coverage findings remain across the cache, planning, and test paths.

5 open findings

🧠 Review effort: Lite

Copilot AI lite review requested due to automatic review settings October 9, 2026 06:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A critical output-layout correctness issue remains, along with moderate validation and scope findings.

6 open findings
Previously missed (7)

In code that hasn't changed since last review

Low severity Use absolute imports for FlyDSL traits

aiter/​ops/​flydsl/​kernels/​pa_decode/​context.py:13

This new module uses a relative import, contrary to the repository's absolute-import contract for FlyDSL code. Import the traits module through its absolute aiter.ops.flydsl... path.

Low severity Replace FlyDSL relative imports with absolute paths

aiter/​ops/​flydsl/​kernels/​pa_decode/​op_epilog.py:12

These new imports use relative paths, contrary to the repository's absolute-import convention for FlyDSL code. Switch them to the corresponding fully qualified aiter.ops.flydsl... paths.

Low severity Use absolute import for operation traits

aiter/​ops/​flydsl/​kernels/​pa_decode/​op_gemm.py:10

This new operation module uses a relative import, contrary to the repository's absolute-import contract for FlyDSL code. Import the traits module through its absolute aiter.ops.flydsl... path.

Low severity Replace FlyDSL relative imports with absolute paths

aiter/​ops/​flydsl/​kernels/​pa_decode/​op_lds.py:16

These new imports use relative paths, contrary to the repository's absolute-import convention for FlyDSL code. Switch them to the corresponding fully qualified aiter.ops.flydsl... paths.

Low severity Replace FlyDSL relative imports with absolute paths

aiter/​ops/​flydsl/​kernels/​pa_decode/​op_softmax.py:12

These new imports use relative paths, contrary to the repository's absolute-import convention for FlyDSL code. Switch them to the corresponding fully qualified aiter.ops.flydsl... paths.

Low severity Use absolute imports in PA decode package

aiter/​ops/​flydsl/​kernels/​pa_decode/​pipeline.py:20

The new PA decode subpackage uses relative imports here, contrary to the repository's absolute-import convention for FlyDSL modules. Use the fully qualified aiter.ops.flydsl... paths so this refactor follows the established import boundary.

Low severity Use absolute imports in Triton/FlyDSL wrappers

aiter/​ops/​flydsl/​pa_decode.py:30

These new modules introduce relative imports, but the repository's Triton/FlyDSL wrapper convention requires absolute aiter.ops... imports. Please switch this import block to absolute paths so the new package does not add another import style.

🧠 Review effort: Lite

Comment thread aiter/ops/flydsl/pa_decode.py
Copilot AI lite review requested due to automatic review settings October 9, 2026 07:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread aiter/ops/flydsl/kernels/pa_decode/traits.py
Comment thread aiter/ops/flydsl/kernels/pa_decode_plan.py
Comment thread aiter/ops/flydsl/pa_decode.py
Copilot AI lite review requested due to automatic review settings October 10, 2026 08:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot AI lite review requested due to automatic review settings October 10, 2026 08:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread aiter/ops/flydsl/kernels/pa_decode/pipeline.py
@coderfeli
coderfeli merged commit 42e8726 into main Oct 10, 2026
67 of 69 checks passed
@coderfeli
coderfeli deleted the refactor/pa-decode-oop branch October 10, 2026 14:14
sshlyapn added a commit to andrei-kochin/aiter that referenced this pull request Oct 14, 2026
New optional query_start_loc ([num_seqs + 1], int32) for pa_decode_gluon,
PS path only. Sequence i owns packed query/output rows
[query_start_loc[i], query_start_loc[i + 1]); query_length must be >= every
per-sequence length; rows not owned by any sequence are left untouched.
Non-PS paths assert on it.

- Supported in the gluon head_1 and multi-KV attention kernels and in the
  Triton PS reduce, each behind a compile-time IS_VARLEN flag; the HIP PS
  reduce is used through its query_start_loc argument.
- Causal masks use the per-sequence query length, on top of the per-split
  causal mask from ROCm#5915.
- Fix the default temporary_output dtype: the attention kernels store bf16
  for fp8 compute (otherwise compute_type), not query.dtype. An fp8 query
  with fp8 compute and no temporary_output failed to compile before.
- New test op_tests/triton_tests/test_pa_decode_gluon_varlen.py: each
  sequence of a ragged call must match a batch-1 uniform call, and unowned
  rows must keep a sentinel. Covers bf16, fp8 KV, fp8 Q+KV, sinks, sliding
  window, block 16/64/128 with shuffled V, head_1 and multi-KV, one-shot and
  split runs through the HIP and Triton reduces, and zero-length sequences.

Split out of ROCm#6099, rebased onto main. The FlyDSL reduce part of ROCm#6099 is
dropped: since ROCm#5809 the Gluon path no longer dispatches to the FlyDSL PS
reduce, and the no-work-plan mode it extended no longer exists.

Signed-off-by: Sergei Shliapnikov <sergei.shliapnikov@amd.com>
Signed-off-by: Andrei Kochin <andrei.kochin@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
johannes-graner added a commit that referenced this pull request Oct 15, 2026
Transplant fp8-Q pa_decode onto the modular #5809 package (single
query-scale-aware launcher, output dtype in schedule key), migrate
fmha op_lds BT/V LDS access to recast_iter/add_offset/ptr_load/ptr_store,
and route the adapter decode path through a retained geometry-and-stream
keyed work plan with capture-safe refresh.
sshlyapn added a commit to andrei-kochin/aiter that referenced this pull request Oct 15, 2026
New optional query_start_loc ([num_seqs + 1], int32) for pa_decode_gluon,
PS path only. Sequence i owns packed query/output rows
[query_start_loc[i], query_start_loc[i + 1]); query_length must be >= every
per-sequence length; rows not owned by any sequence are left untouched.
Non-PS paths assert on it.

- Supported in the gluon head_1 and multi-KV attention kernels and in the
  Triton PS reduce, each behind a compile-time IS_VARLEN flag; the HIP PS
  reduce is used through its query_start_loc argument.
- Causal masks use the per-sequence query length, on top of the per-split
  causal mask from ROCm#5915.
- Fix the default temporary_output dtype: the attention kernels store bf16
  for fp8 compute (otherwise compute_type), not query.dtype. An fp8 query
  with fp8 compute and no temporary_output failed to compile before.
- New test op_tests/triton_tests/test_pa_decode_gluon_varlen.py: each
  sequence of a ragged call must match a batch-1 uniform call, and unowned
  rows must keep a sentinel. Covers bf16, fp8 KV, fp8 Q+KV, sinks, sliding
  window, block 16/64/128 with shuffled V, head_1 and multi-KV, one-shot and
  split runs through the HIP and Triton reduces, and zero-length sequences.

Split out of ROCm#6099, rebased onto main. The FlyDSL reduce part of ROCm#6099 is
dropped: since ROCm#5809 the Gluon path no longer dispatches to the FlyDSL PS
reduce, and the no-work-plan mode it extended no longer exists.

Signed-off-by: Sergei Shliapnikov <sergei.shliapnikov@amd.com>
Signed-off-by: Andrei Kochin <andrei.kochin@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
sshlyapn added a commit to andrei-kochin/aiter that referenced this pull request Oct 15, 2026
New optional query_start_loc ([num_seqs + 1], int32) for pa_decode_gluon,
PS path only. Sequence i owns packed query/output rows
[query_start_loc[i], query_start_loc[i + 1]); query_length must be >= every
per-sequence length; rows not owned by any sequence are left untouched.
Non-PS paths assert on it.

- Supported in the gluon head_1 and multi-KV attention kernels and in the
  Triton PS reduce, each behind a compile-time IS_VARLEN flag; the HIP PS
  reduce is used through its query_start_loc argument.
- Causal masks use the per-sequence query length, on top of the per-split
  causal mask from ROCm#5915.
- Fix the default temporary_output dtype: the attention kernels store bf16
  for fp8 compute (otherwise compute_type), not query.dtype. An fp8 query
  with fp8 compute and no temporary_output failed to compile before.
- New test op_tests/triton_tests/test_pa_decode_gluon_varlen.py: each
  sequence of a ragged call must match a batch-1 uniform call, and unowned
  rows must keep a sentinel. Covers bf16, fp8 KV, fp8 Q+KV, sinks, sliding
  window, block 16/64/128 with shuffled V, head_1 and multi-KV, one-shot and
  split runs through the HIP and Triton reduces, and zero-length sequences.

Split out of ROCm#6099, rebased onto main. The FlyDSL reduce part of ROCm#6099 is
dropped: since ROCm#5809 the Gluon path no longer dispatches to the FlyDSL PS
reduce, and the no-work-plan mode it extended no longer exists.

Signed-off-by: Sergei Shliapnikov <sergei.shliapnikov@amd.com>
Signed-off-by: Andrei Kochin <andrei.kochin@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
sshlyapn added a commit to andrei-kochin/aiter that referenced this pull request Oct 16, 2026
New optional query_start_loc ([num_seqs + 1], int32) for pa_decode_gluon,
PS path only. Sequence i owns packed query/output rows
[query_start_loc[i], query_start_loc[i + 1]); query_length must be >= every
per-sequence length; rows not owned by any sequence are left untouched.
Non-PS paths assert on it.

- Supported in the gluon head_1 and multi-KV attention kernels and in the
  Triton PS reduce, each behind a compile-time IS_VARLEN flag; the HIP PS
  reduce is used through its query_start_loc argument.
- Causal masks use the per-sequence query length, on top of the per-split
  causal mask from ROCm#5915.
- Fix the default temporary_output dtype: the attention kernels store bf16
  for fp8 compute (otherwise compute_type), not query.dtype. An fp8 query
  with fp8 compute and no temporary_output failed to compile before.
- New test op_tests/triton_tests/test_pa_decode_gluon_varlen.py: each
  sequence of a ragged call must match a batch-1 uniform call, and unowned
  rows must keep a sentinel. Covers bf16, fp8 KV, fp8 Q+KV, sinks, sliding
  window, block 16/64/128 with shuffled V, head_1 and multi-KV, one-shot and
  split runs through the HIP and Triton reduces, and zero-length sequences.

Split out of ROCm#6099, rebased onto main. The FlyDSL reduce part of ROCm#6099 is
dropped: since ROCm#5809 the Gluon path no longer dispatches to the FlyDSL PS
reduce, and the no-work-plan mode it extended no longer exists.

Signed-off-by: Sergei Shliapnikov <sergei.shliapnikov@amd.com>
Signed-off-by: Andrei Kochin <andrei.kochin@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants