NanoVDB: fail with an actionable diagnostic when CUDA memory pools are unavailable#2256
NanoVDB: fail with an actionable diagnostic when CUDA memory pools are unavailable#2256harrism wants to merge 3 commits intoAcademySoftwareFoundation:masterAcademySoftwareFoundation/openvdb:masterfrom harrism:nanovdb-vgpu-malloc-fallbackharrism/openvdb:nanovdb-vgpu-malloc-fallbackCopy head branch name to clipboard
Conversation
957ca02 to
e27f757
Compare
|
I can confirm that this works on a |
Fractional vGPU configurations commonly expose no stream-ordered memory pools (cudaDevAttrMemoryPoolsSupported == 0), so the first cudaMallocAsync fails with cudaErrorNotSupported. Rather than silently substituting synchronous cudaMalloc -- which would make an async resource misrepresent its own semantics, since the choice of a synchronous backend belongs to the caller -- the util::cuda::mallocAsync wrapper now detects the missing capability and emits an actionable diagnostic naming NANOVDB_USE_SYNC_CUDA_MALLOC before returning cudaErrorNotSupported, which every caller already surfaces via cudaCheck. freeAsync mirrors the check. The NANOVDB_USE_SYNC_CUDA_MALLOC macro remains the caller's explicit opt-in to synchronous allocation, and must be defined identically in every translation unit (the wrappers are inline). memoryPoolsSupported() exposes the cached per-device capability query, and mallocSync/freeSync name the synchronous pair the macro path maps to. TestUtilCuda.cu is compiled into two binaries -- default and macro-defined -- covering both modes; its async-path tests skip on a device without memory pools, where those wrappers fail by design. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Mark Harris <mharris@nvidia.com>
e27f757 to
3178296
Compare
|
Thanks for validating on Heads up that I've since changed the behavior in this PR, so it's worth a re-test: instead of silently falling back to So on |
|
I've rebuilt |
GridBuildThroughWrappers built a device grid through the wrappers, but PointsToGrid device builds are already covered by TestMemoryResource, TestNanoVDB, and TestMultiGPU. Its only unique coverage was the macro-forced-synchronous build, which is better established by the MallocAsyncRoundTrip test running in the NANOVDB_USE_SYNC_CUDA_MALLOC binary plus validation on pool-less hardware -- and it pulled the PointsToGrid builder header into a util-wrapper unit test. Remove it and the now-unused include. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Mark Harris <mharris@nvidia.com>
…lpers mallocSync/freeSync were the shared synchronous implementation for two callers: the macro tier and a runtime fallback. With the fallback removed in favor of failing loudly, only the macro tier remains, so the helpers are one-line pass-throughs with a single caller each. Inline cudaMalloc / cudaFree directly, restoring the macro-tier wrappers to their prior form and leaving memoryPoolsSupported as the only added function. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Mark Harris <mharris@nvidia.com>
|
To test the expected behaviour without My only note about the message should be to say "Define NANOVDB_USE_SYNC_CUDA_MALLOC when building …" to make it clear that this isn't a runtime environment variable that's respected. |
swahtz
left a comment
There was a problem hiding this comment.
This looks like the right sized fix for this point in time for situations where a hardware configuration can't support the stream-ordered memory pool feature (time-share vGPUs) until we get the injectable memory allocator feature.
My only note is at the end changing the message to make it clear that the variable needs to be defined when building so the end-user doesn't assume we're referring to a runtime environment variable.
Addresses the failure mode in #2255 and complements #1798 / #1807's
NANOVDB_USE_SYNC_CUDA_MALLOC. Related roadmap: #2232 (whose step 2 adds an injectable synchronous resource — the explicit, per-call form of the backend choice this PR keeps out of a hidden runtime path).What
Fractional vGPU configurations (e.g. AWS
g6fL4 instances) expose no stream-ordered memory pools —cudaDevAttrMemoryPoolsSupported == 0— so NanoVDB's firstcudaMallocAsyncfails withcudaErrorNotSupported(801) with no indication of why or what to do.Every device allocation in NanoVDB routes through the
util::cuda::mallocAsync/freeAsyncwrappers, so this PR improves the diagnostic at that single point without silently changing allocation semantics:mallocAsyncemits an actionable message namingNANOVDB_USE_SYNC_CUDA_MALLOCand returnscudaErrorNotSupported— which every caller already surfaces throughcudaCheck.freeAsyncmirrors the capability check.cudaMalloc. A silent fallback would make an async-typed resource (DeviceResource, which advertisesis_async_resource) quietly perform synchronous allocation — the type would misrepresent its runtime semantics, and a multi-stream caller would get unexpected serialization it never asked for. The choice of a synchronous backend belongs to the caller, explicitly: today via the macro, and (roadmap) via an injected synchronous resource under NanoVDB: injectable CUDA memory resources — design & roadmap #2232.NANOVDB_USE_SYNC_CUDA_MALLOCremains the caller's compile-time opt-in to synchronous allocation, documented with the three-way selection and the one-definition-rule requirement (the wrappers areinline, so it must be defined identically in every TU).memoryPoolsSupported()exposes the cached per-device query.Deploying on a pool-less device
Define
NANOVDB_USE_SYNC_CUDA_MALLOCproject-wide. A CI probe can automate this: compile-and-run a one-linecudaDevAttrMemoryPoolsSupportedcheck on the target instance and set the flag when it reports 0 — capability detection performed by the deployer, not hidden in the library.Testing
New
unittest/TestUtilCuda.cu, built as two binaries — default (ctestnanovdb_cuda_util_unit_test) and-DNANOVDB_USE_SYNC_CUDA_MALLOC(ctestnanovdb_cuda_util_sync_unit_test) — covering both allocation modes. Tests: the cached query agrees with a direct per-device attribute query (and rejects out-of-range ids); and an allocate/use/free round-trip through the wrapper in whichever mode the build selects. The default binary's async round-trip skips on a device without memory pools (where those wrappers fail by design). Full suites green (CPU 153/153,nanovdb_test_cuda53/53, memory-resource 8/8; compute-sanitizer clean; sm_89 / CUDA 12.6).Non-CUDA builds
No impact —
util/cuda/Util.his CUDA-domain and unreachable fromNANOVDB_USE_CUDA=OFFbuilds.🤖 Generated with Claude Code