Skip to content

Commit 227a126

Browse files
oleksandr-pavlykDiptorup Deb
and
Diptorup Deb
authored
Added C-API hash function, used them in Python interface (#491)
* Added `size_t DPCTLQueue_Hash(QRef)` * Added `size_t DPCTLDevice_Hash(DRef)` * Added `size_t DPCTLContext_Hash(CtxRef)` * added DPCTL<Name>_Hash to _backend * Defines __hash__ for all classes that define __eq__ method. - SyclContext, SyclDevice, SyclQueue classes previously where not hashable. The PR defines __hash__ function based on the C API DPCTL<Obj>_Hash function. * Extended tests to check consistency of __eq__ and __hash__ * try to use 'python -m pytest args' rather than 'pytest args' * Test runners should not be activating oneAPI. More recent run-times are being installed into Python environment and should be used instead of those in the compiler installation * changes per PR feedback Co-authored-by: Diptorup Deb <diptorup.deb@intel.com>
1 parent ca5697c commit 227a126

18 files changed

+185
-44
lines changed

conda-recipe/run_test.bat

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
1-
call "%ONEAPI_ROOT%\compiler\latest\env\vars.bat"
2-
if errorlevel 1 (
3-
echo "oneAPI compiler activation failed%"
4-
exit /b 1
5-
)
6-
REM conda uses %ERRORLEVEL% but FPGA scripts can set it. So it should be reseted.
7-
set ERRORLEVEL=
8-
91
@echo on
102

113
"%PYTHON%" -c "import dpctl"
124
if errorlevel 1 exit 1
135

14-
pytest -q -ra --disable-warnings --pyargs dpctl -vv
6+
python -m pytest -q -ra --disable-warnings --pyargs dpctl -vv
157
if errorlevel 1 exit 1

conda-recipe/run_test.sh

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,5 @@
22

33
set -e
44

5-
# Suppress error b/c it could fail on Ubuntu 18.04
6-
source ${ONEAPI_ROOT}/compiler/latest/env/vars.sh || true
7-
85
${PYTHON} -c "import dpctl"
9-
pytest -q -ra --disable-warnings --cov dpctl --cov-report term-missing --pyargs dpctl -vv
6+
python -m pytest -q -ra --disable-warnings --cov dpctl --cov-report term-missing --pyargs dpctl -vv

dpctl-capi/include/dpctl_sycl_context_interface.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,14 @@ DPCTLContext_GetBackend(__dpctl_keep const DPCTLSyclContextRef CtxRef);
161161
DPCTL_API
162162
void DPCTLContext_Delete(__dpctl_take DPCTLSyclContextRef CtxRef);
163163

164+
/*!
165+
* @brief Wrapper over std::hash<sycl::context>'s operator()
166+
*
167+
* @param CtxRef The DPCTLSyclContextRef pointer.
168+
* @return Hash value of the underlying ``sycl::context`` instance.
169+
* @ingroup ContextInterface
170+
*/
171+
DPCTL_API
172+
size_t DPCTLContext_Hash(__dpctl_take DPCTLSyclContextRef CtxRef);
173+
164174
DPCTL_C_EXTERN_C_END

dpctl-capi/include/dpctl_sycl_device_interface.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,13 @@ uint32_t DPCTLDevice_GetPreferredVectorWidthHalf(
555555
DPCTL_API
556556
__dpctl_give DPCTLSyclDeviceRef
557557
DPCTLDevice_GetParentDevice(__dpctl_keep const DPCTLSyclDeviceRef DRef);
558+
559+
/*!
560+
* @brief Wrapper over
561+
* std::hash<sycl::device>'s operator()
562+
*
563+
* @param DRef Opaque pointer to a sycl::device
564+
* @return Returns hash value.
565+
*/
566+
DPCTL_API
567+
size_t DPCTLDevice_Hash(__dpctl_keep const DPCTLSyclDeviceRef DRef);

dpctl-capi/include/dpctl_sycl_queue_interface.h

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,11 @@ __dpctl_give DPCTLSyclQueueRef
126126
DPCTLQueue_Copy(__dpctl_keep const DPCTLSyclQueueRef QRef);
127127

128128
/*!
129-
* @brief Checks if two DPCTLSyclQueueRef objects point to the same sycl::queue.
129+
* @brief Checks if two DPCTLSyclQueueRef objects point to the
130+
* same ``sycl::queue``.
130131
*
131-
* @param QRef1 First opaque pointer to the sycl queue.
132-
* @param QRef2 Second opaque pointer to the sycl queue.
132+
* @param QRef1 First opaque pointer to the ``sycl::queue``.
133+
* @param QRef2 Second opaque pointer to the ``sycl::queue``.
133134
* @return True if the underlying sycl::queue are same, false otherwise.
134135
* @ingroup QueueInterface
135136
*/
@@ -174,11 +175,12 @@ DPCTLQueue_GetDevice(__dpctl_keep const DPCTLSyclQueueRef QRef);
174175
* @brief Submits the kernel to the specified queue with the provided range
175176
* argument.
176177
*
177-
* A wrapper over sycl::queue.submit(). The function takes an interoperability
178-
* kernel, the kernel arguments, and a Sycl queue as input. The kernel is
179-
* submitted as parallel_for(range<NRange>, *unwrap(KRef)).
178+
* A wrapper over ``sycl::queue.submit()``. The function takes an
179+
* interoperability kernel, the kernel arguments, and a ``sycl::queue`` as
180+
* input. The kernel is submitted as
181+
* ``parallel_for(range<NRange>, *unwrap(KRef))``.
180182
*
181-
* \todo sycl::buffer arguments are not supported yet.
183+
* \todo ``sycl::buffer`` arguments are not supported yet.
182184
* \todo Add support for id<Dims> WorkItemOffset
183185
*
184186
* @param KRef Opaque pointer to an OpenCL interoperability kernel
@@ -195,11 +197,11 @@ DPCTLQueue_GetDevice(__dpctl_keep const DPCTLSyclQueueRef QRef);
195197
* dimensions.
196198
* @param NRange Size of the gRange array.
197199
* @param DepEvents List of dependent DPCTLSyclEventRef objects (events)
198-
* for the kernel. We call sycl::handler.depends_on for
199-
* each of the provided events.
200+
* for the kernel. We call ``sycl::handler.depends_on``
201+
* for each of the provided events.
200202
* @param NDepEvents Size of the DepEvents list.
201-
* @return An opaque pointer to the sycl::event returned by the
202-
* sycl::queue.submit() function.
203+
* @return An opaque pointer to the ``sycl::event`` returned by the
204+
* ``sycl::queue.submit()`` function.
203205
* @ingroup QueueInterface
204206
*/
205207
DPCTL_API
@@ -218,9 +220,9 @@ DPCTLQueue_SubmitRange(__dpctl_keep const DPCTLSyclKernelRef KRef,
218220
* @brief Submits the kernel to the specified queue with the provided nd_range
219221
* argument.
220222
*
221-
* A wrapper over sycl::queue.submit(). The function takes an interoperability
222-
* kernel, the kernel arguments, and a Sycl queue as input. The kernel is
223-
* submitted as parallel_for(nd_range<NRange>, *unwrap(KRef)).
223+
* A wrapper over ``sycl::queue.submit()``. The function takes an
224+
* interoperability kernel, the kernel arguments, and a Sycl queue as input.
225+
* The kernel is submitted as ``parallel_for(nd_range<NRange>, *unwrap(KRef))``.
224226
*
225227
* \todo sycl::buffer arguments are not supported yet.
226228
* \todo Add support for id<Dims> WorkItemOffset
@@ -243,11 +245,11 @@ DPCTLQueue_SubmitRange(__dpctl_keep const DPCTLSyclKernelRef KRef,
243245
* @param NDims The number of dimensions for both local and global
244246
* ranges.
245247
* @param DepEvents List of dependent DPCTLSyclEventRef objects (events)
246-
* for the kernel. We call sycl::handler.depends_on for
247-
* each of the provided events.
248+
* for the kernel. We call ``sycl::handler.depends_on``
249+
* for each of the provided events.
248250
* @param NDepEvents Size of the DepEvents list.
249-
* @return An opaque pointer to the sycl::event returned by the
250-
* sycl::queue.submit() function.
251+
* @return An opaque pointer to the ``sycl::event`` returned by the
252+
* ``sycl::queue.submit()`` function.
251253
* @ingroup QueueInterface
252254
*/
253255
DPCTL_API
@@ -264,20 +266,20 @@ DPCTLQueue_SubmitNDRange(__dpctl_keep const DPCTLSyclKernelRef KRef,
264266
size_t NDepEvents);
265267

266268
/*!
267-
* @brief Calls the sycl::queue.submit function to do a blocking wait on all
268-
* enqueued tasks in the queue.
269+
* @brief Calls the ``sycl::queue.submit`` function to do a blocking wait on
270+
* all enqueued tasks in the queue.
269271
*
270-
* @param QRef Opaque pointer to a sycl::queue.
272+
* @param QRef Opaque pointer to a ``sycl::queue``.
271273
* @ingroup QueueInterface
272274
*/
273275
DPCTL_API
274276
void DPCTLQueue_Wait(__dpctl_keep const DPCTLSyclQueueRef QRef);
275277

276278
/*!
277-
* @brief C-API wrapper for sycl::queue::memcpy, the function waits on an event
278-
* till the memcpy operation completes.
279+
* @brief C-API wrapper for ``sycl::queue::memcpy``, the function waits on an
280+
* event till the memcpy operation completes.
279281
*
280-
* @param QRef An opaque pointer to the sycl queue.
282+
* @param QRef An opaque pointer to the ``sycl::queue``.
281283
* @param Dest An USM pointer to the destination memory.
282284
* @param Src An USM pointer to the source memory.
283285
* @param Count A number of bytes to copy.
@@ -290,10 +292,10 @@ void DPCTLQueue_Memcpy(__dpctl_keep const DPCTLSyclQueueRef QRef,
290292
size_t Count);
291293

292294
/*!
293-
* @brief C-API wrapper for sycl::queue::prefetch, the function waits on an
295+
* @brief C-API wrapper for ``sycl::queue::prefetch``, the function waits on an
294296
* event till the prefetch operation completes.
295297
*
296-
* @param QRef An opaque pointer to the sycl queue.
298+
* @param QRef An opaque pointer to the ``sycl::queue``.
297299
* @param Ptr An USM pointer to memory.
298300
* @param Count A number of bytes to prefetch.
299301
* @ingroup QueueInterface
@@ -307,7 +309,7 @@ void DPCTLQueue_Prefetch(__dpctl_keep DPCTLSyclQueueRef QRef,
307309
* @brief C-API wrapper for sycl::queue::mem_advise, the function waits on an
308310
* event till the operation completes.
309311
*
310-
* @param QRef An opaque pointer to the sycl queue.
312+
* @param QRef An opaque pointer to the ``sycl::queue``.
311313
* @param Ptr An USM pointer to memory.
312314
* @param Count A number of bytes to prefetch.
313315
* @param Advice Device-defined advice for the specified allocation.
@@ -325,10 +327,20 @@ void DPCTLQueue_MemAdvise(__dpctl_keep DPCTLSyclQueueRef QRef,
325327
* @brief C-API wrapper for sycl::queue::is_in_order that indicates whether
326328
* the referenced queue is in-order or out-of-order.
327329
*
328-
* @param QRef An opaque pointer to the sycl queue.
330+
* @param QRef An opaque pointer to the ``sycl::queue``.
329331
* @ingroup QueueInterface
330332
*/
331333
DPCTL_API
332334
bool DPCTLQueue_IsInOrder(__dpctl_keep const DPCTLSyclQueueRef QRef);
333335

336+
/*!
337+
* @brief C-API wrapper for std::hash<sycl::queue>'s operator().
338+
*
339+
* @param QRef An opaque pointer to the ``sycl::queue``.
340+
* @return Hash value of the underlying ``sycl::queue`` instance.
341+
* @ingroup QueueInterface
342+
*/
343+
DPCTL_API
344+
size_t DPCTLQueue_Hash(__dpctl_keep const DPCTLSyclQueueRef QRef);
345+
334346
DPCTL_C_EXTERN_C_END

dpctl-capi/source/dpctl_sycl_context_interface.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,17 @@ DPCTLContext_GetBackend(__dpctl_keep const DPCTLSyclContextRef CtxRef)
197197
return DPCTL_UNKNOWN_BACKEND;
198198
}
199199
}
200+
201+
size_t DPCTLContext_Hash(__dpctl_keep const DPCTLSyclContextRef CtxRef)
202+
{
203+
if (CtxRef) {
204+
auto C = unwrap(CtxRef);
205+
std::hash<context> hash_fn;
206+
return hash_fn(*C);
207+
}
208+
else {
209+
std::cerr << "Argument CtxRef is null"
210+
<< "/n";
211+
return 0;
212+
}
213+
}

dpctl-capi/source/dpctl_sycl_device_interface.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,3 +690,18 @@ __dpctl_give DPCTLDeviceVectorRef DPCTLDevice_CreateSubDevicesByAffinity(
690690
}
691691
return wrap(Devices);
692692
}
693+
694+
size_t DPCTLDevice_Hash(__dpctl_keep const DPCTLSyclDeviceRef DRef)
695+
{
696+
if (DRef) {
697+
auto D = unwrap(DRef);
698+
std::hash<device> hash_fn;
699+
return hash_fn(*D);
700+
}
701+
else {
702+
// todo: log error
703+
std::cerr << "Argument DRef is null"
704+
<< "/n";
705+
return 0;
706+
}
707+
}

dpctl-capi/source/dpctl_sycl_queue_interface.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,9 +454,15 @@ void DPCTLQueue_Wait(__dpctl_keep DPCTLSyclQueueRef QRef)
454454
{
455455
// \todo what happens if the QRef is null or a pointer to a valid sycl
456456
// queue
457-
auto SyclQueue = unwrap(QRef);
458-
if (SyclQueue)
459-
SyclQueue->wait();
457+
if (QRef) {
458+
auto SyclQueue = unwrap(QRef);
459+
if (SyclQueue)
460+
SyclQueue->wait();
461+
}
462+
else {
463+
// todo: log error
464+
std::cerr << "Argument QRef is NULL" << '\n';
465+
}
460466
}
461467

462468
void DPCTLQueue_Memcpy(__dpctl_keep const DPCTLSyclQueueRef QRef,
@@ -504,3 +510,17 @@ bool DPCTLQueue_IsInOrder(__dpctl_keep const DPCTLSyclQueueRef QRef)
504510
else
505511
return false;
506512
}
513+
514+
size_t DPCTLQueue_Hash(__dpctl_keep const DPCTLSyclQueueRef QRef)
515+
{
516+
auto Q = unwrap(QRef);
517+
if (Q) {
518+
std::hash<queue> hash_fn;
519+
return hash_fn(*Q);
520+
}
521+
else {
522+
// todo: log error
523+
std::cerr << "Argument QRef is null" << '\n';
524+
return 0;
525+
}
526+
}

dpctl-capi/tests/test_sycl_context_interface.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ TEST_P(TestDPCTLContextInterface, ChkAreEq)
177177
EXPECT_NO_FATAL_FAILURE(are_not_eq = DPCTLContext_AreEq(CRef1, CRef3));
178178
EXPECT_TRUE(are_eq);
179179
EXPECT_FALSE(are_not_eq);
180+
EXPECT_TRUE(DPCTLContext_Hash(CRef1) == DPCTLContext_Hash(CRef2));
181+
EXPECT_FALSE(DPCTLContext_Hash(CRef1) == DPCTLContext_Hash(CRef3));
180182

181183
EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef1));
182184
EXPECT_NO_FATAL_FAILURE(DPCTLContext_Delete(CRef2));

