diff --git a/onedal/_device_offload.py b/onedal/_device_offload.py index 8d4a9d32d7..4e46592bb2 100644 --- a/onedal/_device_offload.py +++ b/onedal/_device_offload.py @@ -29,6 +29,14 @@ from dpctl import SyclQueue from dpctl.memory import MemoryUSMDevice, as_usm_memory from dpctl.tensor import usm_ndarray +else: + import onedal + + # setting fallback to `object` will make if isinstance call + # in _get_global_queue always true for situations without the + # dpc backend when `device_offload` is used. Instead, it will + # fail at the policy check phase yielding a RuntimeError + SyclQueue = getattr(onedal._backend, "SyclQueue", object) if dpnp_available: import dpnp @@ -36,30 +44,6 @@ from .utils._array_api import _convert_to_dpnp -class DummySyclQueue: - """This class is designed to act like dpctl.SyclQueue - to allow device dispatching in scenarios when dpctl is not available""" - - class DummySyclDevice: - def __init__(self, filter_string): - self._filter_string = filter_string - self.is_cpu = "cpu" in filter_string - self.is_gpu = "gpu" in filter_string - self.has_aspect_fp64 = self.is_cpu - - if not (self.is_cpu): - logging.warning( - "Device support is limited. " - "Please install dpctl for full experience" - ) - - def get_filter_string(self): - return self._filter_string - - def __init__(self, filter_string): - self.sycl_device = self.DummySyclDevice(filter_string) - - def _copy_to_usm(queue, array): if not dpctl_available: raise RuntimeError( @@ -139,12 +123,10 @@ def _transfer_to_host(queue, *data): def _get_global_queue(): target = _get_config()["target_offload"] - QueueClass = DummySyclQueue if not dpctl_available else SyclQueue - if target != "auto": - if isinstance(target, QueueClass): + if isinstance(target, SyclQueue): return target - return QueueClass(target) + return SyclQueue(target) return None diff --git a/onedal/common/_policy.py b/onedal/common/_policy.py index 90705854f6..0d7d8ca6a3 100644 --- a/onedal/common/_policy.py +++ b/onedal/common/_policy.py @@ -48,12 +48,8 @@ def __init__(self): if _is_dpc_backend: - from onedal._device_offload import DummySyclQueue class _DataParallelInteropPolicy(_backend.data_parallel_policy): def __init__(self, queue): self._queue = queue - if isinstance(queue, DummySyclQueue): - super().__init__(self._queue.sycl_device.get_filter_string()) - return super().__init__(self._queue) diff --git a/onedal/common/device_lookup.cpp b/onedal/common/device_lookup.cpp deleted file mode 100644 index 307cd2a01b..0000000000 --- a/onedal/common/device_lookup.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* -* Copyright 2024 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. -*******************************************************************************/ - -#include -#include -#include - -#include "oneapi/dal/detail/policy.hpp" - -#include "onedal/common/device_lookup.hpp" - -namespace oneapi::dal::python { - -#ifdef ONEDAL_DATA_PARALLEL - -const std::vector& get_devices() { - static const auto devices = sycl::device::get_devices(); - return devices; -} - -template -inline std::uint32_t get_id(Iter first, Iter it) { - const auto raw_id = std::distance(first, it); - return detail::integral_cast(raw_id); -} - -std::optional get_device_id(const sycl::device& device) { - const auto devices = get_devices(); - const auto first = devices.cbegin(); - const auto sentinel = devices.cend(); - auto iter = std::find(first, sentinel, device); - if (iter != sentinel) { - return get_id(first, iter); - } - else { - return {}; - } -} - -std::optional get_device_by_id(std::uint32_t device_id) { - auto casted = detail::integral_cast(device_id); - const auto devices = get_devices(); - if (casted < devices.size()) { - return devices.at(casted); - } - else { - return {}; - } -} - -#endif // ONEDAL_DATA_PARALLEL - -} // namespace oneapi::dal::python diff --git a/onedal/common/device_lookup.hpp b/onedal/common/device_lookup.hpp deleted file mode 100644 index baa3561f77..0000000000 --- a/onedal/common/device_lookup.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* -* Copyright 2024 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. -*******************************************************************************/ - -#include -#include - -#include "oneapi/dal/detail/policy.hpp" - -namespace oneapi::dal::python { - -#ifdef ONEDAL_DATA_PARALLEL - -std::optional get_device_by_id(std::uint32_t id); -std::optional get_device_id(const sycl::device& device); - -#endif // ONEDAL_DATA_PARALLEL - -} // namespace oneapi::dal::python diff --git a/onedal/common/policy.cpp b/onedal/common/policy.cpp index 9bf46e0909..44f77880d9 100644 --- a/onedal/common/policy.cpp +++ b/onedal/common/policy.cpp @@ -15,7 +15,7 @@ *******************************************************************************/ #include "oneapi/dal/detail/policy.hpp" -#include "onedal/common/policy_common.hpp" +#include "onedal/common/policy.hpp" #include "onedal/common/pybind11_helpers.hpp" namespace py = pybind11; @@ -41,12 +41,28 @@ void instantiate_default_host_policy(py::module& m) { #ifdef ONEDAL_DATA_PARALLEL -using data_parallel_policy_t = dal::detail::data_parallel_policy; +using dp_policy_t = dal::detail::data_parallel_policy; + +dp_policy_t make_dp_policy(std::uint32_t id) { + sycl::queue queue = get_queue_by_device_id(id); + return dp_policy_t{ std::move(queue) }; +} + +dp_policy_t make_dp_policy(const py::object& syclobj) { + sycl::queue queue = get_queue_from_python(syclobj); + return dp_policy_t{ std::move(queue) }; +} + +dp_policy_t make_dp_policy(const std::string& filter) { + sycl::queue queue = get_queue_by_filter_string(filter); + return dp_policy_t{ std::move(queue) }; +} void instantiate_data_parallel_policy(py::module& m) { constexpr const char name[] = "data_parallel_policy"; - py::class_ policy(m, name); - policy.def(py::init()); + py::class_ policy(m, name); + policy.def(py::init()); + policy.def(py::init()); policy.def(py::init([](std::uint32_t id) { return make_dp_policy(id); })); @@ -56,10 +72,10 @@ void instantiate_data_parallel_policy(py::module& m) { policy.def(py::init([](const py::object& syclobj) { return make_dp_policy(syclobj); })); - policy.def("get_device_id", [](const data_parallel_policy_t& policy) { + policy.def("get_device_id", [](const dp_policy_t& policy) { return get_device_id(policy); }); - policy.def("get_device_name", [](const data_parallel_policy_t& policy) { + policy.def("get_device_name", [](const dp_policy_t& policy) { return get_device_name(policy); }); m.def("get_used_memory", &get_used_memory, py::return_value_policy::take_ownership); diff --git a/onedal/common/policy.hpp b/onedal/common/policy.hpp new file mode 100644 index 0000000000..6d0b0ca6da --- /dev/null +++ b/onedal/common/policy.hpp @@ -0,0 +1,62 @@ +/******************************************************************************* +* Copyright 2024 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. +*******************************************************************************/ + +#pragma once + +#include +#include + +#ifdef ONEDAL_DATA_PARALLEL +#include +#endif // ONEDAL_DATA_PARALLEL + +#include + +#include "oneapi/dal/detail/policy.hpp" + +#include "onedal/common/sycl_interfaces.hpp" + +namespace py = pybind11; + +namespace oneapi::dal::python { + +#ifdef ONEDAL_DATA_PARALLEL + +using dp_policy_t = detail::data_parallel_policy; + +dp_policy_t make_dp_policy(std::uint32_t id); +dp_policy_t make_dp_policy(const py::object& syclobj); +dp_policy_t make_dp_policy(const std::string& filter); +inline dp_policy_t make_dp_policy(const dp_policy_t& policy) { + return dp_policy_t{ policy }; +} + +#endif // ONEDAL_DATA_PARALLEL + +template +inline auto& instantiate_host_policy(py::class_& policy) { + policy.def(py::init<>()); + policy.def(py::init()); + policy.def("get_device_id", [](const Policy&) -> std::uint32_t { + return std::uint32_t{ 0u }; + }); + policy.def("get_device_name", [](const Policy&) -> std::string { + return std::string{ "cpu" }; + }); + return policy; +} + +} // namespace oneapi::dal::python diff --git a/onedal/common/spmd_policy.cpp b/onedal/common/spmd_policy.cpp index 3c321af11d..95bcb6b47f 100644 --- a/onedal/common/spmd_policy.cpp +++ b/onedal/common/spmd_policy.cpp @@ -19,7 +19,7 @@ #include "oneapi/dal/detail/spmd_policy.hpp" #include "oneapi/dal/spmd/mpi/communicator.hpp" -#include "onedal/common/policy_common.hpp" +#include "onedal/common/policy.hpp" #include "onedal/common/pybind11_helpers.hpp" namespace py = pybind11; diff --git a/onedal/common/sycl.cpp b/onedal/common/sycl.cpp new file mode 100644 index 0000000000..f59515d166 --- /dev/null +++ b/onedal/common/sycl.cpp @@ -0,0 +1,81 @@ +/******************************************************************************* +* Copyright 2024 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. +*******************************************************************************/ + +#include "onedal/common/sycl_interfaces.hpp" +#include "onedal/common/pybind11_helpers.hpp" + +namespace py = pybind11; + +namespace oneapi::dal::python { + +#ifdef ONEDAL_DATA_PARALLEL + +void instantiate_sycl_interfaces(py::module& m){ + // These classes mirror a subset of functionality of the DPCtl python + // package's `SyclQueue` and `SyclDevice` objects. In the case that DPCtl + // is not installed, these classes will enable scikit-learn-intelex to still + // properly offload to other devices when built with the dpc backend. + py::class_ syclqueue(m, "SyclQueue"); + syclqueue.def(py::init()) + .def(py::init([](const std::string& filter) { + return get_queue_by_filter_string(filter); + }) + ) + .def(py::init([](const py::int_& obj) { + return get_queue_by_pylong_pointer(obj); + }) + ) + .def(py::init([](const py::object& syclobj) { + return get_queue_from_python(syclobj); + }) + ) + .def("_get_capsule",[](const sycl::queue& queue) { + return pack_queue(std::make_shared(queue)); + } + ) + .def_property_readonly("sycl_device", &sycl::queue::get_device); + + // expose limited sycl device features to python for oneDAL analysis + py::class_ sycldevice(m, "SyclDevice"); + sycldevice.def(py::init([](std::uint32_t id) { + return get_device_by_id(id).value(); + }) + ) + .def_property_readonly("has_aspect_fp64",[](const sycl::device& device) { + return device.has(sycl::aspect::fp64); + } + ) + .def_property_readonly("has_aspect_fp16",[](const sycl::device& device) { + return device.has(sycl::aspect::fp16); + } + ) + .def_property_readonly("filter_string",[](const sycl::device& device) { + // assumes we are not working with accelerators + std::string filter = get_device_name(device); + py::int_ id(get_device_id(device).value()); + return py::str(filter + ":") + py::str(id); + } + ) + .def_property_readonly("is_cpu", &sycl::device::is_cpu) + .def_property_readonly("is_gpu", &sycl::device::is_gpu); +} + +ONEDAL_PY_INIT_MODULE(sycl) { + instantiate_sycl_interfaces(m); +} +#endif + +} // namespace oneapi::dal::python diff --git a/onedal/common/policy_common.cpp b/onedal/common/sycl_interfaces.cpp similarity index 62% rename from onedal/common/policy_common.cpp rename to onedal/common/sycl_interfaces.cpp index bfb3c02cbd..2109312503 100644 --- a/onedal/common/policy_common.cpp +++ b/onedal/common/sycl_interfaces.cpp @@ -18,19 +18,56 @@ #include #endif // ONEDAL_DATA_PARALLEL +#include #include -#include "onedal/common/policy_common.hpp" +#include "onedal/common/sycl_interfaces.hpp" namespace oneapi::dal::python { #ifdef ONEDAL_DATA_PARALLEL constexpr const char unknown_device[] = "Unknown device"; -constexpr const char py_capsule_name[] = "PyCapsule"; constexpr const char get_capsule_name[] = "_get_capsule"; constexpr const char queue_capsule_name[] = "SyclQueueRef"; constexpr const char context_capsule_name[] = "SyclContextRef"; +constexpr const char device_name[] = "sycl_device"; +constexpr const char filter_name[] = "filter_string"; + +const std::vector& get_devices() { + static const auto devices = sycl::device::get_devices(); + return devices; +} + +template +inline std::uint32_t get_id(Iter first, Iter it) { + const auto raw_id = std::distance(first, it); + return detail::integral_cast(raw_id); +} + +std::optional get_device_id(const sycl::device& device) { + const auto devices = get_devices(); + const auto first = devices.cbegin(); + const auto sentinel = devices.cend(); + auto iter = std::find(first, sentinel, device); + if (iter != sentinel) { + return get_id(first, iter); + } + else { + return {}; + } +} + +std::optional get_device_by_id(std::uint32_t device_id) { + auto casted = detail::integral_cast(device_id); + const auto devices = get_devices(); + if (casted < devices.size()) { + return devices.at(casted); + } + else { + return {}; + } +} sycl::queue extract_queue(py::capsule capsule) { constexpr const char* gtr_name = queue_capsule_name; @@ -70,18 +107,14 @@ sycl::queue get_queue_by_get_capsule(const py::object& syclobj) { return extract_from_capsule(std::move(capsule)); } -sycl::queue get_queue_from_python(const py::object& syclobj) { - static auto pycapsule = py::cast(py_capsule_name); - if (py::hasattr(syclobj, get_capsule_name)) { - return get_queue_by_get_capsule(syclobj); - } - else if (py::isinstance(syclobj, pycapsule)) { - const auto caps = syclobj.cast(); - return extract_from_capsule(std::move(caps)); - } - else { - throw std::runtime_error("Unable to interpret \"syclobj\""); - } +sycl::queue get_queue_by_pylong_pointer(const py::int_& syclobj) { + // PyTorch XPU streams have a sycl_queue attribute which is + // a void pointer as PyLong (Python integer). It can be read and + // converted into a sycl::queue. This function allows + // consumption of these objects for use in oneDAL. + void *ptr = PyLong_AsVoidPtr(syclobj.ptr()); + // assumes that the PyLong is a pointer to a queue + return sycl::queue{ *static_cast(ptr) }; } sycl::queue get_queue_by_filter_string(const std::string& filter) { @@ -98,8 +131,29 @@ sycl::queue get_queue_by_device_id(std::uint32_t id) { } } -std::string get_device_name(const sycl::queue& queue) { - const auto& device = queue.get_device(); +sycl::queue get_queue_from_python(const py::object& syclobj) { + if (py::hasattr(syclobj, get_capsule_name)) { + return get_queue_by_get_capsule(syclobj); + } + else if (py::isinstance(syclobj)) { + const auto caps = syclobj.cast(); + return extract_from_capsule(std::move(caps)); + } + else if (py::hasattr(syclobj, device_name) && py::hasattr(syclobj.attr(device_name), filter_name)) { + auto attr = syclobj.attr(device_name).attr(filter_name); + return get_queue_by_filter_string(attr.cast()); + } + else + { + throw std::runtime_error("Unable to interpret \"syclobj\""); + } +} + +std::string get_device_name(const sycl::queue& queue){ + return get_device_name(queue.get_device()); +} + +std::string get_device_name(const sycl::device& device) { if (device.is_gpu()) { return { "gpu" }; } @@ -128,21 +182,6 @@ std::size_t get_used_memory(const py::object& syclobj){ return total_memory - free_memory; } -dp_policy_t make_dp_policy(std::uint32_t id) { - sycl::queue queue = get_queue_by_device_id(id); - return dp_policy_t{ std::move(queue) }; -} - -dp_policy_t make_dp_policy(const py::object& syclobj) { - sycl::queue queue = get_queue_from_python(syclobj); - return dp_policy_t{ std::move(queue) }; -} - -dp_policy_t make_dp_policy(const std::string& filter) { - sycl::queue queue = get_queue_by_filter_string(filter); - return dp_policy_t{ std::move(queue) }; -} - std::uint32_t get_device_id(const dp_policy_t& policy) { const auto& queue = policy.get_queue(); return get_device_id(queue); @@ -153,6 +192,27 @@ std::string get_device_name(const dp_policy_t& policy) { return get_device_name(queue); } +// Create `SyclQueueRef` PyCapsule that represents an opaque value of +// sycl::queue. +py::capsule pack_queue(const std::shared_ptr& queue) { + static const char queue_capsule_name[] = "SyclQueueRef"; + if (queue.get() == nullptr) { + throw std::runtime_error("Empty queue"); + } + else { + void (*deleter)(void*) = [](void* const queue) -> void { + delete reinterpret_cast(queue); + }; + + sycl::queue* ptr = new sycl::queue{ *queue }; + void* const raw = reinterpret_cast(ptr); + + py::capsule capsule(raw, deleter); + capsule.set_name(queue_capsule_name); + return capsule; + } +} + #endif // ONEDAL_DATA_PARALLEL } // namespace oneapi::dal::python diff --git a/onedal/common/policy_common.hpp b/onedal/common/sycl_interfaces.hpp similarity index 74% rename from onedal/common/policy_common.hpp rename to onedal/common/sycl_interfaces.hpp index 370adc8499..1b838254a4 100644 --- a/onedal/common/policy_common.hpp +++ b/onedal/common/sycl_interfaces.hpp @@ -18,6 +18,7 @@ #include #include +#include #ifdef ONEDAL_DATA_PARALLEL #include @@ -27,32 +28,32 @@ #include "oneapi/dal/detail/policy.hpp" -#include "onedal/common/device_lookup.hpp" - namespace py = pybind11; namespace oneapi::dal::python { #ifdef ONEDAL_DATA_PARALLEL +std::optional get_device_by_id(std::uint32_t id); +std::optional get_device_id(const sycl::device& device); + sycl::queue extract_queue(py::capsule capsule); sycl::context extract_context(py::capsule capsule); sycl::queue extract_from_capsule(py::capsule capsule); + sycl::queue get_queue_by_get_capsule(const py::object& syclobj); +sycl::queue get_queue_by_pylong_pointer(const py::int_& syclobj); +sycl::queue get_queue_by_filter_string(const std::string& filter); +sycl::queue get_queue_by_device_id(std::uint32_t id); sycl::queue get_queue_from_python(const py::object& syclobj); using dp_policy_t = detail::data_parallel_policy; -dp_policy_t make_dp_policy(std::uint32_t id); -dp_policy_t make_dp_policy(const py::object& syclobj); -dp_policy_t make_dp_policy(const std::string& filter); -inline dp_policy_t make_dp_policy(const dp_policy_t& policy) { - return dp_policy_t{ policy }; -} - std::uint32_t get_device_id(const dp_policy_t& policy); std::size_t get_used_memory(const py::object& syclobj); std::string get_device_name(const dp_policy_t& policy); +std::string get_device_name(const sycl::device& device); + /// TODO: This is a workaround class. /// It hides deprecated ``sycl::ext::oneapi::filter_selector`` to get rid of build warnings @@ -68,19 +69,8 @@ struct filter_selector_wrapper { sycl::ext::oneapi::filter_selector filter_selector_; }; -#endif // ONEDAL_DATA_PARALLEL +py::capsule pack_queue(const std::shared_ptr& queue); -template -inline auto& instantiate_host_policy(py::class_& policy) { - policy.def(py::init<>()); - policy.def(py::init()); - policy.def("get_device_id", [](const Policy&) -> std::uint32_t { - return std::uint32_t{ 0u }; - }); - policy.def("get_device_name", [](const Policy&) -> std::string { - return std::string{ "cpu" }; - }); - return policy; -} +#endif // ONEDAL_DATA_PARALLEL } // namespace oneapi::dal::python diff --git a/onedal/common/tests/test_sycl.py b/onedal/common/tests/test_sycl.py new file mode 100644 index 0000000000..cdd8cf54d2 --- /dev/null +++ b/onedal/common/tests/test_sycl.py @@ -0,0 +1,118 @@ +# ============================================================================== +# Copyright 2024 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. +# ============================================================================== + +import numpy as np +import pytest + +from onedal import _backend, _is_dpc_backend +from onedal.tests.utils._device_selection import get_queues +from onedal.utils._dpep_helpers import dpctl_available + + +@pytest.mark.skipif( + not _is_dpc_backend or not dpctl_available, reason="requires dpc backend and dpctl" +) +@pytest.mark.parametrize("device_type", ["cpu", "gpu"]) +@pytest.mark.parametrize("device_number", [None, 0, 1, 2, 3]) +def test_sycl_queue_string_creation(device_type, device_number): + # create devices from strings + from dpctl import SyclQueue + from dpctl._sycl_queue import SyclQueueCreationError + + onedal_SyclQueue = _backend.SyclQueue + + device = ":".join([device_type, str(device_number)]) if device_number else device_type + + raised_exception_dpctl = False + raised_exception_backend = False + + try: + dpctl_queue = SyclQueue(device) + except SyclQueueCreationError: + raised_exception_dpctl = True + + try: + onedal_queue = onedal_SyclQueue(device) + except RuntimeError: + raised_exception_backend = True + + assert raised_exception_dpctl == raised_exception_backend + # get_device_id must be modified to follow DPCtl conventions + # this causes filter_string mismatches + # if not raised_exception_backend: + # assert ( + # onedal_queue.sycl_device.filter_string + # in dpctl_queue.sycl_device.filter_string + # ) + + +@pytest.mark.skipif( + not _is_dpc_backend or not dpctl_available, reason="requires dpc backend and dpctl" +) +@pytest.mark.parametrize("queue", get_queues()) +def test_sycl_queue_conversion(queue): + if queue is None: + pytest.skip("Not a DPCtl queue") + SyclQueue = queue.__class__ + onedal_SyclQueue = _backend.SyclQueue + # convert back and forth to test `_get_capsule` attribute + + q = onedal_SyclQueue(queue) + # get_device_id must be modified to follow DPCtl conventions + # assert q.sycl_device.filter_string in queue.sycl_device.filter_string + + +@pytest.mark.skipif( + not _is_dpc_backend or not dpctl_available, reason="requires dpc backend and dpctl" +) +@pytest.mark.parametrize("queue", get_queues()) +def test_sycl_device_attributes(queue): + from dpctl import SyclQueue + + if queue is None: + pytest.skip("Not a DPCtl queue") + onedal_SyclQueue = _backend.SyclQueue + + onedal_queue = onedal_SyclQueue(queue) + + # check fp64 support + assert onedal_queue.sycl_device.has_aspect_fp64 == queue.sycl_device.has_aspect_fp64 + # check fp16 support + assert onedal_queue.sycl_device.has_aspect_fp16 == queue.sycl_device.has_aspect_fp16 + # check is_cpu + assert onedal_queue.sycl_device.is_cpu == queue.sycl_device.is_cpu + # check is_gpu + assert onedal_queue.sycl_device.is_gpu == queue.sycl_device.is_gpu + # check device number + # get_device_id must be modified to follow DPCtl conventions + # assert onedal_queue.sycl_device.filter_string in queue.sycl_device.filter_string + + +@pytest.mark.skipif(not _is_dpc_backend, reason="requires dpc backend") +def test_backend_queue(): + q = _backend.SyclQueue("cpu") + # verify copying via a py capsule object is functional + q2 = _backend.SyclQueue(q._get_capsule()) + # verify copying via the _get_capsule attribute + q3 = _backend.SyclQueue(q) + + q_array = [q, q2, q3] + + assert all([queue.sycl_device.has_aspect_fp64 for queue in q_array]) + assert all([queue.sycl_device.has_aspect_fp16 for queue in q_array]) + assert all([queue.sycl_device.is_cpu for queue in q_array]) + assert all([not queue.sycl_device.is_gpu for queue in q_array]) + assert all(["cpu" in queue.sycl_device.filter_string for queue in q_array]) diff --git a/onedal/dal.cpp b/onedal/dal.cpp index 814b22aa8b..5c63b1c225 100644 --- a/onedal/dal.cpp +++ b/onedal/dal.cpp @@ -44,6 +44,10 @@ namespace oneapi::dal::python { ONEDAL_PY_INIT_MODULE(logistic_regression); #endif // defined(ONEDAL_VERSION) && ONEDAL_VERSION >= 20240001 #else // ONEDAL_DATA_PARALLEL_SPMD + #ifdef ONEDAL_DATA_PARALLEL + ONEDAL_PY_INIT_MODULE(sycl); + #endif // ONEDAL_DATA_PARALLEL + ONEDAL_PY_INIT_MODULE(policy); /* datatypes*/ ONEDAL_PY_INIT_MODULE(table); @@ -102,6 +106,7 @@ namespace oneapi::dal::python { #else #ifdef ONEDAL_DATA_PARALLEL PYBIND11_MODULE(_onedal_py_dpc, m) { + init_sycl(m); #else PYBIND11_MODULE(_onedal_py_host, m) { #endif diff --git a/onedal/datatypes/data_conversion_sua_iface.cpp b/onedal/datatypes/data_conversion_sua_iface.cpp index 4a83ad6fdc..06b492e7b5 100644 --- a/onedal/datatypes/data_conversion_sua_iface.cpp +++ b/onedal/datatypes/data_conversion_sua_iface.cpp @@ -26,7 +26,7 @@ #include "oneapi/dal/table/homogen.hpp" #include "oneapi/dal/table/detail/homogen_utils.hpp" -#include "onedal/common/policy_common.hpp" +#include "onedal/common/sycl_interfaces.hpp" #include "onedal/datatypes/data_conversion_sua_iface.hpp" #include "onedal/datatypes/utils/dtype_conversions.hpp" #include "onedal/datatypes/utils/dtype_dispatcher.hpp" diff --git a/onedal/datatypes/utils/sua_iface_helpers.cpp b/onedal/datatypes/utils/sua_iface_helpers.cpp index 60407f5a4b..a5f0567534 100644 --- a/onedal/datatypes/utils/sua_iface_helpers.cpp +++ b/onedal/datatypes/utils/sua_iface_helpers.cpp @@ -26,7 +26,7 @@ #include "oneapi/dal/table/homogen.hpp" #include "oneapi/dal/table/detail/homogen_utils.hpp" -#include "onedal/common/policy_common.hpp" +#include "onedal/common/sycl_interfaces.hpp" #include "onedal/datatypes/data_conversion_sua_iface.hpp" #include "onedal/datatypes/utils/dtype_conversions.hpp" #include "onedal/datatypes/utils/dtype_dispatcher.hpp" @@ -197,27 +197,6 @@ py::tuple get_npy_strides(const dal::data_layout& data_layout, return strides; } -// Create `SyclQueueRef` PyCapsule that represents an opaque value of -// sycl::queue. -py::capsule pack_queue(const std::shared_ptr& queue) { - static const char queue_capsule_name[] = "SyclQueueRef"; - if (queue.get() == nullptr) { - throw std::runtime_error("Empty queue"); - } - else { - void (*deleter)(void*) = [](void* const queue) -> void { - delete reinterpret_cast(queue); - }; - - sycl::queue* ptr = new sycl::queue{ *queue }; - void* const raw = reinterpret_cast(ptr); - - py::capsule capsule(raw, deleter); - capsule.set_name(queue_capsule_name); - return capsule; - } -} - } // namespace oneapi::dal::python #endif // ONEDAL_DATA_PARALLEL diff --git a/onedal/datatypes/utils/sua_iface_helpers.hpp b/onedal/datatypes/utils/sua_iface_helpers.hpp index 494ce769e2..050dad036f 100644 --- a/onedal/datatypes/utils/sua_iface_helpers.hpp +++ b/onedal/datatypes/utils/sua_iface_helpers.hpp @@ -26,7 +26,7 @@ #include "oneapi/dal/table/homogen.hpp" #include "oneapi/dal/table/detail/homogen_utils.hpp" -#include "onedal/common/policy_common.hpp" +#include "onedal/common/sycl_interfaces.hpp" #include "onedal/datatypes/data_conversion_sua_iface.hpp" #include "onedal/datatypes/utils/dtype_conversions.hpp" #include "onedal/datatypes/utils/dtype_dispatcher.hpp" @@ -62,8 +62,6 @@ py::tuple get_npy_strides(const dal::data_layout& data_layout, npy_intp row_count, npy_intp column_count); -py::capsule pack_queue(const std::shared_ptr& queue); - } // namespace oneapi::dal::python #endif // ONEDAL_DATA_PARALLEL