cuda : fix CUB argsort corruption caused by in-place keys - #28389
cuda : fix CUB argsort corruption caused by in-place keys#28389TheArchitectit wants to merge 1 commit into
Conversation
argsort_f32_i32_cuda_cub called the one-shot DeviceRadixSort::SortPairs API with d_keys_in == d_keys_out (temp_keys, temp_keys). CUB's internal double-buffer ping-pong requires distinct key buffers: with aliased buffers the sort partially overwrites its own input mid-pass and emits a corrupted permutation, surfacing as intermittent garbage indices (e.g. backend top_k over a 248k-column vocab on Maxwell/CUDA 12.5/CCCL 2.x, which then triggered out-of-bounds gathers in downstream get_rows). Use a distinct keys-out buffer for all six call sites (plain and segmented, ascending and descending, size-query and execute). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Hi @TheArchitectit, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
@ORippler Can |
|
@TheArchitectit Tried to reproduce this in test-backend-ops with CUDA 12.5.1 but no luck:
Did you use some custom CCCL installation? |
|
@fairydreaming No custom CCCL — stock CUDA 12.5.20240515 from On non-reproducibility with Re: the new-contributor PR limit flagged above — plan: I will keep my other open PR, #28097, in draft until this one is reviewed/approved. It is a companion to #27836 and blocked on it anyway, so it loses nothing by waiting. If you would prefer it closed and re-filed later instead, say the word. |
|
@TheArchitectit I see, if you have an example prompt and command that allows to reproduce this please share them. I mean if there is such a failure mode then it should appear long ago, so I'd like to reproduce this locally and investigate more. |
|
@fairydreaming Here's the exact repro. The failure lives on the DFlash speculative-decoding path, which is why a synthetic single-row Command (llama.cpp-xhtoken fork, but the argsort code path is upstream-identical): Hardware: 4× Tesla M10 (sm_50) + 1× GTX 1080, CUDA 12.5 (CCCL 2.4.0), driver 580.178.04. Crash signature (non-deterministic, within 1–3 generated tokens): The device varies run-to-run (3, then 4 on the next attempt) — consistent with a corrupted permutation fed to Why Minimal repro without the full 27B model: the same |
|
@TheArchitectit I tried the current mainline llama.cpp with the following command: worked without any issues. Please reproduce the issue in mainline llama.cpp. |
Overview
argsort_f32_i32_cuda_cub()calls the one-shot CUB radix-sort API withd_keys_in == d_keys_out. That API is not in-place: it ping-pongs double buffers internally, and aliasing its ends lets it overwrite its own input mid-pass, corrupting the permutation on sm_50 with CCCL 2.x. The fix allocates a distinct keys-out scratch buffer at all six call sites.Problem
argsort_f32_i32_cuda_cub()(ggml/src/ggml-cuda/argsort.cu) calls the one-shotDeviceRadixSort::SortPairs/SortPairsDescendingAPI withd_keys_in == d_keys_out: the same scratch buffer (temp_keys) is passed as both key input and key output at every CUB call site (size query + actual sort, ASC + DESC,nrows == 1+ segmented variants).The one-shot API is not an in-place sort: internally it runs a double-buffer ping-pong, and with the two ends aliased it partially overwrites its own input mid-pass (observed with CCCL 2.x on Maxwell). The result is a corrupted permutation: a mixture of valid indices and stale values from the keys buffer.
How it manifests
Two properties make this rare in practice:
GGML_OP_TOP_Kin backend sampling);ncols > 1024(smaller inputs take the bitonic path).Observed failure: backend
top_kover a 248,320-column vocab (DFlash speculative decoding on Qwen3.8-27B) on Maxwell (sm_50, CUDA 12.5, CCCL 2.x). The corrupted indices were fed toget_rows, which gathered with a garbage row id -> illegal memory access ->CUDA error: an illegal memory access(Xid 31, sticky context error). Non-deterministic across runs (depends on key values and scheduling).Evidence from debugging the repro:
k_get_rows_floatreading an unmapped address ~2.1 GB below the nearest allocation;get_rowsids input showed thetop_koutput containing float bit patterns instead of indices, and an A/B test (aliased vs distinct keys-out) localized the corruption to the argsort.Fix
Allocate a distinct keys-out scratch buffer (
temp_keys_out) from the same transient pool and pass it asd_keys_outat all 6 CUB call sites. Peak memory grows by at most one chunk (~64 MB) of transient scratch during the sort.Testing
top_kncols = 248320, temp 0.8, 4-way GPU split) now completes full generation; with instrumentation re-enabled, alltop_koutputs validated as in-bounds indices (69/69 calls in a 40-token run).Requirements