dpctl-capi/tests/test_sycl_device_interface.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ TEST_P(TestDPCTLSyclDeviceInterface, ChkCopy)
6868
DPCTLSyclDeviceRef Copied_DRef = nullptr;
6969
EXPECT_NO_FATAL_FAILURE(Copied_DRef = DPCTLDevice_Copy(DRef));
7070
EXPECT_TRUE(bool(Copied_DRef));
71+
EXPECT_TRUE(DPCTLDevice_AreEq(DRef, Copied_DRef));
72+
EXPECT_TRUE(DPCTLDevice_Hash(DRef) == DPCTLDevice_Hash(Copied_DRef));
7173
EXPECT_NO_FATAL_FAILURE(DPCTLDevice_Delete(Copied_DRef));
7274
}
7375

dpctl-capi/tests/test_sycl_queue_interface.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,13 @@ TEST_F(TestDPCTLSyclQueueInterface, CheckAreEq)
166166
}
167167

168168
EXPECT_TRUE(DPCTLQueue_AreEq(Q1, Q2));
169+
EXPECT_TRUE(DPCTLQueue_Hash(Q1) == DPCTLQueue_Hash(Q2));
169170
auto Q3 = DPCTLQueue_CreateForDevice(DRef, nullptr, 0);
170171
auto Q4 = DPCTLQueue_CreateForDevice(DRef, nullptr, 0);
171172

