9
9
from typing import Optional , Tuple , TypeVar , Union
10
10
11
11
from cuda .core .experimental ._dlpack import DLDeviceType , make_py_capsule
12
- from cuda .core .experimental ._stream import default_stream
12
+ from cuda .core .experimental ._stream import Stream , default_stream
13
13
from cuda .core .experimental ._utils .cuda_utils import driver , handle_return
14
14
15
- PyCapsule = TypeVar ("PyCapsule" )
16
-
17
15
18
16
# TODO: define a memory property mixin class and make Buffer and
19
17
# MemoryResource both inherit from it
20
18
19
+
20
+ PyCapsule = TypeVar ("PyCapsule" )
21
+ """Represent the capsule type."""
22
+
21
23
DevicePointerT = Union [driver .CUdeviceptr , int , None ]
22
- """A type union of `Cudeviceptr `, `int` and `None` for hinting Buffer.handle."""
24
+ """A type union of :obj:`~driver.CUdeviceptr `, `int` and `None` for hinting :attr:` Buffer.handle` ."""
23
25
24
26
25
27
class Buffer :
@@ -29,19 +31,7 @@ class Buffer:
29
31
different memory resources are to give access to their memory
30
32
allocations.
31
33
32
- Support for data interchange mechanisms are provided by
33
- establishing both the DLPack and the Python-level buffer
34
- protocols.
35
-
36
- Parameters
37
- ----------
38
- ptr : Any
39
- Allocated buffer handle object
40
- size : Any
41
- Memory size of the buffer
42
- mr : :obj:`~_memory.MemoryResource`, optional
43
- Memory resource associated with the buffer
44
-
34
+ Support for data interchange mechanisms are provided by DLPack.
45
35
"""
46
36
47
37
class _MembersNeededForFinalize :
@@ -64,22 +54,26 @@ def close(self, stream=None):
64
54
# TODO: handle ownership? (_mr could be None)
65
55
__slots__ = ("__weakref__" , "_mnff" )
66
56
67
- def __init__ (self , ptr , size , mr : MemoryResource = None ):
57
+ def __new__ (self , * args , ** kwargs ):
58
+ raise RuntimeError ("Buffer objects cannot be instantiated directly. Please use MemoryResource APIs." )
59
+
60
+ @classmethod
61
+ def _init (cls , ptr : DevicePointerT , size : int , mr : Optional [MemoryResource ] = None ):
62
+ self = super ().__new__ (cls )
68
63
self ._mnff = Buffer ._MembersNeededForFinalize (self , ptr , size , mr )
64
+ return self
69
65
70
- def close (self , stream = None ):
66
+ def close (self , stream : Stream = None ):
71
67
"""Deallocate this buffer asynchronously on the given stream.
72
68
73
69
This buffer is released back to their memory resource
74
70
asynchronously on the given stream.
75
71
76
72
Parameters
77
73
----------
78
- stream : Any, optional
79
- The stream object with a __cuda_stream__ protocol to
80
- use for asynchronous deallocation. Defaults to using
81
- the default stream.
82
-
74
+ stream : Stream, optional
75
+ The stream object to use for asynchronous deallocation. If not set,
76
+ the current default is to the default stream.
83
77
"""
84
78
self ._mnff .close (stream )
85
79
@@ -95,7 +89,7 @@ def handle(self) -> DevicePointerT:
95
89
return self ._mnff .ptr
96
90
97
91
@property
98
- def size (self ):
92
+ def size (self ) -> int :
99
93
"""Return the memory size of this buffer."""
100
94
return self ._mnff .size
101
95
@@ -125,7 +119,7 @@ def device_id(self) -> int:
125
119
return self ._mnff .mr .device_id
126
120
raise NotImplementedError ("WIP: Currently this property only supports buffers with associated MemoryResource" )
127
121
128
- def copy_to (self , dst : Buffer = None , * , stream ) -> Buffer :
122
+ def copy_to (self , dst : Buffer = None , * , stream : Stream ) -> Buffer :
129
123
"""Copy from this buffer to the dst buffer asynchronously on the given stream.
130
124
131
125
Copies the data from this buffer to the provided dst buffer.
@@ -136,7 +130,7 @@ def copy_to(self, dst: Buffer = None, *, stream) -> Buffer:
136
130
----------
137
131
dst : :obj:`~_memory.Buffer`
138
132
Source buffer to copy data from
139
- stream : Any
133
+ stream : Stream
140
134
Keyword argument specifying the stream for the
141
135
asynchronous copy
142
136
@@ -154,14 +148,14 @@ def copy_to(self, dst: Buffer = None, *, stream) -> Buffer:
154
148
handle_return (driver .cuMemcpyAsync (dst ._mnff .ptr , self ._mnff .ptr , self ._mnff .size , stream .handle ))
155
149
return dst
156
150
157
- def copy_from (self , src : Buffer , * , stream ):
151
+ def copy_from (self , src : Buffer , * , stream : Stream ):
158
152
"""Copy from the src buffer to this buffer asynchronously on the given stream.
159
153
160
154
Parameters
161
155
----------
162
156
src : :obj:`~_memory.Buffer`
163
157
Source buffer to copy data from
164
- stream : Any
158
+ stream : Stream
165
159
Keyword argument specifying the stream for the
166
160
asynchronous copy
167
161
@@ -219,55 +213,117 @@ def __release_buffer__(self, buffer: memoryview, /):
219
213
# Supporting method paired with __buffer__.
220
214
raise NotImplementedError ("WIP: Buffer.__release_buffer__ hasn't been implemented yet." )
221
215
216
+ @staticmethod
217
+ def from_handle (ptr : DevicePointerT , size : int , mr : Optional [MemoryResource ] = None ) -> Buffer :
218
+ """Create a new :class:`Buffer` object from a pointer.
219
+
220
+ Parameters
221
+ ----------
222
+ ptr : :obj:`~_memory.DevicePointerT`
223
+ Allocated buffer handle object
224
+ size : int
225
+ Memory size of the buffer
226
+ mr : :obj:`~_memory.MemoryResource`, optional
227
+ Memory resource associated with the buffer
228
+ """
229
+ return Buffer ._init (ptr , size , mr = mr )
230
+
222
231
223
232
class MemoryResource (abc .ABC ):
233
+ """Abstract base class for memory resources that manage allocation and deallocation of buffers.
234
+
235
+ Subclasses must implement methods for allocating and deallocation, as well as properties
236
+ associated with this memory resource from which all allocated buffers will inherit. (Since
237
+ all :class:`Buffer` instances allocated and returned by the :meth:`allocate` method would
238
+ hold a reference to self, the buffer properties are retrieved simply by looking up the underlying
239
+ memory resource's respective property.)
240
+ """
241
+
224
242
__slots__ = ("_handle" ,)
225
243
226
244
@abc .abstractmethod
227
- def __init__ (self , * args , ** kwargs ): ...
245
+ def __init__ (self , * args , ** kwargs ):
246
+ """Initialize the memory resource.
247
+
248
+ Subclasses may use additional arguments to configure the resource.
249
+ """
250
+ ...
228
251
229
252
@abc .abstractmethod
230
- def allocate (self , size , stream = None ) -> Buffer : ...
253
+ def allocate (self , size : int , stream : Stream = None ) -> Buffer :
254
+ """Allocate a buffer of the requested size.
255
+
256
+ Parameters
257
+ ----------
258
+ size : int
259
+ The size of the buffer to allocate, in bytes.
260
+ stream : object, optional
261
+ The stream on which to perform the allocation asynchronously.
262
+ If None, allocation is synchronous.
263
+
264
+ Returns
265
+ -------
266
+ Buffer
267
+ The allocated buffer object, which can be used for device or host operations
268
+ depending on the resource's properties.
269
+ """
270
+ ...
231
271
232
272
@abc .abstractmethod
233
- def deallocate (self , ptr , size , stream = None ): ...
273
+ def deallocate (self , ptr : DevicePointerT , size : int , stream : Stream = None ):
274
+ """Deallocate a buffer previously allocated by this resource.
275
+
276
+ Parameters
277
+ ----------
278
+ ptr : object
279
+ The pointer or handle to the buffer to deallocate.
280
+ size : int
281
+ The size of the buffer to deallocate, in bytes.
282
+ stream : object, optional
283
+ The stream on which to perform the deallocation asynchronously.
284
+ If None, deallocation is synchronous.
285
+ """
286
+ ...
234
287
235
288
@property
236
289
@abc .abstractmethod
237
290
def is_device_accessible (self ) -> bool :
238
- # Check if the buffers allocated from this MR can be accessed from
239
- # GPUs.
291
+ """bool: True if buffers allocated by this resource can be accessed on the device."""
240
292
...
241
293
242
294
@property
243
295
@abc .abstractmethod
244
296
def is_host_accessible (self ) -> bool :
245
- # Check if the buffers allocated from this MR can be accessed from
246
- # CPUs.
297
+ """bool: True if buffers allocated by this resource can be accessed on the host."""
247
298
...
248
299
249
300
@property
250
301
@abc .abstractmethod
251
302
def device_id (self ) -> int :
252
- # Return the device ID if this MR is for single devices. Raise an
253
- # exception if it is not.
303
+ """int: The device ordinal for which this memory resource is responsible.
304
+
305
+ Raises
306
+ ------
307
+ RuntimeError
308
+ If the resource is not bound to a specific device.
309
+ """
254
310
...
255
311
256
312
257
313
class _DefaultAsyncMempool (MemoryResource ):
258
314
__slots__ = ("_dev_id" ,)
259
315
260
- def __init__ (self , dev_id ):
316
+ def __init__ (self , dev_id : int ):
261
317
self ._handle = handle_return (driver .cuDeviceGetMemPool (dev_id ))
262
318
self ._dev_id = dev_id
263
319
264
- def allocate (self , size , stream = None ) -> Buffer :
320
+ def allocate (self , size : int , stream : Stream = None ) -> Buffer :
265
321
if stream is None :
266
322
stream = default_stream ()
267
323
ptr = handle_return (driver .cuMemAllocFromPoolAsync (size , self ._handle , stream .handle ))
268
- return Buffer (ptr , size , self )
324
+ return Buffer . _init (ptr , size , self )
269
325
270
- def deallocate (self , ptr , size , stream = None ):
326
+ def deallocate (self , ptr : DevicePointerT , size : int , stream : Stream = None ):
271
327
if stream is None :
272
328
stream = default_stream ()
273
329
handle_return (driver .cuMemFreeAsync (ptr , stream .handle ))
@@ -290,11 +346,11 @@ def __init__(self):
290
346
# TODO: support flags from cuMemHostAlloc?
291
347
self ._handle = None
292
348
293
- def allocate (self , size , stream = None ) -> Buffer :
349
+ def allocate (self , size : int , stream : Stream = None ) -> Buffer :
294
350
ptr = handle_return (driver .cuMemAllocHost (size ))
295
- return Buffer (ptr , size , self )
351
+ return Buffer . _init (ptr , size , self )
296
352
297
- def deallocate (self , ptr , size , stream = None ):
353
+ def deallocate (self , ptr : DevicePointerT , size : int , stream : Stream = None ):
298
354
handle_return (driver .cuMemFreeHost (ptr ))
299
355
300
356
@property
@@ -319,7 +375,7 @@ def __init__(self, dev_id):
319
375
320
376
def allocate (self , size , stream = None ) -> Buffer :
321
377
ptr = handle_return (driver .cuMemAlloc (size ))
322
- return Buffer (ptr , size , self )
378
+ return Buffer . _init (ptr , size , self )
323
379
324
380
def deallocate (self , ptr , size , stream = None ):
325
381
if stream is None :
0 commit comments