Skip to content

Commit 913c9c9

Browse files
q10meta-codesync[bot]
authored andcommitted
Apply ROCm grid-overflow cap hygiene to _float_to_fusednbitrowwise_cuda_kernel
Summary: X-link: https://github.com/facebookresearch/FBGEMM/pull/2931 Tier-1 follow-up to D104903707 / D104937969 / D105282095 / D105286434 — apply the canonical ROCm grid-overflow cap to a `quantize_ops/` launch site identified by the audit at: `/home/bensonma415/.llms/plans/permute_metric_layout_etc_rocm_grid_overflow_audit.plan.md` `_float_to_fusednbitrowwise_cuda_kernel` is launched with grid `cuda_calc_xblock_count(nrows, 256)` and block `256`. The host fn reads `const int nrows = input.size(0)`, so total threads are naturally bounded below 2**31 by valid inputs (the int truncation silently caps at INT_MAX rows). Thus this kernel cannot strictly trigger HIP's 2**32 cap with valid input — **the cap added here is defensive**, applied for hygienic consistency with the broader audit and to match the canonical pattern from D104903707 / D104937969. The kernel grid-strides over `row` (line 31), so the cap is correctness-preserving regardless. NVIDIA codegen sees the host-side `#ifdef USE_ROCM` block as a plain alias and remains a no-op delta in PTX/SASS. Reviewed By: henrylhtsang Differential Revision: D105286713 fbshipit-source-id: 95f99caa203861b75b45a283f8aa83813ad1bc4e
1 parent 2db16a8 commit 913c9c9

2 files changed

Lines changed: 69 additions & 3 deletions

File tree

fbgemm_gpu/src/quantize_ops/quantize_fused_nbit_rowwise.cu

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
#include "common.cuh"
10+
#include "fbgemm_gpu/utils/cuda_utilities.cuh"
1011

1112
using Tensor = at::Tensor;
1213

@@ -144,7 +145,12 @@ Tensor _float_to_fusednbitrowwise_gpu_t(
144145
}
145146

146147
constexpr auto threads_per_block = 256;
147-
const auto num_blocks = cuda_calc_xblock_count(nrows, threads_per_block);
148+
// HIP enforces a hard limit of 2^32 total threads per launch.
149+
// _float_to_fusednbitrowwise_cuda_kernel grid-strides over `row`, so
150+
// capping is correctness-preserving.
151+
// See: https://github.com/ROCm/hip/issues/2253
152+
const auto num_blocks = utils::cuda::cap_grid_dim_x_from_workload(
153+
nrows, threads_per_block, at::cuda::getCurrentCUDAStream());
148154
// think unsigned as we use 0, 255
149155

150156
FBGEMM_DISPATCH_FLOATING_TYPES(

fbgemm_gpu/test/quantize/fused_nbit_rowwise_test.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@
2828
# pyre-fixme[16]: Module `common` has no attribute `open_source`.
2929
if open_source:
3030
# pyre-ignore[21]
31-
from test_utils import gpu_available, optests
31+
from test_utils import gpu_available, gpu_memory_lt_gb, gpu_unavailable, optests
3232
else:
33-
from fbgemm_gpu.test.test_utils import gpu_available, optests
33+
from fbgemm_gpu.test.test_utils import (
34+
gpu_available,
35+
gpu_memory_lt_gb,
36+
gpu_unavailable,
37+
optests,
38+
)
3439

3540
torch.ops.load_library("//deeplearning/fbgemm/fbgemm_gpu:sparse_ops")
3641

@@ -412,6 +417,61 @@ def test_quantize_and_dequantize_op_cpu_and_cuda(
412417
)
413418
torch.testing.assert_close(dequantized_data.cpu(), reference)
414419

420+
@optests.dontGenerateOpCheckTests("Large grid HIP regression — uses ~4 GB HBM")
421+
@unittest.skipIf(*gpu_unavailable)
422+
# pyre-ignore[56]: gpu_memory_lt_gb is typed (bool, str), but Pyre cannot
423+
# infer the unpacked arg type through the open-source import shim above.
424+
@unittest.skipIf(*gpu_memory_lt_gb(8))
425+
def test_float_to_fusednbitrowwise_large_grid(self) -> None:
426+
"""
427+
Regression test for the HIP grid-cap on
428+
`_float_to_fusednbitrowwise_cuda_kernel`
429+
(quantize_ops/quantize_fused_nbit_rowwise.cu line ~152).
430+
431+
Block: 256. Grid: cuda_calc_xblock_count(nrows, 256). Total threads
432+
~= nrows. The host fn reads `nrows = input.size(0)` into an `int`,
433+
so total threads are naturally bounded below 2**31 by valid inputs;
434+
thus this kernel cannot strictly trigger HIP's 2**32 cap. The cap
435+
added here is defensive — consistent with the canonical pattern
436+
applied across the audit and harmless for kernels that grid-stride.
437+
438+
Two-part test:
439+
1. Launch-survival at large grid (nrows ~ 2**28) — verifies the
440+
cap-path compiles and the launch succeeds.
441+
2. Small-scale Tier-A CPU-oracle correctness — verifies the
442+
kernel produces the same quantized bytes as the CPU dispatch
443+
for non-trivial input.
444+
"""
445+
device = torch.device(torch.accelerator.current_accelerator() or "cuda")
446+
447+
# ---- Step 1: launch-survival at large nrows. ----
448+
nrows = 1 << 28 # 256M rows
449+
ncols = 4 # smallest valid: ncols % (2 * num_elem_per_byte) == 0 for bit_rate=4
450+
inp = torch.zeros((nrows, ncols), dtype=torch.float32, device=device)
451+
output = torch.ops.fbgemm.FloatToFusedNBitRowwiseQuantizedSBHalf(inp, 4)
452+
# Output shape: (nrows, ncols / num_elem_per_byte + 2 * sizeof(at::Half))
453+
# = (nrows, 4/2 + 4) = (nrows, 6).
454+
self.assertEqual(output.shape[0], nrows)
455+
del inp, output
456+
torch.cuda.empty_cache()
457+
458+
# ---- Step 2: Tier-A CPU-oracle correctness at small scale. ----
459+
small_nrows = 2 * 256 + 7 # multi-block on the small kernel grid
460+
small_ncols = 8 # multi-element per row, valid for bit_rate=4
461+
# Sentinel non-zero values at start / middle / end of each row.
462+
small_inp_cpu = torch.zeros((small_nrows, small_ncols), dtype=torch.float32)
463+
small_inp_cpu[0, 0] = 1.0
464+
small_inp_cpu[small_nrows // 2, small_ncols // 2] = 2.0
465+
small_inp_cpu[-1, -1] = 3.0
466+
467+
out_cpu = torch.ops.fbgemm.FloatToFusedNBitRowwiseQuantizedSBHalf(
468+
small_inp_cpu, 4
469+
)
470+
out_gpu = torch.ops.fbgemm.FloatToFusedNBitRowwiseQuantizedSBHalf(
471+
small_inp_cpu.to(device), 4
472+
)
473+
torch.testing.assert_close(out_gpu.cpu(), out_cpu)
474+
415475

416476
if __name__ == "__main__":
417477
unittest.main()

0 commit comments

Comments
 (0)