172173
// These are different queues
173174
EXPECT_FALSE(DPCTLQueue_AreEq(Q3, Q4));
175+
EXPECT_FALSE(DPCTLQueue_Hash(Q3) == DPCTLQueue_Hash(Q4));
174176

175177
auto C0 = DPCTLQueue_GetContext(Q3);
176178
auto C1 = DPCTLQueue_GetContext(Q4);

dpctl/_backend.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ cdef extern from "dpctl_sycl_device_interface.h":
152152
cdef DPCTLSyclPlatformRef DPCTLDevice_GetPlatform(
153153
const DPCTLSyclDeviceRef DRef)
154154
cdef const char *DPCTLDevice_GetVendor(const DPCTLSyclDeviceRef DRef)
155+
cdef size_t DPCTLDevice_Hash(const DPCTLSyclDeviceRef DRef)
155156
cdef bool DPCTLDevice_IsAccelerator(const DPCTLSyclDeviceRef DRef)
156157
cdef bool DPCTLDevice_IsCPU(const DPCTLSyclDeviceRef DRef)
157158
cdef bool DPCTLDevice_IsGPU(const DPCTLSyclDeviceRef DRef)
@@ -268,6 +269,7 @@ cdef extern from "dpctl_sycl_context_interface.h":
268269
cdef size_t DPCTLContext_DeviceCount(const DPCTLSyclContextRef CRef)
269270
cdef bool DPCTLContext_AreEq(const DPCTLSyclContextRef CtxRef1,
270271
const DPCTLSyclContextRef CtxRef2)
272+
cdef size_t DPCTLContext_Hash(const DPCTLSyclContextRef CRef)
271273
cdef _backend_type DPCTLContext_GetBackend(const DPCTLSyclContextRef)
272274
cdef void DPCTLContext_Delete(DPCTLSyclContextRef CtxRef)
273275

