Skip to content

Commit 9d5087d

Browse files
[executorch][cuda] Add a length-aware Triton SDPA over per-sequence KV columns (#24879)
This PR was created by the merge bot to help merge the original PR into the main branch. ghstack PR number: #24072 by @Gasoonjia ^ Please use this as the source of truth for the PR details, comments, and reviews ghstack PR base: https://github.com/pytorch/executorch/tree/gh/Gasoonjia/90/base ghstack PR head: https://github.com/pytorch/executorch/tree/gh/Gasoonjia/90/head Merge bot PR base: https://github.com/pytorch/executorch/tree/main Merge bot PR head: https://github.com/pytorch/executorch/tree/gh/Gasoonjia/90/orig Differential Revision: [D125987296](https://our.internmc.facebook.com/intern/diff/D125987296/) @diff-train-skip-merge Co-authored-by: gasoonjia <gasoonjia@icloud.com>
1 parent d2b856a commit 9d5087d

6 files changed

Lines changed: 1175 additions & 0 deletions

File tree

‎backends/cuda/BUCK‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ fbcode_target(
178178
"triton/kernels/quantized_gemm_family.py",
179179
"triton/kernels/quantized_gemm_utils.py",
180180
"triton/kernels/sdpa.py",
181+
"triton/kernels/sdpa_varlen.py",
181182
"triton/kernels/topk.py",
182183
"triton/kernels/tq4_sdpa.py",
183184
"triton/kernels/triton_mutation_cache.py",
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
"""Benchmark triton::sdpa_varlen against the cell layout's triton::sdpa.
8+
9+
Muse Glimmer 30B attention (H_q=32, H_kv=2, D=128). Each case is a step of
10+
the batching runtime: per sequence, the query rows it adds and the context it
11+
then holds. The cell baseline sweeps one shared pool holding every sequence,
12+
masked per row; sdpa_varlen reads each sequence's own column.
13+
14+
usage: python bench_sdpa_varlen.py [--check]
15+
"""
16+
17+
import argparse
18+
19+
import torch
20+
import triton
21+
22+
from executorch.backends.cuda.triton.kernels.sdpa import sdpa
23+
from executorch.backends.cuda.triton.kernels.sdpa_varlen import sdpa_varlen
24+
25+
HQ, HKV, D = 32, 2, 128
26+
WINDOW = 2048
27+
28+
# (name, [(query rows, context after the step)] per sequence)
29+
CASES = [
30+
("decode c1 ctx600", [(1, 600)]),
31+
("decode c4 ctx600", [(1, 600)] * 4),
32+
("decode c4 ctx4k", [(1, 4096)] * 4),
33+
("decode c4 mixed", [(1, 300), (1, 900), (1, 2500), (1, 4096)]),
34+
("verify c1 16x600", [(16, 600)]),
35+
("verify c4 16x600", [(16, 600)] * 4),
36+
("verify c4 16x4k", [(16, 4096)] * 4),
37+
("prefill 97", [(97, 97)]),
38+
("prefill 512 + 3 decode", [(512, 512), (1, 600), (1, 600), (1, 600)]),
39+
("prefill 4096", [(4096, 4096)]),
40+
]
41+
42+
43+
def _step(case, window, device="cuda"):
44+
t = sum(q for q, _ in case)
45+
cols = len(case) + 1
46+
rows = max(c for _, c in case)
47+
q = torch.randn(1, HQ, t, D, device=device, dtype=torch.bfloat16)
48+
k_seq = torch.randn(rows, cols, HKV, D, device=device, dtype=torch.bfloat16)
49+
v_seq = torch.randn_like(k_seq)
50+
info, start = [], 0
51+
for col, (q_len, ctx) in enumerate(case):
52+
info.append((start, q_len, col, ctx))
53+
start += q_len
54+
info = torch.tensor(info, dtype=torch.int32, device=device)
55+
56+
# Cell layout: every sequence's context packed into one pool, each row's
57+
# visibility in an explicit mask, the sweep bounded by the occupied extent.
58+
pool = sum(c for _, c in case)
59+
k_pool = torch.empty(1, pool, HKV, D, device=device, dtype=torch.bfloat16)
60+
v_pool = torch.empty_like(k_pool)
61+
mask = torch.zeros(1, 1, t, pool, device=device, dtype=torch.bool)
62+
base, row = 0, 0
63+
for col, (q_len, ctx) in enumerate(case):
64+
k_pool[0, base : base + ctx] = k_seq[:ctx, col]
65+
v_pool[0, base : base + ctx] = v_seq[:ctx, col]
66+
for r in range(q_len):
67+
p = ctx - q_len + r
68+
lo = max(0, p - window + 1) if window else 0
69+
mask[0, 0, row + r, base + lo : base + p + 1] = True
70+
base += ctx
71+
row += q_len
72+
read_len = torch.tensor([pool], device=device, dtype=torch.int64)
73+
return q, k_seq, v_seq, info, k_pool, v_pool, mask, read_len
74+
75+
76+
def _time(fn):
77+
fn()
78+
torch.cuda.synchronize()
79+
graph = torch.cuda.CUDAGraph()
80+
with torch.cuda.graph(graph):
81+
fn()
82+
return triton.testing.do_bench(graph.replay, warmup=25, rep=200)
83+
84+
85+
def main():
86+
parser = argparse.ArgumentParser()
87+
parser.add_argument("--check", action="store_true")
88+
args = parser.parse_args()
89+
torch.manual_seed(0)
90+
scale = D**-0.5
91+
print(
92+
f"{'case':28s} {'window':>6s} {'cell ms':>9s} {'varlen ms':>10s} {'speedup':>8s}"
93+
)
94+
for name, case in CASES:
95+
for window in (0, WINDOW):
96+
q, k, v, info, k_pool, v_pool, mask, read_len = _step(case, window)
97+
step_args = (q, k, v, info, scale, True, window, 0)
98+
cell_args = (
99+
q,
100+
k_pool.transpose(1, 2),
101+
v_pool.transpose(1, 2),
102+
mask,
103+
0.0,
104+
False,
105+
scale,
106+
True,
107+
read_len,
108+
)
109+
110+
def varlen(args=step_args):
111+
return sdpa_varlen(*args)
112+
113+
def cell(args=cell_args):
114+
return sdpa(*args)
115+
116+
if args.check:
117+
torch.testing.assert_close(
118+
varlen().float(), cell().float(), atol=2e-2, rtol=2e-2
119+
)
120+
t_cell, t_var = _time(cell), _time(varlen)
121+
print(
122+
f"{name:28s} {window:6d} {t_cell:9.4f} {t_var:10.4f} {t_cell / t_var:7.2f}x"
123+
)
124+
125+
126+
if __name__ == "__main__":
127+
main()

‎backends/cuda/tests/targets.bzl‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,26 @@ def define_common_targets(is_fbcode = False):
134134
),
135135
)
136136

137+
python_unittest_remote_gpu(
138+
name = "test_sdpa_varlen",
139+
srcs = [
140+
"test_sdpa_varlen.py",
141+
],
142+
visibility = [
143+
"//executorch/...",
144+
],
145+
deps = [
146+
"//caffe2:torch",
147+
"//executorch/backends/cuda:triton_kernels",
148+
"//executorch/examples/models/muse-glimmer/model:dflash_model",
149+
],
150+
keep_gpu_sections = True,
151+
remote_execution = re_test_utils.remote_execution(
152+
platform = "gpu-remote-execution",
153+
subplatform = "A100-exclusive",
154+
),
155+
)
156+
137157
python_unittest_remote_gpu(
138158
name = "test_int4_large_quantized_gemm",
139159
srcs = [

0 commit comments

Comments
 (0)