Skip to content

Commit 8453f6c

Browse files
q10meta-codesync[bot]
authored andcommitted
Fix HIP grid overflow in expand_into_jagged_permute_cuda (#5906)
Summary: Pull Request resolved: #5906 X-link: https://github.com/facebookresearch/FBGEMM/pull/2824 Apply the same `#ifdef USE_ROCM` cap pattern used in D104903707 / D104937969 / parent diffs to the launch site in `expand_into_jagged_permute_cuda` in `sparse_expand_into_jagged_permute.cu`. The launch uses `dim3(kWarpSize=32, T_blocks=32)` (block size 1024) and grid `cuda_calc_xblock_count(permute_size, 32)`. Once `permute_size > 2^27 ≈ 134M`, total threads exceed the HIP `2^32` limit and the launch is rejected on ROCm. `expand_into_jagged_permute_kernel` already grid-strides over `t` (line 27), so capping the grid is correctness-preserving. The `#ifdef USE_ROCM / #else / #endif` selector keeps NVIDIA codegen bit-identical and unconditionally caps on ROCm. Same family of fix as: - D104903707 (permute_1D_sparse_data — parent diff) - D104937969 (permute_2D_sparse_data — parent diff) Reviewed By: henrylhtsang Differential Revision: D104951014 fbshipit-source-id: c8cd6ec332b8e0284d72e6da7f869def132a3583
1 parent 90f91ae commit 8453f6c

2 files changed

Lines changed: 97 additions & 3 deletions

File tree

fbgemm_gpu/src/sparse_ops/sparse_expand_into_jagged_permute.cu

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@ DLL_PUBLIC Tensor expand_into_jagged_permute_cuda(
5555
// number of table per block
5656
constexpr int32_t T_blocks = kMaxThreads / kWarpSize;
5757
dim3 threads(kWarpSize, T_blocks);
58-
const auto blocks = cuda_calc_xblock_count(permute_size, T_blocks);
58+
// HIP enforces a hard limit of 2^32 total threads per launch (unlike CUDA,
59+
// which silently wraps). expand_into_jagged_permute_kernel grid-strides
60+
// over t (line 27), so capping is correctness-preserving.
61+
// See: https://github.com/ROCm/hip/issues/2253
62+
const auto blocks = utils::cuda::cap_grid_dim_x(
63+
cuda_calc_xblock_count(permute_size, T_blocks),
64+
kMaxThreads,
65+
at::cuda::getCurrentCUDAStream());
5966
AT_DISPATCH_INDEX_TYPES(
6067
permute.scalar_type(), "expand_into_jagged_permute_kernel", [&] {
6168
using offsets_t = index_t;

fbgemm_gpu/test/jagged/expand_into_jagged_permute_test.py

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,21 @@
2121

2222
if open_source:
2323
# pyre-ignore[21]
24-
from test_utils import gpu_available, on_oss_clang, optests
24+
from test_utils import (
25+
gpu_available,
26+
gpu_memory_lt_gb,
27+
gpu_unavailable,
28+
on_oss_clang,
29+
optests,
30+
)
2531
else:
26-
from fbgemm_gpu.test.test_utils import gpu_available, on_oss_clang, optests
32+
from fbgemm_gpu.test.test_utils import (
33+
gpu_available,
34+
gpu_memory_lt_gb,
35+
gpu_unavailable,
36+
on_oss_clang,
37+
optests,
38+
)
2739

2840

2941
@optests.generate_opcheck_tests(additional_decorators=additional_decorators)
@@ -99,6 +111,81 @@ def test_expand_into_jagged_permute(
99111
output_permute_gpu.cpu(), output_permute_ref_tensor
100112
)
101113

114+
@unittest.skipIf(*gpu_unavailable)
115+
# Skip on GPUs with insufficient HBM. Inputs are int32 of size
116+
# ~permute_size, totaling ~1.5 GiB at the chosen permute_size.
117+
@unittest.skipIf(*gpu_memory_lt_gb(4))
118+
def test_expand_into_jagged_permute_large_grid(self) -> None:
119+
"""
120+
Reproduces the HIP grid-overflow bug in expand_into_jagged_permute_cuda
121+
and verifies output correctness at the same scale.
122+
123+
With T_blocks = kMaxThreads/kWarpSize = 32 and dim3(32, 32)
124+
(block size 1024), the launch grid is
125+
cuda_calc_xblock_count(permute_size, 32). For
126+
permute_size > 2**27, total threads exceed the HIP 2**32
127+
limit, causing FBGEMM_LAUNCH_KERNEL ->
128+
KernelLauncher::checkThreadCountNotExceeded to TORCH_CHECK-fail
129+
on ROCm pre-fix. With the production fix in place, this test
130+
additionally validates output correctness against the CPU
131+
dispatch of the same op — the GPU output must match the CPU
132+
reference element-for-element.
133+
134+
``length`` (per segment) is sparse: all zero except for three
135+
known non-zero positions (start / middle / end of the logical
136+
range), so HBM usage stays bounded (~1.6 GiB int32) while the
137+
permutation expansion logic is still exercised. ``permute`` is
138+
a deterministic non-identity circular shift (``perm[i] != i``
139+
everywhere), so any "kernel computed identity instead of
140+
permutation" bug surfaces in the assertion below.
141+
"""
142+
143+
# Choose permute_size so that total threads strictly exceeds 2**32:
144+
# cuda_calc_xblock_count(permute_size, 32) * 1024 ~= permute_size * 32;
145+
# need permute_size > 2**27.
146+
permute_size = (1 << 27) + 1
147+
148+
device = torch.device(torch.accelerator.current_accelerator() or "cuda")
149+
150+
# Sparse non-zero lengths at start / middle / end. Total = 10.
151+
lengths_cpu = torch.zeros(permute_size, dtype=torch.int32)
152+
lengths_cpu[0] = 3
153+
lengths_cpu[permute_size // 2] = 5
154+
lengths_cpu[permute_size - 1] = 2
155+
156+
# Deterministic non-identity permute: circular shift by +1.
157+
# perm_cpu[0] == permute_size - 1 and perm_cpu[i] == i - 1 for
158+
# i >= 1, so perm_cpu[i] != i for every i.
159+
perm_cpu = torch.roll(torch.arange(permute_size, dtype=torch.int32), 1)
160+
161+
# Build offsets (size permute_size + 1) on CPU.
162+
input_offsets_cpu = torch.zeros(permute_size + 1, dtype=torch.int32)
163+
input_offsets_cpu[1:] = torch.cumsum(lengths_cpu, dim=0).to(torch.int32)
164+
165+
permuted_lengths_cpu = lengths_cpu[perm_cpu.long()]
166+
output_offsets_cpu = torch.zeros(permute_size + 1, dtype=torch.int32)
167+
output_offsets_cpu[1:] = torch.cumsum(permuted_lengths_cpu, dim=0).to(
168+
torch.int32
169+
)
170+
171+
total_length = int(input_offsets_cpu[-1].item())
172+
173+
# CPU reference oracle — same op, different dispatch.
174+
output_permute_cpu = torch.ops.fbgemm.expand_into_jagged_permute(
175+
perm_cpu, input_offsets_cpu, output_offsets_cpu, total_length
176+
)
177+
178+
# GPU op under test. Pre-fix, this launch trips
179+
# KernelLauncher::checkThreadCountNotExceeded on ROCm.
180+
output_permute_gpu = torch.ops.fbgemm.expand_into_jagged_permute(
181+
perm_cpu.to(device),
182+
input_offsets_cpu.to(device),
183+
output_offsets_cpu.to(device),
184+
total_length,
185+
)
186+
187+
torch.testing.assert_close(output_permute_gpu.cpu(), output_permute_cpu)
188+
102189

103190
if __name__ == "__main__":
104191
unittest.main()

0 commit comments

Comments
 (0)