@@ -307,6 +309,7 @@ cdef extern from "dpctl_sycl_queue_interface.h":
307309
cdef _backend_type DPCTLQueue_GetBackend(const DPCTLSyclQueueRef Q)
308310
cdef DPCTLSyclContextRef DPCTLQueue_GetContext(const DPCTLSyclQueueRef Q)
309311
cdef DPCTLSyclDeviceRef DPCTLQueue_GetDevice(const DPCTLSyclQueueRef Q)
312+
cdef size_t DPCTLQueue_Hash(const DPCTLSyclQueueRef Q)
310313
cdef DPCTLSyclEventRef DPCTLQueue_SubmitRange(
311314
const DPCTLSyclKernelRef Ref,
312315
const DPCTLSyclQueueRef QRef,

dpctl/_sycl_context.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ from ._backend cimport ( # noqa: E211
3434
DPCTLContext_Delete,
3535
DPCTLContext_DeviceCount,
3636
DPCTLContext_GetDevices,
37+
DPCTLContext_Hash,
3738
DPCTLDevice_Copy,
3839
DPCTLDevice_Delete,
3940
DPCTLDeviceMgr_GetCachedContext,
@@ -335,6 +336,13 @@ cdef class SyclContext(_SyclContext):
335336
else:
336337
return False
337338

339+
def __hash__(self):
340+
"""
341+
Returns a hash value by hashing the underlying ``sycl::context`` object.
342+
343+
"""
344+
return DPCTLContext_Hash(self._ctxt_ref)
345+
338346
cdef DPCTLSyclContextRef get_context_ref(self):
339347
return self._ctxt_ref
340348

dpctl/_sycl_device.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ from ._backend cimport ( # noqa: E211
5858
DPCTLDevice_GetSubGroupIndependentForwardProgress,
5959
DPCTLDevice_GetVendor,
6060
DPCTLDevice_HasAspect,
61+
DPCTLDevice_Hash,
6162
DPCTLDevice_IsAccelerator,
6263
DPCTLDevice_IsCPU,
6364
DPCTLDevice_IsGPU,
@@ -709,6 +710,13 @@ cdef class SyclDevice(_SyclDevice):
709710
+ "] at {}>".format(hex(id(self)))
710711
)
711712

713+
def __hash__(self):
714+
"""
715+
Returns a hash value by hashing the underlying ``sycl::device`` object.
716+
717+
"""
718+
return DPCTLDevice_Hash(self._device_ref)
719+
712720
cdef list create_sub_devices_equally(self, size_t count):
713721
""" Returns a list of sub-devices partitioned from this SYCL device
714722
based on the ``count`` parameter.

dpctl/_sycl_queue.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ from ._backend cimport ( # noqa: E211
3838
DPCTLQueue_GetBackend,
3939
DPCTLQueue_GetContext,
4040
DPCTLQueue_GetDevice,
41+
DPCTLQueue_Hash,
4142
DPCTLQueue_IsInOrder,
4243
DPCTLQueue_MemAdvise,
4344
DPCTLQueue_Memcpy,
@@ -863,6 +864,13 @@ cdef class SyclQueue(_SyclQueue):
863864
else:
864865
return "<dpctl." + self.__name__ + " at {}>".format(hex(id(self)))
865866

867+
def __hash__(self):
868+
"""
869+
Returns a hash value by hashing the underlying ``sycl::queue`` object.
870+
871+
"""
872+
return DPCTLQueue_Hash(self._queue_ref)
873+
866874
def _get_capsule(self):
867875
cdef DPCTLSyclQueueRef QRef = NULL
868876
QRef = DPCTLQueue_Copy(self._queue_ref)

0 commit comments

Comments
 (0)