Skip to content

Commit 375e5c0

Browse files
committed
lint
1 parent 47d6b98 commit 375e5c0

7 files changed

Lines changed: 359 additions & 99 deletions

File tree

backends/cuda/runtime/cuda_backend.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,27 @@ class ET_EXPERIMENTAL CudaBackend final
262262
FreeableBuffer* processed, // This will be a empty buffer
263263
ArrayRef<CompileSpec> compile_specs // This will be my empty list
264264
) const override {
265+
// Apply runtime_specs from LoadBackendOptionsMap (if provided)
266+
auto runtime_specs = context.runtime_specs();
267+
if (runtime_specs.size() > 0) {
268+
for (size_t i = 0; i < runtime_specs.size(); ++i) {
269+
const auto& opt = runtime_specs[i];
270+
if (std::strcmp(opt.key, kSkipCopyOutputToCpuForMethod) == 0) {
271+
if (auto* val =
272+
std::get_if<std::array<char, kMaxOptionValueLength>>(
273+
&opt.value)) {
274+
const_cast<CudaBackend*>(this)->set_skip_copy_method(*val);
275+
}
276+
} else if (std::strcmp(opt.key, kUseSharedCudaStream) == 0) {
277+
if (auto* val = std::get_if<bool>(&opt.value)) {
278+
if (*val) {
279+
const_cast<CudaBackend*>(this)->create_shared_cuda_stream();
280+
}
281+
}
282+
}
283+
}
284+
}
285+
265286
std::string method_name;
266287
for (const CompileSpec& spec : compile_specs) {
267288
if (std::strcmp(spec.key, "method_name") == 0) {

backends/cuda/tests/test_chunk_gated_delta_rule.py

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
from torch.export import export
4343

4444

45-
B, T, H, K, V = 1, 128, 4, 64, 64
45+
B, H, K, V = 1, 4, 64, 64
46+
T = 128 # default T for chunked tests
4647

4748
EXECUTORCH_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "../../.."))
4849
RUNNER_PATH = os.path.join(EXECUTORCH_ROOT, "cmake-out", "executor_runner")
@@ -88,32 +89,37 @@ def _make_inputs_from_fla(
8889
gate_logit_normalizer,
8990
mask_p=0.0,
9091
nonzero_h0=False,
92+
seq_len=T,
9193
dtype=torch.bfloat16,
9294
device="cuda",
9395
):
9496
"""Generate inputs following FLA test_chunk() conventions."""
9597
torch.manual_seed(seed)
96-
q = torch.rand(B, T, H, K, dtype=dtype, device=device)
97-
k = torch.rand(B, T, H, K, dtype=dtype, device=device)
98-
v = torch.rand(B, T, H, V, dtype=dtype, device=device)
99-
beta = torch.rand(B, T, H, dtype=torch.float32, device=device).sigmoid().to(dtype)
100-
g = F.logsigmoid(torch.rand(B, T, H, dtype=torch.float32, device=device))
98+
q = torch.rand(B, seq_len, H, K, dtype=dtype, device=device)
99+
k = torch.rand(B, seq_len, H, K, dtype=dtype, device=device)
100+
v = torch.rand(B, seq_len, H, V, dtype=dtype, device=device)
101+
beta = (
102+
torch.rand(B, seq_len, H, dtype=torch.float32, device=device)
103+
.sigmoid()
104+
.to(dtype)
105+
)
106+
g = F.logsigmoid(torch.rand(B, seq_len, H, dtype=torch.float32, device=device))
101107
g = (g / gate_logit_normalizer).to(dtype)
102108
if mask_p > 0:
103-
g = g * (torch.rand(B, T, H, dtype=dtype, device=device) > mask_p)
109+
g = g * (torch.rand(B, seq_len, H, dtype=dtype, device=device) > mask_p)
104110
if nonzero_h0:
105111
h0 = torch.randn(B, H, K, V, dtype=dtype, device=device)
106112
else:
107113
h0 = torch.zeros(B, H, K, V, dtype=dtype, device=device)
108114
return q, k, v, g, beta, h0
109115

110116

111-
def _make_inputs(dtype=torch.bfloat16, device="cuda"):
112-
q = torch.randn(B, T, H, K, dtype=dtype, device=device)
113-
k = torch.randn(B, T, H, K, dtype=dtype, device=device)
114-
v = torch.randn(B, T, H, V, dtype=dtype, device=device)
115-
g = F.logsigmoid(torch.randn(B, T, H, dtype=dtype, device=device))
116-
beta = torch.rand(B, T, H, dtype=dtype, device=device).sigmoid()
117+
def _make_inputs(seq_len=T, dtype=torch.bfloat16, device="cuda"):
118+
q = torch.randn(B, seq_len, H, K, dtype=dtype, device=device)
119+
k = torch.randn(B, seq_len, H, K, dtype=dtype, device=device)
120+
v = torch.randn(B, seq_len, H, V, dtype=dtype, device=device)
121+
g = F.logsigmoid(torch.randn(B, seq_len, H, dtype=dtype, device=device))
122+
beta = torch.rand(B, seq_len, H, dtype=dtype, device=device).sigmoid()
117123
initial_state = torch.randn(B, H, K, V, dtype=dtype, device=device)
118124
return q, k, v, g, beta, initial_state
119125

@@ -252,6 +258,69 @@ def test_eager_matches_fla(self):
252258

253259
self.assertLess((o_ours.float() - o_ref.float()).abs().max().item(), 0.01)
254260

261+
def test_recurrent_t1(self):
262+
"""T=1 (decode) uses recurrent kernel — verify vs naive reference."""
263+
from fla.ops.gated_delta_rule.naive import naive_recurrent_gated_delta_rule
264+
265+
model = ChunkGatedDeltaModel().eval()
266+
for seed, norm, mask_p, nonzero_h0, desc in FLA_TEST_CONFIGS:
267+
with self.subTest(desc=desc):
268+
inputs = _make_inputs_from_fla(
269+
seed, norm, mask_p, nonzero_h0, seq_len=1
270+
)
271+
q, k, v, g, beta, h0 = inputs
272+
273+
with torch.no_grad():
274+
o_ours, s_ours = model(q, k, v, g, beta, h0)
275+
276+
o_ref, s_ref = naive_recurrent_gated_delta_rule(
277+
q=F.normalize(q, p=2, dim=-1),
278+
k=F.normalize(k, p=2, dim=-1),
279+
v=v,
280+
beta=beta,
281+
g=g,
282+
initial_state=h0,
283+
output_final_state=True,
284+
)
285+
286+
self.assertEqual(o_ours.shape, torch.Size([B, 1, H, V]))
287+
self.assertEqual(s_ours.shape, torch.Size([B, H, K, V]))
288+
o_diff = (o_ours.float() - o_ref.float()).abs().max().item()
289+
s_diff = (s_ours.float() - s_ref.float()).abs().max().item()
290+
self.assertLess(o_diff, 0.01, f"{desc}: output diff {o_diff}")
291+
self.assertLess(s_diff, 0.01, f"{desc}: state diff {s_diff}")
292+
293+
def test_dispatch_multiple_seq_lengths(self):
294+
"""Verify correctness across T values hitting both dispatch paths."""
295+
from fla.ops.gated_delta_rule.naive import naive_recurrent_gated_delta_rule
296+
297+
model = ChunkGatedDeltaModel().eval()
298+
# T=1 → recurrent, T>1 → chunked; include boundary values
299+
for seq_len in [1, 2, 32, 63, 64, 65, 128, 256]:
300+
with self.subTest(T=seq_len):
301+
inputs = _make_inputs_from_fla(42, 1.0, 0.0, True, seq_len=seq_len)
302+
q, k, v, g, beta, h0 = inputs
303+
304+
with torch.no_grad():
305+
o_ours, s_ours = model(q, k, v, g, beta, h0)
306+
307+
o_ref, s_ref = naive_recurrent_gated_delta_rule(
308+
q=F.normalize(q, p=2, dim=-1),
309+
k=F.normalize(k, p=2, dim=-1),
310+
v=v,
311+
beta=beta,
312+
g=g,
313+
initial_state=h0,
314+
output_final_state=True,
315+
)
316+
317+
self.assertEqual(o_ours.shape, torch.Size([B, seq_len, H, V]))
318+
self.assertEqual(s_ours.shape, torch.Size([B, H, K, V]))
319+
o_diff = (o_ours.float() - o_ref.float()).abs().max().item()
320+
s_diff = (s_ours.float() - s_ref.float()).abs().max().item()
321+
self.assertLess(o_diff, 0.02, f"T={seq_len}: output diff {o_diff}")
322+
self.assertLess(s_diff, 0.02, f"T={seq_len}: state diff {s_diff}")
323+
255324
def test_export_cuda(self):
256325
with tempfile.TemporaryDirectory() as tmpdir:
257326
pte_path = export_chunk_gated_delta(tmpdir)

0 commit comments

Comments
 (0)