Repository navigation
[Triton/Gluon] [FlyDSL] [CI] feat(flydsl): Add paged-attention Tile kernel #4332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 19 commits
Commits
Show all changes
70 commits
Select commit
Hold shift + click to select a range
c6be02e
feat(flydsl): Add paged-attention Tile kernel
fsx950223 9183f94
refactor(flydsl): Remove unused memory helpers
fsx950223 5824387
refactor(flydsl): Separate PA decode kernel from host
fsx950223 ed3e0d6
refactor(flydsl): Rename PA decode host API
fsx950223 da010a7
refactor(flydsl): Merge PA decode host modules
fsx950223 430b8cd
feat(flydsl): Add Gluon-compatible PA decode dispatch
fsx950223 6f8d4fd
fix(ci): Resolve Ruff 0.16 lint failures
fsx950223 3500f28
fix(flydsl): use in-tree PA buffer helpers
fsx950223 48e320d
fix(flydsl): support FlyDSL 0.3 vector APIs
fsx950223 10ad6d3
[FlyDSL] fix(pa_decode): rename to pa_decode_flydsl and fix review bl…
fsx950223 e65d6ba
refactor(pa_decode): move FlyDSL reduce kernel
fsx950223 7bac868
refactor(flydsl): address pa decode review feedback
fsx950223 8de2af2
style(flydsl): format scalar buffer helper
fsx950223 5b232b1
refactor(flydsl): use buffer tensor helper in pa decode
fsx950223 03d44cb
refactor(flydsl): inline pa decode buffer loaders
fsx950223 e847b80
refactor(flydsl): use pointer ABI for pa decode tile
fsx950223 27b6f58
[FlyDSL] Fix PA edge cases and add tuned page-128 support (#5474)
zhiding512 da579b8
[FlyDSL] Fix phase-split PA decode softmax overflow
Bernard-Liu be0963c
[FlyDSL] Remove duplicate group_idx calculation in PA reduce
Bernard-Liu 77572ba
fix(flydsl): import PA decode function explicitly
fsx950223 3bcde89
perf(flydsl): prototype page16 PA V/K pipeline
fsx950223 68cf491
perf(flydsl): add page16 PA IGLP variant
fsx950223 cdc4c74
perf(flydsl): allow page16 PA pipeline for all split counts
fsx950223 7bc3660
perf(flydsl): add Gluon numeric-policy PA ablation
fsx950223 f4f8431
fix(flydsl): address PA decode review follow-ups
fsx950223 d39ba45
feat(flydsl): extend PA reduction to 256 partitions
fsx950223 dbfff5f
fix(flydsl): mask partial PA reduction waves
fsx950223 cbec769
perf(flydsl): streamline single-tile PA softmax
fsx950223 f03c6af
perf(flydsl): tune native page128 long-context decode
fsx950223 c154696
[FlyDSL] fix(pa_decode): allow a packed V cache to span fewer blocks
zhiding512 71241bb
[FlyDSL] test(pa_decode): sweep served TP4 batches over both page sizes
zhiding512 959a148
perf(flydsl): parallelize large PA reductions
fsx950223 610db54
test(flydsl): model parallel PA reduction
fsx950223 53cac03
perf(flydsl): use eight PA reduction groups
fsx950223 5ae39e4
test(flydsl): expose transposed-V PA sweep option
fsx950223 af6f4db
Merge PR 4332 latest head into FlyDSL long-context optimization
fsx950223 d5edb5c
feat(flydsl): make PA split clamp configurable
fsx950223 38291a4
test(flydsl): remove standalone PA reducer model test
fsx950223 42edab9
test(flydsl): remove Gluon-specific PA cases
fsx950223 29689c3
test(flydsl): add per-token PA performance sweep
fsx950223 60d4474
[FlyDSL] perf(pa_decode): improve page-16 and page-128 load scheduling
zhiding512 3d53dca
Revert PA load-scheduling optimization for separate review
zhiding512 64a07d6
perf(flydsl): optimize FP8 per-token MTP4 paged attention
fsx950223 ae7bb72
refactor(flydsl): remove PA prefetch environment overrides
fsx950223 60e6331
refactor(flydsl): address PA kernel cleanup feedback
fsx950223 b4d34f6
test(flydsl): extend and streamline PA benchmark
fsx950223 b7b549c
perf(flydsl): optimize MTP2 and MTP3 decode
fsx950223 cecd335
[FlyDSL] perf(pa_decode): add dynamic KV work planning and tune parti…
zhiding512 d699fb2
fix(flydsl): satisfy lint for plan re-export
fsx950223 939d64a
refactor(flydsl): consolidate PA tuning constants
fsx950223 ed84952
perf(flydsl): unify per-token V prefetch policy
fsx950223 2f295c6
feat(flydsl): add sliding windows and attention sinks to PA decode
fsx950223 fd82533
test(flydsl): consolidate PA decode into one parametrized test
fsx950223 380b949
refactor(flydsl): consolidate PA decode constants and conditions
fsx950223 2100893
refactor(flydsl): centralize PA decode scheduling
fsx950223 a4902d0
refactor(flydsl): unify PA decode scheduling and compilation
fsx950223 466e94c
refactor(flydsl): align PA decode API and test arguments
fsx950223 0b9f100
fix(flydsl): exclude work_plan from the Torch operator schema
fsx950223 98dd157
refactor(flydsl): simplify and broaden PA decode plan scheduling
fsx950223 c5911aa
style(flydsl): fix PA decode Black formatting
fsx950223 aa7ac40
test(flydsl): consolidate PA decode unit cases
fsx950223 d42e8f0
Merge branch 'main' into flydsl-pa-decode-tile
fsx950223 1ef243a
fix(flydsl): use Numeric bitcast in DPP XOR helper
fsx950223 408aed7
fix(flydsl): cap planned PA decode partitions at device CU count
fsx950223 436ea3a
docs(flydsl): trim PA decode comments
fsx950223 12f43f8
refactor(flydsl): defer PA decode custom-op registration
fsx950223 d9d5663
refactor(flydsl): remove unused kernel helpers
fsx950223 452a91d
refactor(flydsl): simplify PA decode entry and DPP bitcast
fsx950223 2bd7e88
Merge branch 'main' into flydsl-pa-decode-tile
fsx950223 09b194f
Merge branch 'main' into flydsl-pa-decode-tile
coderfeli File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # Copyright (C) 2025-2026 FlyDSL Project Contributors | ||
|
|
||
| """Partitioned-softmax reduction kernel for FlyDSL paged attention.""" | ||
|
|
||
| from functools import lru_cache | ||
|
|
||
| import flydsl.compiler as flyc | ||
| import flydsl.expr as fx | ||
| from flydsl.expr.typing import T | ||
|
|
||
|
|
||
| @lru_cache(maxsize=256) | ||
| def compile_pa_decode_ps_reduce( | ||
| *, | ||
| max_context_partition_num: int, | ||
| head_size: int, | ||
| output_dtype_str: str, | ||
| logits_dtype_str: str, | ||
| sink_dtype_str: str, | ||
| use_sinks: bool, | ||
| ): | ||
| """Build the partitioned-softmax reduction used by ``pa_decode``. | ||
|
|
||
| Each wave reduces the partition statistics independently, then reuses the | ||
| per-partition weights to combine one output element per thread. The host | ||
| API limits the partition count to one wave, so the LDS path from the more | ||
| general Gluon implementation is unnecessary here. | ||
| """ | ||
| if not 1 <= max_context_partition_num <= 64: | ||
| raise ValueError( | ||
| "max_context_partition_num must be in [1, 64], " | ||
| f"got {max_context_partition_num}" | ||
| ) | ||
| if head_size <= 0 or head_size > 1024 or head_size % 64: | ||
| raise ValueError( | ||
| f"head_size must be a multiple of 64 in [64, 1024], got {head_size}" | ||
| ) | ||
|
|
||
| dtype_map = { | ||
| "f32": fx.Float32, | ||
| "f16": fx.Float16, | ||
| "bf16": fx.BFloat16, | ||
| } | ||
| try: | ||
| output_dtype = dtype_map[output_dtype_str] | ||
| logits_dtype = dtype_map[logits_dtype_str] | ||
| sink_dtype = dtype_map[sink_dtype_str] | ||
| except KeyError as exc: | ||
| raise ValueError(f"Unsupported FlyDSL dtype: {exc.args[0]!r}") from exc | ||
|
|
||
| warp_size = 64 | ||
| log2e = 1.4426950408889634 | ||
| reduce_width = ( | ||
| 1 | ||
| if max_context_partition_num == 1 | ||
| else 1 << ((max_context_partition_num - 1).bit_length()) | ||
| ) | ||
| reduce_shuffle_offsets = [ | ||
| offset for offset in (32, 16, 8, 4, 2, 1) if offset < reduce_width | ||
| ] | ||
|
|
||
| @flyc.kernel(known_block_size=[head_size, 1, 1]) | ||
| def pa_decode_ps_reduce_kernel( | ||
| output_ptr: fx.Pointer, | ||
| exp_sums_ptr: fx.Pointer, | ||
| max_logits_ptr: fx.Pointer, | ||
| logits_ptr: fx.Pointer, | ||
| sink_token_ptr: fx.Pointer, | ||
| stride_output_bs: fx.Int32, | ||
| stride_output_len: fx.Int32, | ||
| stride_output_kv_head: fx.Int32, | ||
| stride_output_group_size: fx.Int32, | ||
| stride_exp_sums_seq: fx.Int32, | ||
| stride_exp_sums_head: fx.Int32, | ||
| stride_exp_sums_part: fx.Int32, | ||
| stride_logits_seq: fx.Int32, | ||
| stride_logits_head: fx.Int32, | ||
| stride_logits_part: fx.Int32, | ||
| stride_logits_group: fx.Int32, | ||
| query_group_size: fx.Int32, | ||
| ): | ||
| tid = fx.thread_idx.x | ||
| batch_idx = fx.block_idx.x | ||
| kv_head_idx = fx.block_idx.y | ||
| eqgs_idx = fx.block_idx.z | ||
|
|
||
| output = fx.recast_iter(output_dtype, output_ptr) | ||
| exp_sums = fx.recast_iter(fx.Float32, exp_sums_ptr) | ||
| max_logits = fx.recast_iter(fx.Float32, max_logits_ptr) | ||
| logits = fx.recast_iter(logits_dtype, logits_ptr) | ||
| if fx.const_expr(use_sinks): | ||
| sink_token = fx.recast_iter(sink_dtype, sink_token_ptr) | ||
|
|
||
| zero_f = fx.Float32(0.0) | ||
| one_f = fx.Float32(1.0) | ||
| neg_inf = fx.Float32(float("-inf")) | ||
| c_log2e = fx.Float32(log2e) | ||
| zero_i = fx.Int32(0) | ||
| c_warp_size = fx.Int32(warp_size) | ||
| c_wave_mask = fx.Int32(warp_size - 1) | ||
| c_part_num = fx.Int32(max_context_partition_num) | ||
| c_reduce_width = fx.Int32(reduce_width) | ||
| c_four = fx.Int32(4) | ||
| c_qgs = query_group_size | ||
| lane = tid & c_wave_mask | ||
| group_idx = eqgs_idx % c_qgs | ||
|
|
||
| def _wave_reduce_max(value): | ||
| reduced = value | ||
| for offset in reduce_shuffle_offsets: | ||
| reduced = reduced.maximumf( | ||
| reduced.shuffle_xor(fx.Int32(offset), c_warp_size) | ||
| ) | ||
| return reduced | ||
|
|
||
| def _wave_reduce_sum(value): | ||
| reduced = value | ||
| for offset in reduce_shuffle_offsets: | ||
| reduced = fx.Float32( | ||
| reduced.addf( | ||
| reduced.shuffle_xor(fx.Int32(offset), c_warp_size), | ||
| fastmath="fast", | ||
| ) | ||
| ) | ||
| return reduced | ||
|
|
||
| lane_in_range = lane < c_part_num | ||
| lane_in_reduce = lane < c_reduce_width | ||
| part_sum = zero_f | ||
| part_max = neg_inf | ||
| if lane_in_reduce: | ||
| part_idx = lane_in_range.select(lane, zero_i) | ||
| stats_offset = ( | ||
| batch_idx * stride_exp_sums_seq | ||
| + kv_head_idx * stride_exp_sums_head | ||
| + part_idx * stride_exp_sums_part | ||
| + eqgs_idx | ||
| ) | ||
| loaded_sum = fx.Float32(exp_sums[stats_offset]) | ||
| loaded_max = fx.Float32(max_logits[stats_offset]) | ||
| part_sum = lane_in_range.select(loaded_sum, zero_f) | ||
| part_max = lane_in_range.select(loaded_max, neg_inf) | ||
|
|
||
| global_max = _wave_reduce_max(part_max) | ||
| safe_global_max = (global_max > neg_inf).select(global_max, zero_f) | ||
| part_scale = (part_max > neg_inf).select( | ||
| fx.exp2((part_max - safe_global_max) * c_log2e, fastmath="fast"), | ||
| zero_f, | ||
| ) | ||
| scaled_sum = part_sum * part_scale | ||
| global_exp_sum = _wave_reduce_sum(scaled_sum) | ||
| if fx.const_expr(use_sinks): | ||
| sink_value = fx.Float32(sink_token[kv_head_idx * c_qgs + group_idx]) | ||
| sink_scale = (global_max > neg_inf).select( | ||
| fx.exp2( | ||
| (sink_value - safe_global_max) * c_log2e, | ||
| fastmath="fast", | ||
| ), | ||
| zero_f, | ||
| ) | ||
| global_exp_sum = global_exp_sum + sink_scale | ||
| safe_global_exp_sum = (global_exp_sum > zero_f).select(global_exp_sum, one_f) | ||
| weight_local = scaled_sum / safe_global_exp_sum | ||
| weight_local_i32 = weight_local.bitcast(fx.Int32) | ||
|
|
||
| acc = zero_f | ||
| for part_idx in fx.range_constexpr(max_context_partition_num): | ||
| c_part_idx = fx.Int32(part_idx) | ||
| weight_i32 = fx.Int32( | ||
| fx.rocdl.ds_bpermute( | ||
| T.i32, | ||
| c_part_idx * c_four, | ||
| weight_local_i32, | ||
| ) | ||
| ) | ||
| weight = weight_i32.bitcast(fx.Float32) | ||
| logits_offset = ( | ||
| batch_idx * stride_logits_seq | ||
| + kv_head_idx * stride_logits_head | ||
| + c_part_idx * stride_logits_part | ||
| + eqgs_idx * stride_logits_group | ||
| + tid | ||
| ) | ||
| part_logits = fx.Float32(logits[logits_offset]) | ||
| acc = acc + part_logits * weight | ||
|
|
||
| query_idx = eqgs_idx // c_qgs | ||
| output_offset = ( | ||
| batch_idx * stride_output_bs | ||
| + query_idx * stride_output_len | ||
| + kv_head_idx * stride_output_kv_head | ||
| + group_idx * stride_output_group_size | ||
| + tid | ||
| ) | ||
| output[output_offset] = acc.to(output_dtype) | ||
|
|
||
| @flyc.jit | ||
| def launch_pa_decode_ps_reduce_kernel( | ||
| output: fx.Pointer, | ||
| exp_sums: fx.Pointer, | ||
| max_logits: fx.Pointer, | ||
| logits: fx.Pointer, | ||
| sink_token: fx.Pointer, | ||
| stride_output_bs: fx.Int32, | ||
| stride_output_len: fx.Int32, | ||
| stride_output_kv_head: fx.Int32, | ||
| stride_output_group_size: fx.Int32, | ||
| stride_exp_sums_seq: fx.Int32, | ||
| stride_exp_sums_head: fx.Int32, | ||
| stride_exp_sums_part: fx.Int32, | ||
| stride_logits_seq: fx.Int32, | ||
| stride_logits_head: fx.Int32, | ||
| stride_logits_part: fx.Int32, | ||
| stride_logits_group: fx.Int32, | ||
| query_seq_len: fx.Int32, | ||
| query_group_size: fx.Int32, | ||
| batch_size: fx.Int32, | ||
| num_kv_heads: fx.Int32, | ||
| stream: fx.Stream, | ||
| ): | ||
| pa_decode_ps_reduce_kernel( | ||
| output, | ||
| exp_sums, | ||
| max_logits, | ||
| logits, | ||
| sink_token, | ||
| stride_output_bs, | ||
| stride_output_len, | ||
| stride_output_kv_head, | ||
| stride_output_group_size, | ||
| stride_exp_sums_seq, | ||
| stride_exp_sums_head, | ||
| stride_exp_sums_part, | ||
| stride_logits_seq, | ||
| stride_logits_head, | ||
| stride_logits_part, | ||
| stride_logits_group, | ||
| query_group_size, | ||
| ).launch( | ||
| grid=(batch_size, num_kv_heads, query_seq_len * query_group_size), | ||
| block=(head_size, 1, 1), | ||
| stream=stream, | ||
| ) | ||
|
|
||
| return { | ||
| "launch": launch_pa_decode_ps_reduce_kernel, | ||
| "kernel": pa_decode_ps_reduce_kernel, | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This breaks Gluon’s Triton fallback for configurations unsupported by this reducer: the caller only catches ImportError. On gfx950, I reproduced this through pa_decode_gluon with BF16, head_size=96, ps=True, 4 partitions, and the C++ reducer disabled. The same input succeeds with FlyDSL disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the concrete repro. Confirmed:
compile_pa_decode_ps_reduceraisesValueErrorforhead_size=96while the Gluon dispatch only treatsImportErroras unsupported, so the Triton fallback is bypassed. I will convert unsupported-configuration failures at the FlyDSL adapter boundary toImportErrorwithout broadly swallowing internalValueErrorexceptions, and add a fallback regression for the BF16/D96/NP4 case. I will keep this thread open until the code and test land.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented in f4f8431. The Gluon wrapper now capability-checks the optional FlyDSL reducer before selecting it, so BF16/D96/NP4 falls through to Triton. Direct FlyDSL compile validation still raises ValueError, and errors after a supported FlyDSL reducer is selected are no longer swallowed. Added regressions for both D96 fallback and propagation of an internal ImportError on a supported D128 configuration.