cuda: read memory through NVML if available - #23604
Conversation
…ontext Co-authored-by: Pascal <admin@serveurperso.com>
JohannesGaessler
left a comment
There was a problem hiding this comment.
Logically this seems correct to me, I only have requests for cosmetic changes.
| #include <windows.h> | ||
| #else | ||
| #include <dlfcn.h> | ||
| #endif |
There was a problem hiding this comment.
| #endif | |
| #endif // defined(_WIN32) |
| auto init = (ggml_nvml_init_fn) dlsym(lib, "nvmlInit_v2"); | ||
| get_handle = (ggml_nvml_handle_fn) dlsym(lib, "nvmlDeviceGetHandleByPciBusId_v2"); | ||
| get_mem = (ggml_nvml_mem_fn) dlsym(lib, "nvmlDeviceGetMemoryInfo"); | ||
| #endif |
There was a problem hiding this comment.
| #endif | |
| #endif // defined(_WIN32) |
| GGML_LOG_DEBUG("%s: %s: using NVML context-free path\n", __func__, ctx->name.c_str()); | ||
| return; | ||
| } | ||
| #endif |
There was a problem hiding this comment.
| #endif | |
| #endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) |
|
|
||
| #if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) | ||
| if (ggml_cuda_nvml_get_memory(ctx->pci_bus_id, free, total)) { | ||
| GGML_LOG_DEBUG("%s: %s: using NVML context-free path\n", __func__, ctx->name.c_str()); |
There was a problem hiding this comment.
| GGML_LOG_DEBUG("%s: %s: using NVML context-free path\n", __func__, ctx->name.c_str()); | |
| GGML_LOG_DEBUG("%s: %s: using NVML path\n", __func__, ctx->name.c_str()); |
| } | ||
| #endif | ||
|
|
||
| GGML_LOG_DEBUG("%s: %s: using cudaMemGetInfo fallback\n", __func__, ctx->name.c_str()); |
There was a problem hiding this comment.
| GGML_LOG_DEBUG("%s: %s: using cudaMemGetInfo fallback\n", __func__, ctx->name.c_str()); | |
| GGML_LOG_DEBUG("%s: %s: using cudaMemGetInfo fallback - this results in eager memory allocation\n", __func__, ctx->name.c_str()); |
|
@ORippler @gaugarg-nv since I am not familiar with NVML, if either one of you could also review this PR it would be appreciated. |
|
Thanks! I only ran a quick check on Linux so far, so I'll follow up with a small standalone GGML test on Windows: load nvml.dll via LoadLibraryA, then compare the values returned by the NVML path against cudaMemGetInfo to confirm they match on WDDM. Will report back! |
|
Made a small cross-platform tool with Claude Opus to sanity check this on Linux and Windows : 3d09638 It reads VRAM two ways and compares them: the NVML path from this PR, and a plain cudaMemGetInfo that forces the primary context. The NVML read matches ggml's device query exactly and stays contextless, while cudaMemGetInfo permanently eats: RTX PRO 6000 (Blackwell): 555 MiB So the cost scales with SM count, and #21231 would pay it in the router just to read margins. The NVML path avoids that entirely. Windows numbers are a bit noisier (WDDM keeps moving VRAM around) but show the same picture. LinuxWindows: |
| #include <vector> | ||
|
|
||
| #if defined(_WIN32) | ||
| #include <windows.h> |
There was a problem hiding this comment.
| #include <windows.h> | |
| #define WIN32_LEAN_AND_MEAN | |
| #ifndef NOMINMAX | |
| #define NOMINMAX | |
| #endif | |
| #include <windows.h> |
The other code in the project defines these two macros before windows.h to avoid compilation errors.
|
NVML changes look fine to me. |
There was a problem hiding this comment.
This avoids initializing a context (which takes up a few hundred MB of memory) and brings CUDA in line with Metal and Vulkan, which both also read memory without initialization. #21231 requires this change, otherwise reading CUDA available memory initializes VRAM in the router process.
CUDA contexts do have a sizeable overhead:
https://forums.developer.nvidia.com/t/whats-cuda-contexts-gpumemory-contain-is-it-necessary-and-available-to-minimum-the-cuda-contexts-gpumemorysize/289411
This overhead will be required when actually doing work in the backend, and thus should be accounted for when budgeting/reporting available VRAM.
In this light, skimming #21231 it seems probing in the child, not the router
is the way we should proceed. General guidance is to have one CUDA context per GPU & application open at a time.
An alternative path would be to add backend destructures/resets so we can construct/destruct the CUDA context via cudaDeviceReset inside CUDA Runtime as needed.
|
Reading up on this a bit more, I'd say the best path would be to add |
|
Maybe I misunderstood the intent of this PR. I thought it was to avoid allocating VRAM in a situation where ggml is compiled both with CUDA and another backend and one wouldn't want to allocate memory unless a backend is actually used. |
|
How about this: extend |
|
Superceded by #23935 |
Overview
This avoids initializing a context (which takes up a few hundred MB of memory) and brings CUDA in line with Metal and Vulkan, which both also read memory without initialization. #21231 requires this change, otherwise reading CUDA available memory initializes VRAM in the router process. See also #21231 (comment). It's a soft-dependency on the nvml driver library, if it is not available or initialization fails, it falls back to the standard
cudaMemGetInfopath. iGPUs on Linux also skip directly into the existing/proc/meminfofunction.I also looked into using RSMI for ROCm, but when testing I did not see VRAM being allocated from the use of
hipMemGetInfo, so I skipped it.@ServeurpersoCom FYI
Requirements