44
55from __future__ import annotations
66
7- from libc.stdint cimport uintptr_t
7+ cimport cpython
8+ from libc.stdint cimport uintptr_t, intptr_t
9+ from libc.string cimport memset
10+
11+ from cuda.bindings cimport cydriver
12+
813from cuda.core.experimental._utils.cuda_utils cimport (
914 _check_driver_error as raise_if_driver_error,
1015 check_or_create_options,
16+ HANDLE_RETURN,
1117)
1218
1319from dataclasses import dataclass
@@ -53,7 +59,7 @@ cdef class Buffer:
5359 """
5460
5561 cdef:
56- uintptr_t _ptr
62+ intptr_t _ptr
5763 size_t _size
5864 object _mr
5965 object _ptr_obj
@@ -140,20 +146,23 @@ cdef class Buffer:
140146 """Export a buffer allocated for sharing between processes."""
141147 if not self._mr.is_ipc_enabled:
142148 raise RuntimeError("Memory resource is not IPC-enabled")
143- err , ptr = driver.cuMemPoolExportPointer(self .handle)
144- raise_if_driver_error(err )
145- return IPCBufferDescriptor._init(ptr.reserved , self.size )
149+ cdef cydriver.CUmemPoolPtrExportData data
150+ with nogil:
151+ HANDLE_RETURN(cydriver.cuMemPoolExportPointer(&data , <cydriver.CUdeviceptr>(self._ptr )))
152+ cdef bytes data_b = cpython.PyBytes_FromStringAndSize(< char * > (data.reserved), sizeof(data.reserved))
153+ return IPCBufferDescriptor._init(data_b , self.size )
146154
147155 @classmethod
148- def from_ipc_descriptor(cls , mr: MemoryResource , ipc_buffer: IPCBufferDescriptor ) -> Buffer:
156+ def from_ipc_descriptor(cls , mr: DeviceMemoryResource , ipc_buffer: IPCBufferDescriptor ) -> Buffer:
149157 """Import a buffer that was exported from another process."""
150158 if not mr.is_ipc_enabled:
151159 raise RuntimeError("Memory resource is not IPC-enabled")
152- share_data = driver .CUmemPoolPtrExportData()
160+ cdef cydriver .CUmemPoolPtrExportData share_data
153161 share_data.reserved = ipc_buffer._reserved
154- err , ptr = driver.cuMemPoolImportPointer(mr._mempool_handle, share_data)
155- raise_if_driver_error(err )
156- return Buffer.from_handle(ptr , ipc_buffer.size , mr )
162+ cdef cydriver.CUdeviceptr ptr
163+ with nogil:
164+ HANDLE_RETURN(cydriver.cuMemPoolImportPointer(&ptr , mr._mempool_handle , &share_data ))
165+ return Buffer.from_handle(<intptr_t>ptr , ipc_buffer.size , mr )
157166
158167 def copy_to(self , dst: Buffer = None , *, stream: Stream ) -> Buffer:
159168 """Copy from this buffer to the dst buffer asynchronously on the given stream.
@@ -360,10 +369,9 @@ class MemoryResource(abc.ABC):
360369
361370# IPC is currently only supported on Linux. On other platforms , the IPC handle
362371# type is set equal to the no-IPC handle type.
372+ cdef cydriver.CUmemAllocationHandleType _IPC_HANDLE_TYPE = cydriver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR \
373+ if platform.system() == "Linux" else cydriver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_NONE
363374
364- _NOIPC_HANDLE_TYPE = driver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_NONE
365- _IPC_HANDLE_TYPE = driver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR \
366- if platform.system() == "Linux" else _NOIPC_HANDLE_TYPE
367375
368376cdef class IPCBufferDescriptor:
369377 """Serializable object describing a buffer that can be shared between processes."""
@@ -466,6 +474,7 @@ cdef class DeviceMemoryResourceOptions:
466474 max_size : cython.int = 0
467475
468476
477+ # TODO: cythonize this?
469478class DeviceMemoryResourceAttributes :
470479 def __init__ (self , *args , **kwargs ):
471480 raise RuntimeError (" DeviceMemoryResourceAttributes cannot be instantiated directly. Please use MemoryResource APIs." )
@@ -483,8 +492,9 @@ class DeviceMemoryResourceAttributes:
483492 def fget (self ) -> property_type:
484493 mr = self ._mr()
485494 if mr is None:
486- raise RuntimeError("DeviceMemoryResource is expired")
487- err , value = driver.cuMemPoolGetAttribute(mr._mempool_handle, attr_enum)
495+ raise RuntimeError("DeviceMemoryResource is expired")
496+ # TODO: this implementation does not allow lowering to Cython + nogil
497+ err , value = driver.cuMemPoolGetAttribute(mr.handle, attr_enum)
488498 raise_if_driver_error(err )
489499 return property_type(value )
490500 return property(fget = fget, doc = stub.__doc__ )
@@ -531,7 +541,34 @@ class DeviceMemoryResourceAttributes:
531541# and the serialized buffer descriptor.
532542_ipc_registry = {}
533543
534- class DeviceMemoryResource (MemoryResource ):
544+
545+ cdef class _DeviceMemoryResourceBase:
546+ """ Internal only. Responsible for offering C layout & attribute access."""
547+ cdef:
548+ int _dev_id
549+ cydriver.CUmemoryPool _mempool_handle
550+ object _attributes
551+ cydriver.CUmemAllocationHandleType _ipc_handle_type
552+ bint _mempool_owned
553+ bint _is_mapped
554+ object _uuid
555+ IPCAllocationHandle _alloc_handle
556+
557+ def __cinit__ (self ):
558+ self ._dev_id = cydriver.CU_DEVICE_INVALID
559+ self ._mempool_handle = NULL
560+ self ._attributes = None
561+ self ._ipc_handle_type = cydriver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_MAX
562+ self ._mempool_owned = False
563+ self ._is_mapped = False
564+ self ._uuid = None
565+ self ._alloc_handle = None
566+
567+ def __dealloc__ (self ):
568+ pass
569+
570+
571+ cdef class DeviceMemoryResource(_DeviceMemoryResourceBase, MemoryResource):
535572 """
536573 Create a device memory resource managing a stream-ordered memory pool.
537574
@@ -609,97 +646,91 @@ class DeviceMemoryResource(MemoryResource):
609646 methods. The reconstruction procedure uses the registry to find the
610647 associated MMR.
611648 """
612- __slots__ = (" _dev_id" , " _mempool_handle" , " _attributes" , " _ipc_handle_type" ,
613- " _mempool_owned" , " _is_mapped" , " _uuid" , " _alloc_handle" )
649+ cdef:
650+ dict __dict__
651+ object __weakref__
614652
615653 def __init__ (self , device_id: int | Device , options = None ):
616- device_id = getattr (device_id, ' device_id' , device_id)
654+ cdef int dev_id = getattr (device_id, ' device_id' , device_id)
617655 opts = check_or_create_options(
618656 DeviceMemoryResourceOptions, options, " DeviceMemoryResource options" , keep_none = True
619657 )
658+ cdef cydriver.cuuint64_t current_threshold
659+ cdef cydriver.cuuint64_t max_threshold = 0xFFFFFFFFFFFFFFFF
660+ cdef cydriver.CUmemPoolProps properties
620661
621662 if opts is None :
622663 # Get the current memory pool.
623- self ._dev_id = device_id
624- self ._mempool_handle = None
625- self ._attributes = None
626- self ._ipc_handle_type = _NOIPC_HANDLE_TYPE
664+ self ._dev_id = dev_id
665+ self ._ipc_handle_type = cydriver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_NONE
627666 self ._mempool_owned = False
628- self ._is_mapped = False
629- self ._uuid = None
630- self ._alloc_handle = None
631667
632- err, self ._mempool_handle = driver.cuDeviceGetMemPool( self .device_id)
633- raise_if_driver_error(err )
668+ with nogil:
669+ HANDLE_RETURN(cydriver.cuDeviceGetMemPool( & ( self ._mempool_handle), dev_id) )
634670
635- # Set a higher release threshold to improve performance when there are no active allocations.
636- # By default, the release threshold is 0, which means memory is immediately released back
637- # to the OS when there are no active suballocations, causing performance issues.
638- # Check current release threshold
639- err, current_threshold = driver.cuMemPoolGetAttribute(
640- self ._mempool_handle, driver.CUmemPool_attribute.CU_MEMPOOL_ATTR_RELEASE_THRESHOLD
641- )
642- raise_if_driver_error(err)
643- # If threshold is 0 (default), set it to maximum to retain memory in the pool
644- if int (current_threshold) == 0 :
645- err, = driver.cuMemPoolSetAttribute(
646- self ._mempool_handle,
647- driver.CUmemPool_attribute.CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
648- driver.cuuint64_t(0xFFFFFFFFFFFFFFFF ),
671+ # Set a higher release threshold to improve performance when there are no active allocations.
672+ # By default, the release threshold is 0, which means memory is immediately released back
673+ # to the OS when there are no active suballocations, causing performance issues.
674+ # Check current release threshold
675+ HANDLE_RETURN(cydriver.cuMemPoolGetAttribute(
676+ self ._mempool_handle, cydriver.CUmemPool_attribute.CU_MEMPOOL_ATTR_RELEASE_THRESHOLD, & current_threshold)
649677 )
650- raise_if_driver_error(err)
678+
679+ # If threshold is 0 (default), set it to maximum to retain memory in the pool
680+ if current_threshold == 0 :
681+ HANDLE_RETURN(cydriver.cuMemPoolSetAttribute(
682+ self ._mempool_handle,
683+ cydriver.CUmemPool_attribute.CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
684+ & max_threshold
685+ ))
651686 else :
652687 # Create a new memory pool.
653- if opts.ipc_enabled and _IPC_HANDLE_TYPE == _NOIPC_HANDLE_TYPE :
688+ if opts.ipc_enabled and _IPC_HANDLE_TYPE == cydriver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_NONE :
654689 raise RuntimeError (" IPC is not available on {platform.system()}" )
655690
656- properties = driver.CUmemPoolProps()
657- properties.allocType = driver.CUmemAllocationType.CU_MEM_ALLOCATION_TYPE_PINNED
658- properties.handleTypes = _IPC_HANDLE_TYPE if opts.ipc_enabled else _NOIPC_HANDLE_TYPE
659- properties.location = driver.CUmemLocation()
660- properties.location.id = device_id
661- properties.location.type = driver.CUmemLocationType.CU_MEM_LOCATION_TYPE_DEVICE
691+ memset(& properties, 0 , sizeof(cydriver.CUmemPoolProps))
692+ properties.allocType = cydriver.CUmemAllocationType.CU_MEM_ALLOCATION_TYPE_PINNED
693+ properties.handleTypes = _IPC_HANDLE_TYPE if opts.ipc_enabled else cydriver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_NONE
694+ properties.location.id = dev_id
695+ properties.location.type = cydriver.CUmemLocationType.CU_MEM_LOCATION_TYPE_DEVICE
662696 properties.maxSize = opts.max_size
663- properties.win32SecurityAttributes = 0
697+ properties.win32SecurityAttributes = NULL
664698 properties.usage = 0
665699
666- self ._dev_id = device_id
667- self ._mempool_handle = None
668- self ._attributes = None
700+ self ._dev_id = dev_id
669701 self ._ipc_handle_type = properties.handleTypes
670702 self ._mempool_owned = True
671- self ._is_mapped = False
672- self ._uuid = None
673- self ._alloc_handle = None
674703
675- err, self ._mempool_handle = driver.cuMemPoolCreate(properties)
676- raise_if_driver_error(err)
704+ with nogil:
705+ HANDLE_RETURN(cydriver.cuMemPoolCreate(& (self ._mempool_handle), & properties))
706+ # TODO: should we also set the threshold here?
677707
678708 if opts.ipc_enabled:
679- self .get_allocation_handle() # enables Buffer.get_ipc_descriptor, sets uuid
709+ self .get_allocation_handle() # enables Buffer.get_ipc_descriptor, sets uuid
680710
681- def __del__ (self ):
711+ def __dealloc__ (self ):
682712 self .close()
683713
684714 def close (self ):
685715 """ Close the device memory resource and destroy the associated memory pool if owned."""
686- if self ._mempool_handle is not None :
687- try :
688- if self ._mempool_owned:
689- err, = driver.cuMemPoolDestroy(self ._mempool_handle)
690- raise_if_driver_error(err)
691- finally :
692- if self .is_mapped:
693- self .unregister()
694- self ._dev_id = None
695- self ._mempool_handle = None
696- self ._attributes = None
697- self ._ipc_handle_type = _NOIPC_HANDLE_TYPE
698- self ._mempool_owned = False
699- self ._is_mapped = False
700- self ._uuid = None
701- self ._alloc_handle = None
716+ if self ._mempool_handle == NULL :
717+ return
702718
719+ try :
720+ if self ._mempool_owned:
721+ with nogil:
722+ HANDLE_RETURN(cydriver.cuMemPoolDestroy(self ._mempool_handle))
723+ finally :
724+ if self .is_mapped:
725+ self .unregister()
726+ self ._dev_id = cydriver.CU_DEVICE_INVALID
727+ self ._mempool_handle = NULL
728+ self ._attributes = None
729+ self ._ipc_handle_type = cydriver.CUmemAllocationHandleType.CU_MEM_HANDLE_TYPE_MAX
730+ self ._mempool_owned = False
731+ self ._is_mapped = False
732+ self ._uuid = None
733+ self ._alloc_handle = None
703734
704735 def __reduce__ (self ):
705736 return DeviceMemoryResource.from_registry, (self .uuid,)
@@ -775,30 +806,33 @@ class DeviceMemoryResource(MemoryResource):
775806 """
776807 # Quick exit for registry hits.
777808 uuid = getattr (alloc_handle, ' uuid' , None )
778- self = _ipc_registry.get(uuid)
779- if self is not None:
780- return self
809+ mr = _ipc_registry.get(uuid)
810+ if mr is not None:
811+ return mr
781812
782813 device_id = getattr (device_id, ' device_id' , device_id)
783814
784- self = cls .__new__ (cls )
815+ cdef DeviceMemoryResource self = DeviceMemoryResource .__new__ (cls )
785816 self._dev_id = device_id
786- self._mempool_handle = None
817+ self._mempool_handle = NULL
787818 self._attributes = None
788819 self._ipc_handle_type = _IPC_HANDLE_TYPE
789820 self._mempool_owned = True
790821 self._is_mapped = True
791822 self._uuid = None
792- self._alloc_handle = None # only used for non-imported
823+ self._alloc_handle = None # only used for non-imported
793824
794- err , self._mempool_handle = driver.cuMemPoolImportFromShareableHandle(int (alloc_handle), _IPC_HANDLE_TYPE, 0 )
795- raise_if_driver_error(err )
825+ cdef int handle = int (alloc_handle)
826+ with nogil:
827+ HANDLE_RETURN(cydriver.cuMemPoolImportFromShareableHandle(
828+ &(self._mempool_handle ), &handle , _IPC_HANDLE_TYPE , 0)
829+ )
796830 if uuid is not None:
797831 registered = self .register(uuid)
798832 assert registered is self
799833 return self
800834
801- def get_allocation_handle(self ) -> IPCAllocationHandle :
835+ cpdef IPCAllocationHandle get_allocation_handle(self ):
802836 """ Export the memory pool handle to be shared (requires IPC).
803837
804838 The handle can be used to share the memory pool with other processes.
@@ -808,13 +842,19 @@ class DeviceMemoryResource(MemoryResource):
808842 -------
809843 The shareable handle for the memory pool.
810844 """
845+ # Note: This is Linux only (int for file descriptor)
846+ cdef int alloc_handle
847+
811848 if self ._alloc_handle is None :
812849 if not self .is_ipc_enabled:
813850 raise RuntimeError (" Memory resource is not IPC-enabled" )
814851 if self ._is_mapped:
815852 raise RuntimeError (" Imported memory resource cannot be exported" )
816- err , alloc_handle = driver.cuMemPoolExportToShareableHandle(self ._mempool_handle, _IPC_HANDLE_TYPE, 0 )
817- raise_if_driver_error(err )
853+
854+ with nogil:
855+ HANDLE_RETURN(cydriver.cuMemPoolExportToShareableHandle(
856+ & alloc_handle, self ._mempool_handle, _IPC_HANDLE_TYPE, 0 )
857+ )
818858 try :
819859 assert self ._uuid is None
820860 import uuid
@@ -846,9 +886,11 @@ class DeviceMemoryResource(MemoryResource):
846886 raise TypeError("Cannot allocate from a mapped IPC-enabled memory resource")
847887 if stream is None:
848888 stream = default_stream()
849- err , ptr = driver.cuMemAllocFromPoolAsync(size, self ._mempool_handle, stream.handle)
850- raise_if_driver_error(err )
851- return Buffer._init(ptr , size , self )
889+ cdef cydriver.CUstream s = < cydriver.CUstream>< uintptr_t> (stream.handle)
890+ cdef cydriver.CUdeviceptr devptr
891+ with nogil:
892+ HANDLE_RETURN(cydriver.cuMemAllocFromPoolAsync(&devptr , size , self._mempool_handle , s ))
893+ return Buffer._init(<intptr_t>devptr , size , self )
852894
853895 def deallocate(self , ptr: DevicePointerT , size_t size , stream: Stream = None ):
854896 """ Deallocate a buffer previously allocated by this resource.
@@ -865,8 +907,10 @@ class DeviceMemoryResource(MemoryResource):
865907 """
866908 if stream is None :
867909 stream = default_stream()
868- err, = driver.cuMemFreeAsync(ptr, stream.handle)
869- raise_if_driver_error(err)
910+ cdef cydriver.CUstream s = < cydriver.CUstream>< uintptr_t> (stream.handle)
911+ cdef cydriver.CUdeviceptr devptr = < cydriver.CUdeviceptr>< intptr_t> ptr
912+ with nogil:
913+ HANDLE_RETURN(cydriver.cuMemFreeAsync(devptr, s))
870914
871915 @property
872916 def attributes (self ) -> DeviceMemoryResourceAttributes:
@@ -881,9 +925,9 @@ class DeviceMemoryResource(MemoryResource):
881925 return self._dev_id
882926
883927 @property
884- def handle(self ) -> cuda.bindings. driver.CUmemoryPool:
928+ def handle(self ) -> driver.CUmemoryPool:
885929 """Handle to the underlying memory pool."""
886- return self._mempool_handle
930+ return driver.CUmemoryPool(<uintptr_t>( self._mempool_handle ))
887931
888932 @property
889933 def is_handle_owned(self ) -> bool:
@@ -911,7 +955,7 @@ class DeviceMemoryResource(MemoryResource):
911955 @property
912956 def is_ipc_enabled(self ) -> bool:
913957 """Whether this memory resource has IPC enabled."""
914- return self._ipc_handle_type != _NOIPC_HANDLE_TYPE
958+ return self._ipc_handle_type != cydriver.CUmemAllocationHandleType. CU_MEM_HANDLE_TYPE_NONE
915959
916960
917961def _deep_reduce_device_memory_resource(mr ):
0 commit comments