Coverage for cuda / core / _memory / _device_memory_resource.pyx: 70.00%
60 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-08 01:07 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-08 01:07 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
5from __future__ import annotations
7from cuda.bindings cimport cydriver
8from cuda.core._memory._memory_pool cimport _MemPool, _MemPoolOptions
9from cuda.core._memory cimport _ipc
10from cuda.core._memory._ipc cimport IPCAllocationHandle
11from cuda.core._utils.cuda_utils cimport (
12 check_or_create_options,
13 HANDLE_RETURN,
14)
16from dataclasses import dataclass
17import multiprocessing
18import platform # no-cython-lint
19import uuid
21from cuda.core._utils.cuda_utils import check_multiprocessing_start_method
22from cuda.core._resource_handles cimport as_cu
24__all__ = ['DeviceMemoryResource', 'DeviceMemoryResourceOptions']
27@dataclass
28cdef class DeviceMemoryResourceOptions:
29 """Customizable :obj:`~_memory.DeviceMemoryResource` options.
31 Attributes
32 ----------
33 ipc_enabled : bool, optional
34 Specifies whether to create an IPC-enabled memory pool. When set to
35 True, the memory pool and its allocations can be shared with other
36 processes. (Default to False)
38 max_size : int, optional
39 Maximum pool size. When set to 0, defaults to a system-dependent value.
40 (Default to 0)
41 """
42 ipc_enabled : bool = False
43 max_size : int = 0
46cdef class DeviceMemoryResource(_MemPool):
47 """
48 A device memory resource managing a stream-ordered memory pool.
50 Parameters
51 ----------
52 device_id : Device | int
53 Device or Device ordinal for which a memory resource is constructed.
55 options : DeviceMemoryResourceOptions
56 Memory resource creation options.
58 If set to `None`, the memory resource uses the driver's current
59 stream-ordered memory pool for the specified `device_id`. If no memory
60 pool is set as current, the driver's default memory pool for the device
61 is used.
63 If not set to `None`, a new memory pool is created, which is owned by
64 the memory resource.
66 When using an existing (current or default) memory pool, the returned
67 device memory resource does not own the pool (`is_handle_owned` is
68 `False`), and closing the resource has no effect.
70 Notes
71 -----
72 To create an IPC-Enabled memory resource (MR) that is capable of sharing
73 allocations between processes, specify ``ipc_enabled=True`` in the initializer
74 option. Sharing an allocation is a two-step procedure that involves
75 mapping a memory resource and then mapping buffers owned by that resource.
76 These steps can be accomplished in several ways.
78 An IPC-enabled memory resource can allocate memory buffers but cannot
79 receive shared buffers. Mapping an MR to another process creates a "mapped
80 memory resource" (MMR). An MMR cannot allocate memory buffers and can only
81 receive shared buffers. MRs and MMRs are both of type
82 :class:`DeviceMemoryResource` and can be distinguished via
83 :attr:`DeviceMemoryResource.is_mapped`.
85 An MR is shared via an allocation handle obtained by calling
86 :meth:`DeviceMemoryResource.get_allocation_handle`. The allocation handle
87 has a platform-specific interpretation; however, memory IPC is currently
88 only supported for Linux, and in that case allocation handles are file
89 descriptors. After sending an allocation handle to another process, it can
90 be used to create an MMR by invoking
91 :meth:`DeviceMemoryResource.from_allocation_handle`.
93 Buffers can be shared as serializable descriptors obtained by calling
94 :meth:`Buffer.get_ipc_descriptor`. In a receiving process, a shared buffer is
95 created by invoking :meth:`Buffer.from_ipc_descriptor` with an MMR and
96 buffer descriptor, where the MMR corresponds to the MR that created the
97 described buffer.
99 To help manage the association between memory resources and buffers, a
100 registry is provided. Every MR has a unique identifier (UUID). MMRs can be
101 registered by calling :meth:`DeviceMemoryResource.register` with the UUID
102 of the corresponding MR. Registered MMRs can be looked up via
103 :meth:`DeviceMemoryResource.from_registry`. When registering MMRs in this
104 way, the use of buffer descriptors can be avoided. Instead, buffer objects
105 can themselves be serialized and transferred directly. Serialization embeds
106 the UUID, which is used to locate the correct MMR during reconstruction.
108 IPC-enabled memory resources interoperate with the :mod:`multiprocessing`
109 module to provide a simplified interface. This approach can avoid direct
110 use of allocation handles, buffer descriptors, MMRs, and the registry. When
111 using :mod:`multiprocessing` to spawn processes or send objects through
112 communication channels such as :class:`multiprocessing.Queue`,
113 :class:`multiprocessing.Pipe`, or :class:`multiprocessing.Connection`,
114 :class:`Buffer` objects may be sent directly, and in such cases the process
115 for creating MMRs and mapping buffers will be handled automatically.
117 For greater efficiency when transferring many buffers, one may also send
118 MRs and buffers separately. When an MR is sent via :mod:`multiprocessing`,
119 an MMR is created and registered in the receiving process. Subsequently,
120 buffers may be serialized and transferred using ordinary :mod:`pickle`
121 methods. The reconstruction procedure uses the registry to find the
122 associated MMR.
123 """
125 def __init__(self, device_id: Device | int, options=None):
126 from .._device import Device 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
127 cdef int dev_id = Device(device_id).device_id 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
128 cdef DeviceMemoryResourceOptions opts = check_or_create_options( 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
129 DeviceMemoryResourceOptions, options, "DeviceMemoryResource options",
130 keep_none=True
131 )
132 cdef _MemPoolOptions opts_base = _MemPoolOptions() 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
134 cdef bint ipc_enabled = False 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
135 if opts: 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
136 ipc_enabled = opts.ipc_enabled 1satbuevwxyzABCDfgEFGhijkcdpHIJKLMNOPQRSTUVWXYqlrmn
137 if ipc_enabled and not _ipc.is_supported(): 1satbuevwxyzABCDfgEFGhijkcdpHIJKLMNOPQRSTUVWXYqlrmn
138 raise RuntimeError("IPC is not available on {platform.system()}")
139 opts_base._max_size = opts.max_size 1satbuevwxyzABCDfgEFGhijkcdpHIJKLMNOPQRSTUVWXYqlrmn
140 opts_base._use_current = False 1satbuevwxyzABCDfgEFGhijkcdpHIJKLMNOPQRSTUVWXYqlrmn
141 opts_base._ipc_enabled = ipc_enabled 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
142 opts_base._location = cydriver.CUmemLocationType.CU_MEM_LOCATION_TYPE_DEVICE 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
143 opts_base._type = cydriver.CUmemAllocationType.CU_MEM_ALLOCATION_TYPE_PINNED 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
145 super().__init__(dev_id, opts_base) 2. / : ; = ? @ [ ] ^ _ s a t b u e v w x y z A B C D f g E F G h i j k c d ` { | } Z 0 p ~ H I J K L M N O P Q R S T U V W X Y q abl r m n bbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAb1 2 3 4 5 6 7 8 9 ! # $ % ' ( ) * + BbCbDb, Eb-
147 def __reduce__(self):
148 return DeviceMemoryResource.from_registry, (self.uuid,) 1abcd
150 @staticmethod
151 def from_registry(uuid: uuid.UUID) -> DeviceMemoryResource: # no-cython-lint
152 """
153 Obtain a registered mapped memory resource.
155 Raises
156 ------
157 RuntimeError
158 If no mapped memory resource is found in the registry.
159 """
160 return <DeviceMemoryResource>(_ipc.MP_from_registry(uuid))
162 def register(self, uuid: uuid.UUID) -> DeviceMemoryResource: # no-cython-lint
163 """
164 Register a mapped memory resource.
166 Returns
167 -------
168 The registered mapped memory resource. If one was previously registered
169 with the given key, it is returned.
170 """
171 return <DeviceMemoryResource>(_ipc.MP_register(self, uuid))
173 @classmethod
174 def from_allocation_handle(
175 cls, device_id: Device | int, alloc_handle: int | IPCAllocationHandle
176 ) -> DeviceMemoryResource:
177 """Create a device memory resource from an allocation handle.
179 Construct a new `DeviceMemoryResource` instance that imports a memory
180 pool from a shareable handle. The memory pool is marked as owned, and
181 the resource is associated with the specified `device_id`.
183 Parameters
184 ----------
185 device_id : int | Device
186 The ID of the device or a Device object for which the memory
187 resource is created.
189 alloc_handle : int | IPCAllocationHandle
190 The shareable handle of the device memory resource to import. If an
191 integer is supplied, it must represent a valid platform-specific
192 handle. It is the caller's responsibility to close that handle.
194 Returns
195 -------
196 A new device memory resource instance with the imported handle.
197 """
198 cdef DeviceMemoryResource mr = <DeviceMemoryResource>(
199 _ipc.MP_from_allocation_handle(cls, alloc_handle))
200 from .._device import Device
201 mr._dev_id = Device(device_id).device_id
202 return mr
204 def get_allocation_handle(self) -> IPCAllocationHandle:
205 """Export the memory pool handle to be shared (requires IPC).
207 The handle can be used to share the memory pool with other processes.
208 The handle is cached in this `MemoryResource` and owned by it.
210 Returns
211 -------
212 The shareable handle for the memory pool.
213 """
214 if not self.is_ipc_enabled: 2Fba b Gbe HbIbJbQbKbLbMbf g NbObPbh i j k c d q l r m n
215 raise RuntimeError("Memory resource is not IPC-enabled") 1q
216 return self._ipc_data._alloc_handle 2Fba b Gbe HbIbJbQbKbLbMbf g NbObPbh i j k c d l r m n
218 @property
219 def is_device_accessible(self) -> bool:
220 """Return True. This memory resource provides device-accessible buffers."""
221 return True 2Z 0 p 1 2 Rb3 4 5 SbTb6 7 8 Ub9 ! Vb# $ % WbXbYb' Zb0b( 1b2b) 3b* 4b+ 5b, 6b-
223 @property
224 def is_host_accessible(self) -> bool:
225 """Return False. This memory resource does not provide host-accessible buffers."""
226 return False 1Z0p
229# Note: this is referenced in instructions to debug nvbug 5698116.
230cpdef DMR_mempool_get_access(DeviceMemoryResource dmr, int device_id):
231 """
232 Probes peer access from the given device using cuMemPoolGetAccess.
234 Parameters
235 ----------
236 device_id : int or Device
237 The device to query access for.
239 Returns
240 -------
241 str
242 Access permissions: "rw" for read-write, "r" for read-only, "" for no access.
243 """
244 from .._device import Device
246 cdef int dev_id = Device(device_id).device_id
247 cdef cydriver.CUmemAccess_flags flags
248 cdef cydriver.CUmemLocation location
250 location.type = cydriver.CUmemLocationType.CU_MEM_LOCATION_TYPE_DEVICE
251 location.id = dev_id
253 with nogil:
254 HANDLE_RETURN(cydriver.cuMemPoolGetAccess(&flags, as_cu(dmr._h_pool), &location))
256 if flags == cydriver.CUmemAccess_flags.CU_MEM_ACCESS_FLAGS_PROT_READWRITE:
257 return "rw"
258 elif flags == cydriver.CUmemAccess_flags.CU_MEM_ACCESS_FLAGS_PROT_READ:
259 return "r"
260 else:
261 return ""
264def _deep_reduce_device_memory_resource(mr):
265 check_multiprocessing_start_method() 2Fba b Gbe HbIbJbKbLbMbf g NbObPbh i j k c d l m n
266 from .._device import Device 2Fba b Gbe HbIbJbKbLbMbf g NbObPbh i j k c d l m n
267 device = Device(mr.device_id) 2Fba b Gbe HbIbJbKbLbMbf g NbObPbh i j k c d l m n
268 alloc_handle = mr.get_allocation_handle() 2Fba b Gbe HbIbJbKbLbMbf g NbObPbh i j k c d l m n
269 return mr.from_allocation_handle, (device, alloc_handle) 2Fba b Gbe HbIbJbKbLbMbf g NbObPbh i j k c d l m n
272multiprocessing.reduction.register(DeviceMemoryResource, _deep_reduce_device_memory_resource)