Repository navigation
fix(kernel): correct jit tensor matcher checks and pdl arch guards - #638
Conversation
- with_dtype<Ts...>(sym) and with_device<Codes...>(sym) applied no options, so their dtype and device checks accepted anything - index and store list kDLROCM, which the ignored check let through - emit griddepcontrol only for !FREETOKEN_USE_ROCM && __CUDA_ARCH__ >= 900; it failed ptxas below sm_90 and does not exist on HIP - declare the launch attribute cache only on CUDA - use kWarpThreads for the index and store launch geometry Co-authored-by: zihaomu <zihaomu@outlook.com>
- a zero dimension counted as unset, so empty inputs failed with "Size value is not set" and a zero-row tensor did not catch a mismatched one
|
AMD validation for 8fab7c4: the main HIP changes pass the targeted non-empty index/store checks. There is one remaining empty-batch issue, exercised by four parameter combinations. A small local follow-up patch fixes all four; it has now been tested on both runtime stacks below. Test machine and environments
Before / after resultsThese are targeted kernel/adjacent tests, not the entire repository test suite.
The original four failing combinations were index/store x int32/int64 indices, all with a zero-length batch. Each was confirmed in a fresh Python process to avoid a failed HIP launch affecting the next test. They are additional boundary tests, not four previously passing upstream tests regressing. Coverage includes FP32/FP16/BF16, split factors 1/2/4, masked vocabulary indexing, strided store buffers and untouched padding, invalid tensor contracts, and non-default-stream graph replay. The larger 7.14 set also includes pinned/fast-index-copy, fused multi-bank copy, launch-kwargs, cache-version, and ROCm-architecture tests. The two old #133 tests expecting an explicit PDL rejection on HIP were deselected: this PR intentionally changes that contract to ordinary stream ordering. Instead, 12 explicit Why the four empty-batch cases failFor The message says CUDA because of the shared error wrapper; this was HIP execution. Changing the unbound-size sentinel from Importantly, the same four inputs also fail before this PR:
The relevant C++ kernel sources and index/store Python wrappers are unchanged between that baseline and checked main Locally tested follow-up diffReturn after the existing tensor/row-size validation, but before constructing/launching a zero-sized grid. This preserves invalid-input rejection and does not change the non-empty path. No ROCm-specific conditional or change to the shared diff --git a/python/freetoken/kernel/csrc/jit/index.cu b/python/freetoken/kernel/csrc/jit/index.cu
--- a/python/freetoken/kernel/csrc/jit/index.cu
+++ b/python/freetoken/kernel/csrc/jit/index.cu
@@ -134,6 +134,11 @@ struct IndexKernel {
RuntimeCheck(entry_size == element_size,
"IndexKernel: element_size mismatch.");
+ // Empty batches are valid no-ops; do not launch a zero-sized grid.
+ if (num_indices == 0) {
+ return;
+ }
+
constexpr auto kWarpPerBlock = num_threads / device::kWarpThreads;
const auto num_warps = num_splits * num_indices;
const auto num_blocks = div_ceil(num_warps, kWarpPerBlock);
diff --git a/python/freetoken/kernel/csrc/jit/store.cu b/python/freetoken/kernel/csrc/jit/store.cu
--- a/python/freetoken/kernel/csrc/jit/store.cu
+++ b/python/freetoken/kernel/csrc/jit/store.cu
@@ -96,6 +96,11 @@ struct StoreKernel {
const auto kv_cache_stride = X.unwrap() * dtype_size;
const auto kv_input_stride = Y.unwrap() * dtype_size;
+ // Empty batches are valid no-ops; do not launch a zero-sized grid.
+ if (length == 0) {
+ return;
+ }
+
const auto params = StoreKernelParams{
.k_cache = k_cache.data_ptr(),
.v_cache = v_cache.data_ptr(),The 22 extra local regression cases cover:
Observed patched pytest summaries: Minimal reproducer for the four original failuresSave as import sys
import torch
from freetoken.kernel import indexing, store_cache
kernel, index_dtype = sys.argv[1:]
bank = torch.ones((8, 64), device="cuda")
other = bank.clone()
indices = torch.empty(0, dtype=getattr(torch, index_dtype), device="cuda")
empty = torch.empty((0, 64), device="cuda")
if kernel == "index":
assert indexing(bank, indices, output=empty) is empty
else:
store_cache(bank, other, indices, empty, empty)
torch.cuda.synchronize()
torch.testing.assert_close(bank, torch.ones_like(bank), rtol=0, atol=0)
torch.testing.assert_close(other, torch.ones_like(other), rtol=0, atol=0)
print("PASS")FREETOKEN_DISABLE_KERNEL_CACHE=1 python repro_empty.py index int32
FREETOKEN_DISABLE_KERNEL_CACHE=1 python repro_empty.py index int64
FREETOKEN_DISABLE_KERNEL_CACHE=1 python repro_empty.py store int32
FREETOKEN_DISABLE_KERNEL_CACHE=1 python repro_empty.py store int64Use a fresh Recommendation: keep the shared-header fix, add the two local no-op guards and regression coverage. This resolves the remaining four cases on the tested AMD machine. The patch above is locally validated but has not been committed/pushed to this PR. No NVIDIA, RDNA3/CDNA, multi-GPU, full-model serving, or performance-regression claim is being made here. |
- an empty batch launched a zero-sized grid, which CUDA and HIP reject; the leftover error then failed the next GPU call Co-authored-by: zihaomu <zihaomu@outlook.com>
|
Added in 1f31fbc, thanks! Merging. |
Use upstream FlashML-org#638 and FlashML-org#134; integrate incremental FlashML-org#135 and FlashML-org#378 plus the updated standalone FlashML-org#491 before FlashML-org#587. Retain the probed FlashML-org#378 handshake and upstream installation document.
Upstream's kernel perf PRs overlap our own FP4/KV work: - mxfp4 split-K GEMV (FlashML-org#653): keep upstream's column-pair int16 reads, decode the codes with our bit-placement e2m1_code_f32 instead of the LUT. - nvfp4 Marlin decode (FlashML-org#631): take upstream's kernel body (same fp16 bit trick plus register-layout and wide activation loads); import both _nvfp4_pair_f16 and _nvfp4_pair_f32. - Triton extend attention (FlashML-org#632): keep upstream's mask-free tile ranges and exp2 math; quantized KV caches (fp8 codes with per-token scales, nvfp4) go through the new _extend_attend_kv_quant_tile inside the same loops, bf16 stays on upstream's _extend_attend_tile. Decode keeps our per-kv-head grid (group % VALID_BLOCK_H != 0) plus upstream's 64-key sliding-window tiles. - CUDA JIT (FlashML-org#638, FlashML-org#134): upstream's sm_90/ROCm PDL guards and kDLROCM device matchers, with our MSVC-safe __forceinline__ and .template spellings. - docs/install_amd.md: keep both ROCm sections. Not GPU-tested (no GPU on the box); CPU test runs were unreliable on the current unstable host. Assisted-by: Claude Opus 5.5
…lashML-org#638) * fix(kernel): correct jit matcher options and pdl arch guards - with_dtype<Ts...>(sym) and with_device<Codes...>(sym) applied no options, so their dtype and device checks accepted anything - index and store list kDLROCM, which the ignored check let through - emit griddepcontrol only for !FREETOKEN_USE_ROCM && __CUDA_ARCH__ >= 900; it failed ptxas below sm_90 and does not exist on HIP - declare the launch attribute cache only on CUDA - use kWarpThreads for the index and store launch geometry Co-authored-by: zihaomu <zihaomu@outlook.com> * fix(kernel): treat zero as a matched size in tensor matcher - a zero dimension counted as unset, so empty inputs failed with "Size value is not set" and a zero-row tensor did not catch a mismatched one * fix(kernel): return early on empty index and store batches - an empty batch launched a zero-sized grid, which CUDA and HIP reject; the leftover error then failed the next GPU call Co-authored-by: zihaomu <zihaomu@outlook.com> --------- Co-authored-by: zihaomu <zihaomu@outlook.com>
A smaller version of #133 by @zihaomu, fixed in the shared headers instead of at the index/store call sites. Supersedes #133.
@zihaomu could you test this on AMD? Only NVIDIA was tested here.
with_dtype<Ts...>(sym)/with_device<Codes...>(sym)applied no options, so index/store accepted float indices and CPU tensors. Index and store now listkDLROCM.griddepcontrolis emitted only for!FREETOKEN_USE_ROCM && __CUDA_ARCH__ >= 900.use_pdl=Truefailed to build below sm_90 and on HIP.kWarpThreadsin index/store, and the launch attribute cache only on CUDA.Tested on RTX PRO 6000 (sm_120):
pytest tests/ -m "not slow"gives the same result as main, and gpt-oss-20b e2e output is identical to main.