From bf9e159b04ad14b7f5fc96e07d4ef2405d31f097 Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 20 Oct 2021 17:04:49 -0500 Subject: [PATCH] Closes #629 Implement dpctl.utils.get_execition_queue( queue_list ) --- .flake8 | 1 + dpctl/tests/test_utils.py | 71 +++++++++++++++++++++++++++ dpctl/utils/__init__.py | 5 ++ dpctl/utils/_compute_follows_data.pyx | 68 +++++++++++++++++++++++++ setup.py | 7 +++ 5 files changed, 152 insertions(+) create mode 100644 dpctl/tests/test_utils.py create mode 100644 dpctl/utils/__init__.py create mode 100644 dpctl/utils/_compute_follows_data.pyx diff --git a/.flake8 b/.flake8 index f261c8a691..6e9c78b236 100644 --- a/.flake8 +++ b/.flake8 @@ -25,6 +25,7 @@ per-file-ignores = dpctl/tensor/_usmarray.pyx: E999, E225, E226, E227 dpctl/tensor/numpy_usm_shared.py: F821 dpctl/tests/_cython_api.pyx: E999, E225, E227, E402 + dpctl/utils/_compute_follows_data.pyx: E999, E225, E227 examples/cython/sycl_buffer/_buffer_example.pyx: E999, E225, E402 examples/cython/sycl_direct_linkage/_buffer_example.pyx: E999, E225, E402 examples/cython/usm_memory/blackscholes.pyx: E999, E225, E226, E402 diff --git a/dpctl/tests/test_utils.py b/dpctl/tests/test_utils.py new file mode 100644 index 0000000000..57aa0db30f --- /dev/null +++ b/dpctl/tests/test_utils.py @@ -0,0 +1,71 @@ +# Data Parallel Control (dpctl) +# +# Copyright 2020-2021 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" Defines unit test cases for utility functions. +""" + +import pytest + +import dpctl +import dpctl.utils + + +def test_get_execution_queue_input_validation(): + with pytest.raises(TypeError): + dpctl.utils.get_execution_queue(dict()) + + +def test_get_execution_queue(): + try: + q = dpctl.SyclQueue() + q2 = dpctl.SyclQueue() + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be create for default device") + + exec_q = dpctl.utils.get_execution_queue(()) + assert exec_q is None + + exec_q = dpctl.utils.get_execution_queue([q]) + assert exec_q is q + + exec_q = dpctl.utils.get_execution_queue([q, q, q, q]) + assert exec_q is q + + exec_q = dpctl.utils.get_execution_queue((q, q, None, q)) + assert exec_q is None + + exec_q = dpctl.utils.get_execution_queue( + ( + q, + q2, + q, + ) + ) + assert exec_q is q + + +def test_get_execution_queue_nonequiv(): + try: + q = dpctl.SyclQueue("cpu") + d1, d2 = q.sycl_device.create_sub_devices(partition=[1, 1]) + ctx = dpctl.SyclContext([q.sycl_device, d1, d2]) + q1 = dpctl.SyclQueue(ctx, d1) + q2 = dpctl.SyclQueue(ctx, d2) + except dpctl.SyclQueueCreationError: + pytest.skip("Queue could not be create for default device") + + exec_q = dpctl.utils.get_execution_queue((q, q1, q2)) + assert exec_q is None diff --git a/dpctl/utils/__init__.py b/dpctl/utils/__init__.py new file mode 100644 index 0000000000..f45afa0fab --- /dev/null +++ b/dpctl/utils/__init__.py @@ -0,0 +1,5 @@ +from ._compute_follows_data import get_execution_queue + +__all__ = [ + "get_execution_queue", +] diff --git a/dpctl/utils/_compute_follows_data.pyx b/dpctl/utils/_compute_follows_data.pyx new file mode 100644 index 0000000000..121a3c422f --- /dev/null +++ b/dpctl/utils/_compute_follows_data.pyx @@ -0,0 +1,68 @@ +# Data Parallel Control (dpctl) +# +# Copyright 2020-2021 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# distutils: language = c++ +# cython: language_level=3 +# cython: linetrace=True + +"""This file implements Python buffer protocol using Sycl USM shared and host +allocators. The USM device allocator is also exposed through this module for +use in other Python modules. +""" + + +import dpctl + +from .._sycl_queue cimport SyclQueue + +__all__ = ["get_execution_queue", ] + + +cdef bint queue_equiv(SyclQueue q1, SyclQueue q2): + """ Queues are equivalent if contexts are the same, + devices are the same, and properties are the same.""" + return ( + (q1 is q2) or + ( + (q1.sycl_context == q2.sycl_context) and + (q1.sycl_device == q2.sycl_device) and + (q1.is_in_order == q2.is_in_order) and + (q1.has_enable_profiling == q2.has_enable_profiling) + ) + ) + + +def get_execution_queue(qs): + """ Given a list of :class:`dpctl.SyclQueue` objects + returns the execution queue under compute follows data paradigm, + or returns `None` if queues are not equivalent. + """ + if not isinstance(qs, (list, tuple)): + raise TypeError( + "Expected a list or a tuple, got {}".format(type(qs)) + ) + if len(qs) == 0: + return None + elif len(qs) == 1: + return qs[0] if isinstance(qs[0], dpctl.SyclQueue) else None + for q1, q2 in zip(qs, qs[1:]): + if not isinstance(q1, dpctl.SyclQueue): + return None + elif not isinstance(q2, dpctl.SyclQueue): + return None + elif not queue_equiv( q1, q2): + return None + return qs[0] diff --git a/setup.py b/setup.py index 6d0be874ce..928a8e0010 100644 --- a/setup.py +++ b/setup.py @@ -214,6 +214,13 @@ def extensions(): ], **extension_args, ), + Extension( + "dpctl.utils._compute_follows_data", + [ + os.path.join("dpctl", "utils", "_compute_follows_data.pyx"), + ], + **extension_args, + ), Extension( "dpctl.tensor._usmarray", [