Repository navigation
Fix CUDA initialization race on Windows with CUDA 13 - #114
Merged
Merged
Conversation
ZedongPeng
approved these changes
Oct 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On Windows with CUDA 13, the first solve sometimes fails with:
cupdlpx_gpu_init_async()callscudaFree(0)on a background thread to warm up the GPU. If preprocessing is fast, the solver thread callscusparseCreate()before that warmup has finished, and on Windows with CUDA 13 this call can fail.Why only this setup? On Windows, CUDA 13 programs no longer use their own copy of the CUDA runtime. They use a shared one that comes with the GPU driver (
nvcudart_hybrid64.dll), and the CUDA 13 build of cuSPARSE uses it too. So the warmup thread andcusparseCreate()end up in the same runtime at the same time. A secondcudaFree(0)would wait for the warmup to finish, butcusparseCreate()fails instead. I can't see why, since that code is closed source.With CUDA 12, the program and cuSPARSE each have their own runtime, so the two threads never touch the same one. Linux doesn't use the shared driver runtime either. That is why the comment on
cupdlpx_gpu_init_async(), which says other threads wait for the warmup, was true until now.I tested this with a small program: start
cudaFree(0)on a background thread, then callcusparseCreate()on the main thread. Each row is 40 runs, each in a new process, on an RTX 5080 with driver 610.60. It only fails when both the runtime and cuSPARSE come from CUDA 13 on Windows.The fix adds
CUDA_CHECK(cudaFree(0))on the solver thread right before the cuSPARSE and cuBLAS handles are created. The solver thread now waits for the GPU to be ready before using it. The warmup still runs in the background while the file is parsed and presolved, so we keep that speedup. The extra call runs once per solve. I also updated the comment oncupdlpx_gpu_init_async().To reproduce on
724894e, open an x64 MSVC developer shell with a conda environment that has CUDA 13.4 (conda-forgecuda-version=13.4), CMake and Ninja. The/Zc:preprocessorflag is needed to build with CUDA 13 on MSVC until #113 is merged.Each repeat runs in a new process. On my machine,
test_interfacefailed with the error above in 27 of 40 runs on724894e. With this fix, it passed all 40.With the fix, the small test program also passed 40 of 40 runs. Together with the reduction workspace fix, which will be a separate PR, all 107 Python tests passed 5 times in a row on CUDA 13.4 with pytest's default output capture, and also passed on CUDA 12.9. I have not tested HIP.