Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
dabb10b
[KDA] port the SM90 fused prefill to a CuTeDSL two-kernel pipeline
Jun 26, 2026
2826afa
test sm90 prefill accuracy against FLA triton
Jun 26, 2026
4c9e569
sm90: reject GVA inputs until native support lands
Jun 26, 2026
7395d4b
reorganize sm90 tests and benches
Jun 26, 2026
7e102d9
rename hopper_fused_fwd.py -> hopper_prefill.py
Jun 26, 2026
bb096cb
clean up the sm90 wrapper: import cycle, validation, fp32-only state
Jun 26, 2026
4d23c3d
consolidate sm90 test files
Jun 27, 2026
a1d3112
[KDA] add intra-card CP for the sm90 prefill
Jun 26, 2026
7c74e3f
bench: iqr-mean timing + sm90 cp vs fla comparison
Jun 27, 2026
371277a
fill the whole SM array when splitting long sequences
Jun 27, 2026
c9f263a
remove unreachable asserts in internal launchers
Jun 28, 2026
0213873
handle varlen beta in-kernel via K1-emitted ws_beta
Jun 28, 2026
7abe116
tidy cp comments and pad sentinels
Jun 28, 2026
8b5e7c6
rewrite cula/kda README for users
Jun 29, 2026
29f56e0
test cp against fla ground truth + determinism
Jun 29, 2026
649edcc
simplify arch-context and varlen metadata helpers
Jun 29, 2026
0cd2aef
fix ws_beta sizing in intracard cp
Jun 29, 2026
f703e83
split the cp driver into plan + pipeline steps
Jun 29, 2026
ad5fa3f
simplify cp tests, add cp-on/off speedup bench
Jun 29, 2026
bc1d714
assert varlen args in sm100 bwd_wy_dqkg_fused
Jun 30, 2026
0e616cf
fix varlen jit compile storm (config-keyed cache)
Jun 30, 2026
dc7f5d3
add sm90 benchmark numbers and cp debug notes
Jun 30, 2026
52ecfd1
drop from-future-annotations in sm90 kernel files (cutlass 4.4.2 Cons…
Jul 4, 2026
8644437
fix K2 init/final state fakes sharing one dynamic sym
Jul 4, 2026
21c0138
launch K1/K2 through tvm-ffi instead of JitExecutor
Jul 4, 2026
4cf9803
pool sm90 workspaces in a grow-only arena
Jul 4, 2026
f0de65c
cut cp driver host overhead (968 -> 288 us)
Jul 4, 2026
96aab00
pre_scan: issue kd@M ahead of the S-chain
Jul 4, 2026
a01e751
cost-model engage decision for cp auto mode
Jul 4, 2026
89d95cf
same future-annotations fix for the sm100 cp merge kernel
Jul 4, 2026
f387dc6
slim the prefill wrapper hot path
Jul 4, 2026
1d1a4d6
K1 reads beta straight from its packed [T,H] layout
Jul 5, 2026
44886f3
duration-balanced segment planning for ragged batches
Jul 5, 2026
f8962f3
launch longest segments first
Jul 5, 2026
cb55304
test ragged auto-plan cp + workspace arena reuse
Jul 5, 2026
dc9946c
trim redundant comments and stale docs
Jul 5, 2026
0513762
rework cp dispatch: fold the policy layer into the planner
Jul 6, 2026
594a884
unify copyright headers
Jul 6, 2026
3208b28
slim cp dispatch comments
Jul 6, 2026
f7cebfa
style: apply ruff formatting
Jul 6, 2026
3fd6837
planner polish: clearer names, worked example, drop the env-warning shim
Jul 6, 2026
eead625
reuse the cpu cu_seqlens copy in sm100 cp dispatch
Jul 7, 2026
74ba7d4
[KDA] keep the CUDA C++ fused prefill as a separate backend
Jul 16, 2026
29c04e0
rename the cutedsl backend export to flashkda_prefill
Jul 16, 2026
da7534a
drop dead workspace helpers and decorative comments
Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BENCHMARK_H200.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ Summary (28 configs): **avg=1.58x**, min=1.02x, max=2.51x.
To reproduce:

```bash
python benchmarks/bench_kda_fused_fwd.py --mode both
python benchmarks/bench_kda_sm90_prefill.py --mode both
```
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,23 +149,25 @@ python benchmarks/generate_benchmark_hopper_md.py

```bash
# Tests for modular KDA forward against FLA Triton implementation
python -m pytest tests/test_kda_compare_fla.py -v
python -m pytest tests/test_kda_sm100_chunk_vs_fla.py -v
# Tests for modular KDA forward against naive KDA reference
python -m pytest tests/test_kda.py -v
# Tests for KDA fused forward
python -m pytest tests/test_kda_sm100_chunk_vs_naive.py -v
# Tests for the KDA fused forward (SM90 CUDA C++)
python -m pytest tests/test_kda_fused_fwd.py -v
# Tests for Lightning Attention fused forward
python tests/test_lightning_attn.py
# Tests for the SM90 CuTeDSL two-kernel prefill + intracard CP (vs FLA)
python -m pytest tests/test_kda_sm90_prefill_vs_fla.py tests/test_kda_sm90_intracard_cp.py -v
# Tests for Lightning Attention prefill
python tests/test_lightning_sm100_prefill.py
# Tests for Lightning Attention decode
python -m pytest tests/test_la_decode.py -v
python -m pytest tests/test_lightning_decode.py -v

# test_kda.py and test_kda_compare_fla.py support a fast/slow split.
# test_kda_sm100_chunk_vs_naive.py and test_kda_sm100_chunk_vs_fla.py support a fast/slow split.
# Fast (default) β€” representative correctness paths for default CI and local iteration
python -m pytest tests/test_kda.py tests/test_kda_compare_fla.py -v
python -m pytest tests/test_kda_sm100_chunk_vs_naive.py tests/test_kda_sm100_chunk_vs_fla.py -v
# Slow β€” broader stress coverage for nightly or manual runs
python -m pytest -m kda_slow tests/test_kda.py tests/test_kda_compare_fla.py -v
python -m pytest -m kda_slow tests/test_kda_sm100_chunk_vs_naive.py tests/test_kda_sm100_chunk_vs_fla.py -v
# Full sweep (fast + slow) β€” run before submitting a PR
python -m pytest -m kda_full tests/test_kda.py tests/test_kda_compare_fla.py -v
python -m pytest -m kda_full tests/test_kda_sm100_chunk_vs_naive.py tests/test_kda_sm100_chunk_vs_fla.py -v
```

<details>
Expand Down
22 changes: 12 additions & 10 deletions REPO_LAYOUT.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ cuLA/
β”œβ”€β”€ cula/ # Python package (pip install -e .)
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ utils.py # arch asserts, get_pre_scan, cu_seqlens helpers, ...
β”‚ β”œβ”€β”€ cudac.py # re-export shim over the compiled C++ extension(s)
β”‚ β”‚
β”‚ β”œβ”€β”€ kda/ # KDA PUBLIC API + autograd + dispatch (NO kernels)
β”‚ β”‚ β”œβ”€β”€ __init__.py # lazy PUBLIC API: chunk_kda, kda_prefill_hopper,
Expand All @@ -16,7 +15,7 @@ cuLA/
β”‚ β”‚ β”œβ”€β”€ chunk_fwd.py # chunk_kda_fwd β€” fwd orchestration (lazy-imports kernels)
β”‚ β”‚ β”œβ”€β”€ chunk_intra.py # fwd intra (C++ ext) + bwd intra (Triton)
β”‚ β”‚ β”œβ”€β”€ chunk_bwd.py # chunk_kda_bwd β€” Triton + FLA + CuTeDSL + C++ mix
β”‚ β”‚ └── hopper_fused_fwd.py # cula_kda_prefill (=kda_prefill_hopper) β€” SM90 prefill via the C++ kernel (cula.cudac)
β”‚ β”‚ └── flashkda.py # cula_kda_prefill (=flashkda_prefill) β€” SM90 two-kernel K1+K2 prefill, fwd-only
β”‚ β”‚
β”‚ β”œβ”€β”€ lightning/ # [non-KDA] Lightning Attention operator (LinearAttentionChunkwiseDecay, lightning_attn_fwd, linear_attention_decode)
β”‚ β”‚ └── __init__.py
Expand All @@ -25,15 +24,19 @@ cuLA/
β”‚ β”œβ”€β”€ __init__.py # exports kda_decode, fused_sigmoid_..., linear_attention_decode
β”‚ β”œβ”€β”€ inv.py / ptx.py # shared low-level helpers
β”‚ β”œβ”€β”€ sm100/ # SM100 shared helper only
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── ptx.py # shared PTX helpers (used by KDA + lightning kernels)
β”‚ β”‚
β”‚ β”œβ”€β”€ kda/ # β˜… KDA Python backends β€” by arch (sm100 today)
β”‚ β”‚ β”œβ”€β”€ policy.py # SM100 CP dispatch policy: use_intracard_cp:"auto"|bool
β”‚ β”œβ”€β”€ kda/ # β˜… ALL KDA backend kernels β€” by arch (sm90 / sm100)
β”‚ β”‚ β”œβ”€β”€ policy.py # CP dispatch policy: sm90/sm100 decisions, use_intracard_cp:"auto"|bool
β”‚ β”‚ β”œβ”€β”€ sm100/ # SM100 (Blackwell) modular-chunk kernels
β”‚ β”‚ β”‚ β”œβ”€β”€ delta_h.py # recurrence (chunk_gated_delta_rule_fwd_h)
β”‚ β”‚ β”‚ β”œβ”€β”€ fwd_o.py # output (chunk_gla_fwd_o)
β”‚ β”‚ β”‚ β”œβ”€β”€ bwd_wy_dqkg.py# backward wy/dqkg fused (used by chunk_bwd)
β”‚ β”‚ β”‚ └── cp/ # SM100 intracard-CP: chunk_delta_h, pre_scan, merge
β”‚ β”‚ β”œβ”€β”€ sm90/ # SM90 (Hopper) two-kernel FlashKDA prefill, fwd-only
β”‚ β”‚ β”‚ β”œβ”€β”€ fwd.py k1.py k2.py # flash_kda_fwd β†’ launch_k1 (prepare) + launch_k2 (recurrence)
β”‚ β”‚ β”‚ └── cp/ # SM90 intracard-CP: flashkda, pre_scan, merge, plan
β”‚ β”‚ β”œβ”€β”€ decode/ # single-token + MTP decode
β”‚ β”‚ β”‚ β”œβ”€β”€ cute.py # kda_decode / fused_sigmoid_gating_delta_rule_update (CuTe DSL)
β”‚ β”‚ β”‚ β”œβ”€β”€ mtp.py # kda_decode_mtp recurrent / recurrent_ws MTP verify (CuTe DSL)
Expand All @@ -49,10 +52,9 @@ cuLA/
β”‚ └── experimental/
β”‚ └── linear_attn_prototype.py # [non-KDA] unwired normalized-linear-attn prototype
β”‚
β”œβ”€β”€ csrc/ # CUDA C++ / CUTLASS
β”‚ β”œβ”€β”€ api/{kda_sm90.cu, kda_sm100.cu} # PyBind11 (cula.cudac): SM90 prefill + SM100 chunk intra/recompute_w_u
β”‚ β”œβ”€β”€ kda/sm90/ # SM90 (Hopper) KDA C++ kernels (CUTLASS 3.x, TMA/wgmma)
β”‚ β”œβ”€β”€ kda/sm100/ # Blackwell KDA C++ kernels (CUTLASS 3.x + UMMA)
β”œβ”€β”€ csrc/ # CUDA C++ / CUTLASS β€” SM100 ONLY
β”‚ β”œβ”€β”€ api/{pybind.cu, kda_sm100.cu} # PyBind11 (module cula.cudac): chunk intra + recompute_w_u
β”‚ β”œβ”€β”€ kda/sm100/ # Blackwell KDA C++ kernels (CUTLASS 3.x + UMMA); kda_fwd_sm100.cu is the only .cu
β”‚ └── kerutils/include/ # shared C++ headers (generic device helpers sm80/sm90/sm100, host)
β”‚
β”œβ”€β”€ benchmarks/ tests/ docs/
Expand All @@ -66,8 +68,8 @@ cuLA/

| Directory | Language | Description |
|-----------|----------|-------------|
| `cula/kda/` | Python | KDA **public API only** β€” autograd + dispatch, no kernels. Two prefill entries: modular chunk `chunk_kda` (SM100) and `kda_prefill_hopper` (SM90, driving the C++ kernel). |
| `cula/ops/kda/` | Python (CuTe DSL) | **KDA Python backends**, by arch: `sm100/` (+cp), `decode/`, `experimental/`, plus `policy.py` (CP dispatch). |
| `cula/kda/` | Python | KDA **public API only** β€” autograd + dispatch, no kernels. Two prefill entries: modular chunk `chunk_kda` (SM100) and two-kernel K1+K2 `flashkda_prefill` (SM90; the CUDA C++ fused path stays on `kda_prefill_hopper`). See [`cula/kda/README.md`](cula/kda/README.md). |
| `cula/ops/kda/` | Python (CuTe DSL) | **All KDA backends**, by arch: `sm100/` (+cp), `sm90/` (+cp), `decode/`, `experimental/`, plus `policy.py` (CP dispatch). Both prefill backends are chunked forward; arch is the discriminator (1 impl each). |
| `cula/ops/lightning/` Β· `cula/ops/experimental/` | Python (CuTe DSL) | `[non-KDA]` Lightning/linear attention kernels. |
| `cula/ops/{inv,ptx}.py`, `cula/ops/sm100/ptx.py` | Python | Shared low-level helpers (kept in place; not KDA-specific). |
| `csrc/kda/{sm90,sm100}/` Β· `csrc/api/` | CUDA C++ | Hopper SM90 prefill + Blackwell SM100 (chunk intra + recompute_w_u), exposed as `cula.cudac`. |
73 changes: 71 additions & 2 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ cuLA provides two KDA kernel implementations targeting different GPU architectur
|---|---|---|
| Modular Forward | Blackwell (SM100) | `from cula.kda import chunk_kda` |
| Fused Forward | Hopper (SM90) | `from cula.kda import kda_prefill_hopper` |
| Two-Kernel Prefill | Hopper (SM90) | `from cula.kda import flashkda_prefill` |

Both are drop-in replacements for [FLA](https://github.com/fla-org/flash-linear-attention)'s `chunk_kda` β€” just change the import.

Expand Down Expand Up @@ -115,9 +116,77 @@ print(f'Final state shape: {final_state.shape}') # [2, 32, 128, 128]

---

## Intra-Card Context Parallel (chunk_delta_h)
### Two-Kernel Prefill (SM90 β€” Hopper)

cuLA includes an intra-card context parallel (CP) path for `chunk_gated_delta_rule_fwd_h`. Long sequences are split into sub-sequences, processed independently in parallel, then merged via a prefix-scan step β€” unlocking sequence-dimension parallelism on a single GPU.
The SM90 prefill is a **two-kernel pipeline** β€” K1 (Prepare) β†’ K2 (Recurrence) β€” *not* a single fused kernel. Intra-chunk attention, inter-chunk state propagation, and output are computed across the two kernels. **Forward-only; backward is not yet implemented.**

#### Example

```python
import torch
from cula.kda import flashkda_prefill

B, T, H, K, V = 2, 2048, 32, 128, 128
device = 'cuda'

q = torch.randn(B, T, H, K, device=device, dtype=torch.bfloat16)
k = torch.randn(B, T, H, K, device=device, dtype=torch.bfloat16)
v = torch.randn(B, T, H, V, device=device, dtype=torch.bfloat16)
g = torch.randn(B, T, H, K, device=device, dtype=torch.bfloat16) * 0.1
beta = torch.randn(B, T, H, device=device, dtype=torch.bfloat16).sigmoid()
A_log = torch.randn(H, device=device, dtype=torch.float32) * 0.01
dt_bias = torch.zeros(H * K, device=device, dtype=torch.float32)
init_state = torch.zeros(B, H, K, V, device=device, dtype=torch.float32)

o, final_state = flashkda_prefill(
q=q, k=k, v=v, g=g, beta=beta,
A_log=A_log, dt_bias=dt_bias,
initial_state=init_state,
output_final_state=True,
use_qk_l2norm_in_kernel=True,
use_gate_in_kernel=True,
safe_gate=True,
lower_bound=-5.0,
)

print(f'Output shape: {o.shape}') # [2, 2048, 32, 128]
print(f'Final state shape: {final_state.shape}') # [2, 32, 128, 128]
```

**Notes**

- Mainly **suitable for large-batch inference**. When both batch size and head count are small, throughput on long sequences is recovered by enabling **intra-card CP** (`use_intracard_cp`), which parallelizes the sequence-length dimension on a single GPU β€” see [Intra-Card Context Parallel](#intra-card-context-parallel).
- **Matrix inversion uses fp16 precision**, which is faster and occupies less shared memory but introduces minor numerical differences compared to tf32 inversion.
- **Intra-subchunk attention uses g-first as anchor**, which causes some numerical differences compared with the FLA Triton implementation (FLA uses g-half as anchor in the diagonal).

---

## Intra-Card Context Parallel

Long sequences can be split into sub-sequences, processed in parallel on one GPU, and merged via a prefix scan β€” unlocking sequence-dimension parallelism. **Default off; inference-only.** Two surfaces:

### SM90 β€” via `flashkda_prefill(use_intracard_cp=...)`

Pass `use_intracard_cp` (alias `use_cp`) to the Hopper prefill:

- **`"auto"`** β€” the auto-router enables CP only when it predicts a speedup, otherwise runs serial (no error, no regression).
- **`True`** β€” force CP; raises if the shape cannot be meaningfully split.
- **`False`** (default) β€” CP off.

Works with **any sequence length** (non-CHUNK-aligned is handled internally) and **dense or varlen** input. The auto decision uses the device SM count plus two tunables in `cula/ops/kda/sm90/cp/plan.py` (`CULA_KDA_CP_RERUN_RATIO`, `CULA_KDA_CP_AUTO_MIN_SEG_TILES`).

```python
o, final_state = flashkda_prefill(
q=q, k=k, v=v, g=g, beta=beta, A_log=A_log, dt_bias=dt_bias,
cu_seqlens=cu_seqlens, # varlen packed (int32)
output_final_state=True, use_gate_in_kernel=True, safe_gate=True, lower_bound=-5.0,
use_intracard_cp="auto", # "auto" | True | False (default False)
)
```

### SM100 β€” low-level `chunk_gated_delta_rule_fwd_h`

cuLA also exposes intra-card CP directly on the SM100 recurrence `chunk_gated_delta_rule_fwd_h`, gated by an environment variable.

**Requirements**

Expand Down
194 changes: 194 additions & 0 deletions benchmarks/bench_kda_sm90_cp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
# Copyright 2025-2026 Ant Group Co., Ltd.
# SPDX-License-Identifier: Apache-2.0

"""
bench_kda_sm90_cp.py β€” Benchmark: SM90 intracard CP speedup (CP-on vs CP-off)

Measures the speedup of the SM90 intracard context-parallel path against
the serial K1+K2 baseline across varlen configurations.

Usage:
python bench_kda_sm90_cp.py [--ncu] [--sanitizer]

With --ncu, warmup=1 and iters=1 for ncu profiling:
ncu --set full -o report python bench_kda_sm90_cp.py --ncu
"""

import argparse
import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))

import torch

from benchmarks.utils import SEED, exclusive_cumsum, prepare_safe_gate_inputs, set_seed
from cula.kda import flashkda_prefill as cula_kda_prefill
from cula.utils import assert_hopper, get_device_sm_count

D = 128
H_VALUES = [4, 8]
WARMUP = 10
N_ITERS = 100
NCU_MODE = False
SANITIZER_MODE = False

# (tag, seq_lens) β€” each entry is tested at every H in H_VALUES.
# SM90 CHUNK=16, so sequences need to be long enough (>= ~8K tiles) for CP to pay off.
CONFIGS = [
("T=4K", [4096]),
("T=8K", [8192]),
("T=16K", [16384]),
("T=32K", [32768]),
("T=64K", [65536]),
("2x16K", [16384, 16384]),
("32K+4K", [32768, 4096]),
("32K+1K", [32768, 1024]),
("64K+1K", [65536, 1024]),
("64K+2x1K", [65536, 1024, 1024]),
("64K+5x1K", [65536] + [1024] * 5),
]


def time_kernel(fn, warmup=None, n_iters=None):
if warmup is None:
warmup = 1 if (NCU_MODE or SANITIZER_MODE) else WARMUP
if n_iters is None:
n_iters = 1 if (NCU_MODE or SANITIZER_MODE) else N_ITERS
for _ in range(warmup):
fn()
torch.cuda.synchronize()
start_evt = torch.cuda.Event(enable_timing=True)
end_evt = torch.cuda.Event(enable_timing=True)
start_evt.record()
for _ in range(n_iters):
fn()
end_evt.record()
torch.cuda.synchronize()
return start_evt.elapsed_time(end_evt) / n_iters


def run_kernel(q, k, v, g, beta, scale, A_log, dt_bias, cu_seqlens, lower_bound, *, use_cp):
cula_kda_prefill(
q,
k,
v,
g,
beta,
scale=scale,
A_log=A_log,
dt_bias=dt_bias,
cu_seqlens=cu_seqlens,
output_final_state=False,
safe_gate=True,
lower_bound=lower_bound,
use_intracard_cp="auto" if use_cp else False,
)


def bench_cp(h_values, configs):
print("\n" + "=" * 100)
print(" SM90 Intracard CP Benchmark: CP-on vs CP-off")
print("=" * 100)

device = torch.device("cuda")
assert_hopper(device)
num_sms = get_device_sm_count(device)
print(f" [Device] {torch.cuda.get_device_name(0)} SMs={num_sms}")
results = []

for H in h_values:
for tag, seq_lens in configs:
set_seed(SEED)
torch.cuda.empty_cache()

total_T = sum(seq_lens)
cu_seqlens = torch.tensor(exclusive_cumsum(seq_lens), dtype=torch.int32, device=device)
inputs = prepare_safe_gate_inputs(1, total_T, H, D, device, cu_seqlens=cu_seqlens, seed=SEED)
q, k, v, g, beta = inputs["q"], inputs["k"], inputs["v"], inputs["g"], inputs["beta"]
A_log, dt_bias = inputs["A_log"], inputs["dt_bias"]
scale, lower_bound = inputs["scale"], inputs["lower_bound"]

common = dict(
q=q,
k=k,
v=v,
g=g,
beta=beta,
scale=scale,
A_log=A_log,
dt_bias=dt_bias,
cu_seqlens=cu_seqlens,
lower_bound=lower_bound,
)

ms_off = time_kernel(lambda: run_kernel(**common, use_cp=False))
ms_on = time_kernel(lambda: run_kernel(**common, use_cp=True))

speedup = ms_off / ms_on if ms_on > 0 else float("inf")
r = dict(tag=tag, H=H, total_T=total_T, ms_off=ms_off, ms_on=ms_on, speedup=speedup)
results.append(r)

del q, k, v, g, beta, A_log, dt_bias, inputs
torch.cuda.empty_cache()

return results


def print_report(results, h_values):
sep = "=" * 95
print(f"\n\n{sep}")
print(" BENCHMARK REPORT: SM90 Intracard CP")
print(" CP-on vs CP-off (intracard_prefill vs flash_kda_fwd)")
print(f" D={D} dtype=bf16 safe_gate=True")
wu = 1 if (NCU_MODE or SANITIZER_MODE) else WARMUP
ni = 1 if (NCU_MODE or SANITIZER_MODE) else N_ITERS
mode_tag = " [NCU mode]" if NCU_MODE else (" [Sanitizer mode]" if SANITIZER_MODE else "")
print(f" Warmup={wu} Iters={ni}{mode_tag}")
print(sep)

for H_val in h_values:
h_results = [r for r in results if r["H"] == H_val]
if not h_results:
continue
print(f"\n [H={H_val}]")
print(f" {'─' * 80}")
print(f" {'config':<20s} {'T':>7s} β”‚ {'CP_off(ms)':>10s} {'CP_on(ms)':>10s} {'Speedup':>8s}")
print(f" {'─' * 80}")
for r in h_results:
print(f" {r['tag']:<20s} {r['total_T']:>7d} β”‚ {r['ms_off']:>10.4f} {r['ms_on']:>10.4f} {r['speedup']:>7.2f}x")
print(f" {'─' * 80}")

speedups = [r["speedup"] for r in results]
if speedups:
geo = 1.0
for s in speedups:
geo *= s
geo = geo ** (1 / len(speedups))
print(f"\n All configs: geo-mean={geo:.2f}x best={max(speedups):.2f}x worst={min(speedups):.2f}x")

print(f"\n{sep}\n")


def main():
parser = argparse.ArgumentParser(description="bench_kda_sm90_cp: SM90 intracard CP speedup")
parser.add_argument("--ncu", action="store_true", help="NCU profiling mode: warmup=1, iters=1")
parser.add_argument("--sanitizer", action="store_true", help="Sanitizer mode: warmup=1, iters=1")
args = parser.parse_args()

global NCU_MODE, SANITIZER_MODE
if args.ncu:
NCU_MODE = True
print("[NCU mode] warmup=1, iters=1")
if args.sanitizer:
SANITIZER_MODE = True
print("[Sanitizer mode] warmup=1, iters=1")

results = bench_cp(H_VALUES, CONFIGS)
print_report(results, H_VALUES)
return results


if __name__ == "__main__":
main()
Loading