perf: speed up CUDA virtual package detection - #2568
Conversation
1f4375f to
e8320b5
Compare
|
CUDA detection logic looks good to me overall 👍 I can’t fully validate the DLL/loading behavior across all platforms, but the general approach makes sense. The boot cache also looks great, especially for Windows startup time. One small thing I was wondering about: for the fallback paths, can we make sure we consistently try the nvidia-smi-based detection where possible? From the discussion it sounds like the current MUSL/fallback behavior is similar to what cuda-oxide does, which seems reasonable. I also briefly wondered whether we could extract some constants directly from the driver with something like goblin, even on MUSL systems, but for __cuda_arch we need to load/query the CUDA side anyway, so that probably doesn’t buy us much here. So from my side: looks good, modulo the fallback consistency check. |
47b004e to
2ae1f02
Compare
|
Could you maybe recheck, I added a lot of robustness checks |
|
On Linux you will get problems with this: Containers share a kernel and with that their boot id. You might want to hash that together with the machine ID (if available). At least on systemd-based systems you should be able to tell different containers apart from each other. |
|
Actually: Thinking about that, the graphics card is managed by the kernel, which is the same for all containers anyway. So this should be fine (together with the device-node check for the graphics cards). |
3491eef to
81e555d
Compare
|
Details |
|
Tested on Fedora with CUDA GPU: works both with MUSL and regular glibc build! Code looks good, too. |
c72f4da to
51b2072
Compare
73b37f7 to
7d24dec
Compare
Detecting __cuda and __cuda_arch takes about 1.5 seconds on Windows when the GPU has been idle, which is the normal case for a command line tool. Almost all of it is the driver version query, which loads the CUDA driver library and starts the user-mode driver to read the version out of it. If the driver powered down in the meantime it has to come back up first. No detection API avoids this, so the only fix is to not do it again. Cache the result on disk, keyed on the boot session, a fingerprint of the installed driver and one of the visible GPUs, so reboots, driver updates and plugging in an eGPU all invalidate it. A TTL covers whatever the fingerprints miss. A cache hit takes about 1ms and never touches the driver, which also leaves an idle GPU asleep instead of waking it on every invocation. Callers pass the cache directory through a new cache_dir argument, and None disables it. The Python bindings take the same argument. This also fixes three problems in the detection itself: - Detection uses NVML instead of libcuda, so CUDA_VISIBLE_DEVICES no longer affects the result. Under a job scheduler or in CI, where that variable is often set, __cuda and __cuda_arch could come out wrong or disappear entirely. - __cuda_arch now works on musl, where it was always absent. - __cuda is detected even when the driver fails to initialize, since the version query no longer needs initialization. BREAKING CHANGE: VirtualPackages::detect, VirtualPackages::detect_for_platform, VirtualPackage::detect, Cuda::current, CudaArch::current and cuda::cuda_info, cuda::cuda_version and cuda::cuda_arch take an additional cache_dir argument.
7d24dec to
24b76b7
Compare
Description
Detecting
__cudaand__cuda_archtakes about 1.5 seconds on Windows when the GPU has been idle, which is the normal case for a command line tool. Measured with CUDA 13.0 and compute capability 8.9, each in a fresh process:Nearly all of it is one step. Loading NVML takes 0.5 ms and enumerating devices adds 24 ms. The rest is the driver version query, which loads the CUDA driver library and starts the user-mode driver to read the version out of it. If the driver powered down in the meantime it has to come back up first, and that is where the 1.5 seconds goes. No detection API avoids this, so the only fix is to not do it again.
So this caches the result on disk. A cache hit never touches the driver, which also leaves an idle GPU asleep instead of waking it on every invocation.
The cache is keyed on the boot session, a fingerprint of the installed driver and one of the visible GPUs, so reboots, driver updates and plugging in an eGPU all invalidate it. A TTL covers whatever the fingerprints miss. Callers pass the cache directory through a new
cache_dirargument, andNonedisables it. That is a breaking change to the detection API. The Python bindings take the same argument.It also fixes three things in the detection itself:
CUDA_VISIBLE_DEVICESno longer affects the result. Under a job scheduler or in CI, where that variable is often set,__cudaand__cuda_archcould come out wrong or disappear entirely.__cuda_archnow works on musl, where it was always absent.__cudais detected even when the driver fails to initialize, since the version query no longer needs initialization.Fixes prefix-dev/pixi#6775
How Has This Been Tested?
cargo clippyandcargo fmtpass, the crate builds forx86_64-unknown-linux-musland both Windows targets, andcargo checkpasses forpy-rattler.AI Disclosure
Tools: Claude Code
Checklist: