Skip to content

Commit 3254fbc

Browse files
committed
Add hash and equality support to Stream, Event, Context, and Device classes, enabling their use as dictionary keys and in sets.
1 parent 453577d commit 3254fbc

File tree

7 files changed

+778
-2
lines changed

7 files changed

+778
-2
lines changed

cuda_core/cuda/core/experimental/_context.pyx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,35 @@ cdef class Context:
2929
return ctx
3030

3131
def __eq__(self, other):
32+
"""Check equality based on the underlying CUcontext handle address.
33+
34+
Two Context objects are considered equal if they wrap the same
35+
underlying CUDA context.
36+
37+
Parameters
38+
----------
39+
other : object
40+
Another object to compare with.
41+
42+
Returns
43+
-------
44+
bool
45+
True if other is a Context wrapping the same handle, False otherwise.
46+
"""
47+
if not isinstance(other, Context):
48+
return NotImplemented
3249
return int(self._handle) == int(other._handle)
50+
51+
def __hash__(self) -> int:
52+
"""Return hash based on the underlying CUcontext handle address.
53+
54+
This enables Context objects to be used as dictionary keys and in sets.
55+
Two Context objects wrapping the same underlying CUDA context will hash
56+
to the same value and be considered equal.
57+
58+
Returns
59+
-------
60+
int
61+
Hash value based on the context handle address.
62+
"""
63+
return hash(int(self._handle))

cuda_core/cuda/core/experimental/_device.pyx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,49 @@ class Device:
11451145
def __repr__(self):
11461146
return f"<Device {self._id} ({self.name})>"
11471147

1148+
def __hash__(self) -> int:
1149+
"""Return hash based on the device ordinal.
1150+
1151+
This enables Device objects to be used as dictionary keys and in sets.
1152+
Device objects with the same device_id will hash to the same value
1153+
and be considered equal, even if they are different Python objects or
1154+
exist on different threads.
1155+
1156+
Returns
1157+
-------
1158+
int
1159+
Hash value based on the device ordinal (device_id).
1160+
1161+
Notes
1162+
-----
1163+
Device is a per-thread singleton, but equality is based on logical
1164+
device identity (device_id), not Python object identity. This means
1165+
Device(0) on thread A equals Device(0) on thread B.
1166+
"""
1167+
return hash(self._id)
1168+
1169+
def __eq__(self, other) -> bool:
1170+
"""Check equality based on the device ordinal.
1171+
1172+
Two Device objects are considered equal if they have the same device_id,
1173+
regardless of whether they are the same Python object or exist on
1174+
different threads.
1175+
1176+
Parameters
1177+
----------
1178+
other : object
1179+
Another object to compare with.
1180+
1181+
Returns
1182+
-------
1183+
bool
1184+
True if other is a Device with the same device_id, False otherwise.
1185+
Returns NotImplemented if other is not a Device.
1186+
"""
1187+
if not isinstance(other, Device):
1188+
return NotImplemented
1189+
return self._id == other._id
1190+
11481191
def __reduce__(self):
11491192
return Device, (self.device_id,)
11501193

cuda_core/cuda/core/experimental/_event.pyx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,48 @@ cdef class Event:
165165
raise CUDAError(err)
166166
raise RuntimeError(explanation)
167167

168+
def __hash__(self) -> int:
169+
"""Return hash based on the underlying CUevent handle address.
170+
171+
This enables Event objects to be used as dictionary keys and in sets.
172+
Two Event objects wrapping the same underlying CUDA event will hash
173+
to the same value and be considered equal.
174+
175+
Returns
176+
-------
177+
int
178+
Hash value based on the event handle address.
179+
180+
Warning
181+
-------
182+
Using a closed or destroyed event as a dictionary key or in a set
183+
results in undefined behavior. The event handle may be reused by
184+
the CUDA driver for new events.
185+
"""
186+
return hash(<uintptr_t>(self._handle))
187+
188+
def __eq__(self, other) -> bool:
189+
"""Check equality based on the underlying CUevent handle address.
190+
191+
Two Event objects are considered equal if they wrap the same
192+
underlying CUDA event, regardless of whether they are the same
193+
Python object.
194+
195+
Parameters
196+
----------
197+
other : object
198+
Another object to compare with.
199+
200+
Returns
201+
-------
202+
bool
203+
True if other is an Event wrapping the same handle, False otherwise.
204+
Returns NotImplemented if other is not an Event.
205+
"""
206+
if not isinstance(other, Event):
207+
return NotImplemented
208+
return <uintptr_t>(self._handle) == <uintptr_t>((<Event>other)._handle)
209+
168210
def get_ipc_descriptor(self) -> IPCEventDescriptor:
169211
"""Export an event allocated for sharing between processes."""
170212
if self._ipc_descriptor is not None:

cuda_core/cuda/core/experimental/_stream.pyx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,48 @@ cdef class Stream:
197197
"""Return an instance of a __cuda_stream__ protocol."""
198198
return (0, <uintptr_t>(self._handle))
199199

200+
def __hash__(self) -> int:
201+
"""Return hash based on the underlying CUstream handle address.
202+
203+
This enables Stream objects to be used as dictionary keys and in sets.
204+
Two Stream objects wrapping the same underlying CUDA stream will hash
205+
to the same value and be considered equal.
206+
207+
Returns
208+
-------
209+
int
210+
Hash value based on the stream handle address.
211+
212+
Warning
213+
-------
214+
Using a closed or destroyed stream as a dictionary key or in a set
215+
results in undefined behavior. The stream handle may be reused by
216+
the CUDA driver for new streams.
217+
"""
218+
return hash(<uintptr_t>(self._handle))
219+
220+
def __eq__(self, other) -> bool:
221+
"""Check equality based on the underlying CUstream handle address.
222+
223+
Two Stream objects are considered equal if they wrap the same
224+
underlying CUDA stream, regardless of whether they are the same
225+
Python object.
226+
227+
Parameters
228+
----------
229+
other : object
230+
Another object to compare with.
231+
232+
Returns
233+
-------
234+
bool
235+
True if other is a Stream wrapping the same handle, False otherwise.
236+
Returns NotImplemented if other is not a Stream.
237+
"""
238+
if not isinstance(other, Stream):
239+
return NotImplemented
240+
return <uintptr_t>(self._handle) == <uintptr_t>((<Stream>other)._handle)
241+
200242
@property
201243
def handle(self) -> cuda.bindings.driver.CUstream:
202244
"""Return the underlying ``CUstream`` object.

0 commit comments

Comments
 (0)