Uh oh!
There was an error while loading. Please reload this page.
Add managed-memory advise, prefetch, and discard-prefetch free functions - #1775
Conversation
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
This comment has been minimized.
This comment has been minimized.
rparolin
commented
Mar 17, 2026
/ok to test |
jrhemstad
commented
Mar 17, 2026
question: Does making these member functions of the |
I'm moving this back into draft. We discussed in our team meeting because I was already hesitant as Buffer is becoming a 'God object' with the functionality is gaining. We were going to explore alternatives. Free functions sounds like a good alternative to explore. |
…ns in the cuda.core.managed_memory namespace
…ups, fix docs - Remove duplicate long-form "cu_mem_advise_*" string aliases from _MANAGED_ADVICE_ALIASES; users pass short strings or the enum directly - Replace 4 boolean allow_* params in _normalize_managed_location with a single allowed_loctypes frozenset driven by _MANAGED_ADVICE_ALLOWED_LOCTYPES - Cache immutable runtime checks: CU_DEVICE_CPU, v2 bindings flag, discard_prefetch support, and advice enum-to-alias reverse map - Collapse hasattr+getattr to single getattr in _managed_location_enum - Move _require_managed_discard_prefetch_support to top of discard_prefetch for fail-fast behavior - Fix docs build: reset Sphinx module scope after managed_memory section in api.rst so subsequent sections resolve under cuda.core - Add discard_prefetch pool-allocation test and comment on _get_mem_range_attr Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e legacy path The _V2_BINDINGS cache in _buffer.pyx persists across tests, so monkeypatching get_binding_version alone is insufficient when earlier tests have already populated the cache with the v2 value. Promote _V2_BINDINGS from cdef int to a Python-level variable so tests can monkeypatch it directly via monkeypatch.setattr, and reset it to -1 in both legacy-signature tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t real hardware These three tests call cuMemAdvise on real CUDA devices and verify memory range attributes. On devices without concurrent_managed_access (e.g. Windows/WDDM), set_read_mostly silently no-ops and set_preferred_location fails with CUDA_ERROR_INVALID_DEVICE. Use the stricter _skip_if_managed_location_ops_unsupported guard, matching the pattern already used by test_managed_memory_functions_accept_raw_pointer_ranges. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s support Reorder checks in discard_prefetch so _normalize_managed_target_range runs before _require_managed_discard_prefetch_support. This ensures non-managed buffers raise ValueError before the RuntimeError for missing cuMemDiscardAndPrefetchBatchAsync support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ps module Move advise, prefetch, and discard_prefetch functions and their helpers out of _buffer.pyx into a new _managed_memory_ops Cython module to improve separation of concerns. Expose _init_mem_attrs and _query_memory_attrs as non-inline cdef functions in _buffer.pxd so the new module can reuse them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
…ged_buffer Per PR NVIDIA#1775 review feedback. The deferred import was avoiding a hypothetical circular dependency that doesn't exist; lifting to module top removes the per-call import overhead.
Per PR NVIDIA#1775 review feedback. The deferred import was avoiding a hypothetical circular dependency that doesn't exist; lifting it to module top removes the per-call import overhead.
Per PR NVIDIA#1775 review feedback. The two deferred imports inside _decode_location were avoiding a hypothetical circular dependency that doesn't exist; lifting to module top removes the per-call import overhead.
…ource Per PR NVIDIA#1775 review feedback. The deferred import comment claimed a circular-dependency concern but the cycle did not actually exist; lifting to module top removes the per-allocation import overhead.
Per PR NVIDIA#1775 review feedback: this PR targets the cuda.core v1.1.0 milestone, so the new-feature entries belong in a new 1.1.0-notes.rst rather than being appended to the already-released 1.0.0 notes. The release index uses a glob pattern (release/*-notes), so the new file is picked up automatically.
… bindings only Per PR NVIDIA#1775 review feedback (leofang). The cu13 paths in _managed_buffer.preferred_location and _reject_numa_host_on_cuda12 gated on binding_version() >= (13, 0, 0) only, mirroring the regressions that PR NVIDIA#2054 and PR NVIDIA#2064 fixed for other modules. With cuda-bindings 13.x installed but a CUDA 12.x runtime driver, the v2 CU_MEM_RANGE_ATTRIBUTE_PREFERRED_LOCATION_* attributes and the HOST_NUMA{,_CURRENT} location types fail deep in the driver with CUDA_ERROR_INVALID_VALUE. Both checks now AND driver_version() >= (13, 0, 0) into the gate, and the _reject_numa_host_on_cuda12 error message names both required versions.
leofang
left a comment
There was a problem hiding this comment.
Approved, assuming all pending comments below will be either tracked or followed up.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Advise is now exclusively reached through `ManagedBuffer` property setters, which pass `CUmem_advise` enum members directly. The string alias path in `_normalize_managed_advice` was unreachable, and the enum->alias reverse lookup existed only to key the loc-type allow-set. Replace `_MANAGED_ADVICE_ALIASES`, `_ADVICE_ENUM_TO_ALIAS`, and `_normalize_managed_advice` with enum-keyed `_ADVICE_IGNORES_LOCATION` and `_ADVICE_ALLOWED_LOCTYPES`; rewrite `_advise_one` to validate on the enum directly. The kind-mismatch error message now names the advice via `enum.name` instead of the lowercase alias. Addresses PR NVIDIA#1775 review comment from leofang.
Rename the local skip helper to `_skip_if_raw_managed_alloc_unsupported` to disambiguate from conftest's `skip_if_managed_memory_unsupported`, which gates `ManagedMemoryResource` pool creation rather than the raw `cuMemAllocManaged` capability checked here. Add a comment spelling out the distinction. Both callsites updated; behavior unchanged. Addresses PR NVIDIA#1775 review comment from leofang.
Replace 11 verbatim repetitions of `_get_int_attr(buf, driver.CUmem_range_attribute.CU_MEM_RANGE_ATTRIBUTE_LAST_PREFETCH_LOCATION)` with a `_last_prefetch_location(buf)` one-liner. Net -27 lines. Carries a TODO noting this becomes moot once `ManagedBuffer` exposes mem-range attributes directly (separately tracked). Addresses PR NVIDIA#1775 review comment from leofang.
The `Host`-construction tests are device-agnostic and unrelated to the managed-memory ops surface, so they belong as a sibling of `tests/test_device.py` rather than buried inside `tests/memory/`. Pure move — no test logic changes. 8 tests collected and pass in the new location; combined count across both files is unchanged. Addresses PR NVIDIA#1775 review comment from leofang.
Tracking issue filed for exposing mem-range attributes on `ManagedBuffer` directly. Once NVIDIA#2109 lands the private helper goes away.
Net -140 LOC across three coupled changes: * Fixtures 8 -> 5. Drop `memory_pool_*` (consumers re-target to `location_ops_*`; every batch test exercises prefetch which needs `concurrent_managed_access` anyway), `discard_prefetch_mr`, and the two `*_buffer` fixtures. * Add parametrized `managed_buffer` (pool | external) for prefetch / discard / discard_prefetch coverage. Add `external_managed_buffer` (non-parametrized) for cuMemAdvise tests — pool-allocated managed memory returns NOT_SUPPORTED for some advise values on certain driver/device combos, so advise tests stay external-only to match the original design's documented carve-out. * Delete 5 redundant standalone tests; fold 2 (`test_operation_validation`, `test_advise_location_validation`) into `TestManagedBuffer`. Keep `test_managed_memory_operations_reject_non_managed_allocations` standalone — it wraps a device-only pointer, orthogonal to the pool/external axis. * Pull `test_rejects_single_buffer` and `test_length_mismatch` out of all three batch classes; replace with two module-level parametrized tests covering all three batch fns. Addresses PR NVIDIA#1775 review comments from leofang (U4, U5, U7).
rparolin
commented
May 19, 2026
/ok to test |
@rparolin, there was an error processing your request: See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/ |
rparolin
commented
May 19, 2026
/ok to test be46eed |
…inputs `MutableSet.discard(x)` is contractually a no-op when `x` is not in the set. `set_accessed_by` only accepts `device` and generic `host` kinds, so NUMA-aware host variants (`Host(numa_id=...)`, `Host.numa_current()`) can never enter the set — they are necessarily non-members. Previously, `discard` forwarded such inputs to `_advise_one`, which raised `ValueError` from the kind-allowed check. That broke the `MutableSet` contract that `AccessedBySetProxy` claims by inheriting from `MutableSet`, and caused `test_accessed_by_mutable_set_interface` to fail on the helper's non-member sentinel. Add an explicit short-circuit for the NUMA-host kinds. Fixes CI failure for tests/memory/test_managed_ops.py::TestManagedBuffer::test_accessed_by_mutable_set_interface.
rparolin
commented
May 19, 2026
/ok to test 47d2358 |
Commit 7344d89 lifted the `_read_preferred_location_v2` import in `_managed_buffer.py` from deferred (inside the property getter) to module top, per leofang's "no deferred imports" guidance. But that function was only defined inside the `IF CUDA_CORE_BUILD_MAJOR >= 13` block in `_managed_memory_ops.pyx`, so on a cu12 build the symbol doesn't exist and `import cuda.core` fails with ImportError: cannot import name '_read_preferred_location_v2' from 'cuda.core._memory._managed_memory_ops' Add an ELSE branch defining a stub that raises `NotImplementedError`. `ManagedBuffer.preferred_location` already gates on both binding_version() and driver_version() >= (13, 0, 0) before calling, so the stub is unreachable at runtime — it exists only to satisfy the unconditional module-level import on cu12. Fixes CI failure on all 12.9.1 test jobs.
rparolin
commented
May 19, 2026
/ok to test c63874d |
Two divergent error messages were rejecting NUMA-host inputs on cu12: * `_managed_location.py:44` (call boundary, primary path): "Host(numa_id=...) / Host.numa_current() require both cuda-bindings 13.0+ and a CUDA 13+ runtime driver; use Host() instead" * `_managed_memory_ops.pyx:119` (defensive Cython guard): "use Host() instead — NUMA-aware host locations (Host(numa_id=...), Host.numa_current()) require a CUDA 13 build of cuda.core" The latter wording was stale — "CUDA 13 build of cuda.core" conflates the binding-build gate with the runtime-driver gate, the exact distinction that PRs NVIDIA#2054 / NVIDIA#2064 fixed. Update the pyx-side message to match the call-boundary wording. Test regexes for the kind-rejection cases (3 sites in TestManagedBuffer) previously matched the stale wording; update them to match the unified substring `cuda-bindings 13\.0\+` so cu12 jobs pass. Fixes the CI failure of `test_advise_location_validation` / `test_operation_validation` on all CUDA 12.9.1 matrix jobs.
rparolin
commented
May 19, 2026
/ok to test 1d2b50a |
|
rparolin
commented
May 20, 2026
🥳 🎈 |
Summary
Adds managed-memory range operations to
cuda.core:cuda.core.utils:advise,prefetch,discard,discard_prefetch. Each accepts either a singleBufferor a sequence; N==1 dispatches to the per-range driver entry point and N>1 dispatches to the correspondingcuMem*BatchAsync(CUDA 13+).Host— new top-level singleton class symmetric toDevice.Host()(any host),Host(numa_id=N),Host.numa_current(). Same-argument constructions are interned (Host() is Host()). Used together withDeviceto express managed-memory locations.ManagedBuffer—Buffersubclass returned byManagedMemoryResource.allocate. Exposes a Pythonic property-style advice API on top of the same free functions. Wrap an external managed pointer withBuffer.from_handle(...)(now a@classmethod, soManagedBuffer.from_handle(...)returns aManagedBuffer).Closes#1332. Addresses the managed-memory portion of #1333 (P1:
cuMemPrefetchBatchAsync,cuMemDiscardBatchAsync,cuMemDiscardAndPrefetchBatchAsync). The P0cuMemcpyBatchAsyncfrom #1333 is intentionally out of scope and tracked separately; the holistic batched-API contract this PR commits to is documented in #issuecomment-4355502334 so the upcomingcuMemcpyBatchAsyncwork can mirror it.Public API
ManagedBuffer— property-style advice on managed allocationsManagedMemoryResource.allocatereturns aManagedBuffer(aBuffersubclass). All ManagedBuffer-specific behavior is layered on top of the free functions, so the two surfaces stay consistent.Free functions —
advise/prefetch/discard/discard_prefetchEach accepts a
Buffer(orManagedBuffer) or a sequence of them. Locations are expressed viaDeviceorHost.Batched form — same function, sequence of targets
When N>1, dispatch goes to the corresponding
cuMem*BatchAsync. Sequence locations are paired by index; a scalar location broadcasts to every target.Mismatched sequence lengths raise
ValueError. On a CUDA 12 build ofcuda.core, N>1 raisesNotImplementedError(the*BatchAsyncentry points are CUDA 13+); N==1 works on every supported toolkit.Putting it together
Implementation notes
cuda_core/cuda/core/_memory/_managed_memory_ops.pyxusescimport cydriverfor direct C-level driver calls.cuMemAdviseandcuMemPrefetchAsyncis handled at compile time withIF CUDA_CORE_BUILD_MAJOR >= 13:/ELSE:.cuMemPrefetchBatchAsync,cuMemDiscardBatchAsync,cuMemDiscardAndPrefetchBatchAsync) are CUDA 13+ only. On CUDA 12 builds, N>1 calls raiseNotImplementedError; single-buffer calls work everywhere.Hostis a singleton with__slots__and a__new__-based intern cache keyed by(numa_id, is_numa_current). Same-argument constructions return the same instance on both Python and Cython call paths.ManagedBufferis a pure-Python subclass of the CythonBuffercdef class.Buffer.from_handleis now a@classmethod(was@staticmethod) soMyBufferSubclass.from_handle(...)returns the typed instance viacls._init.Buffer_from_deviceptr_handleand_MP_allocatethread an optionalclsparameter soManagedMemoryResource.allocatematerializes aManagedBuffer._LocSpec(in_managed_location.py) carries the(kind, id)discriminator that the Cython layer maps toCUmemLocation(CUDA 13) or a legacy device ordinal (CUDA 12). Public callers see onlyDevice/Host;_coerce_locationproduces the internal record._buffer.pyxcollapsesout.is_managed = (is_managed != 0)to a single unconditional assignment and adds a TODO noting that HMM/ATS-mapped sysmem is not yet captured byCU_POINTER_ATTRIBUTE_IS_MANAGED.