From 6e4d9bf9237bd891ebd9c00d9a5f4a25f723d45d Mon Sep 17 00:00:00 2001 From: Artur Gainullin Date: Wed, 14 Oct 2026 13:52:11 -0700 Subject: [PATCH 1/4] [SYCL] Implement sycl_ext_oneapi_queue_priority revision 2 Add the sycl::ext::oneapi::property::queue::priority property and the sycl::ext::oneapi::info::device::priority_range descriptor. Numeric priorities are clamped to the device range and passed to UR via ur_exp_queue_priority_properties_t. The pre-defined priorities keep using the UR priority flags, which adapters map to the ends of the priority range. Devices without priority levels report {0, 0}. Also fix queue property validation accepting dataless deprecated aliases as properties with data, which made their kinds collide. Assisted-By: Claude --- sycl/include/sycl/detail/property_helper.hpp | 3 +- sycl/include/sycl/ext/oneapi/info/device.hpp | 6 + .../sycl/properties/queue_properties.def | 1 + .../sycl/properties/queue_properties.hpp | 12 ++ sycl/source/detail/device_impl.hpp | 9 + sycl/source/detail/queue_impl.cpp | 14 +- sycl/source/detail/queue_impl.hpp | 51 +++-- .../detail/ur_device_info_ret_types.inc | 3 + sycl/source/device.cpp | 3 + sycl/source/feature_test.hpp.in | 2 +- sycl/test-e2e/Basic/queue_priority_levels.cpp | 72 +++++++ sycl/test/abi/sycl_symbols_linux.dump | 1 + sycl/test/abi/sycl_symbols_windows.dump | 1 + sycl/unittests/queue/CMakeLists.txt | 1 + sycl/unittests/queue/Priority.cpp | 185 ++++++++++++++++++ 15 files changed, 342 insertions(+), 22 deletions(-) create mode 100644 sycl/test-e2e/Basic/queue_priority_levels.cpp create mode 100644 sycl/unittests/queue/Priority.cpp diff --git a/sycl/include/sycl/detail/property_helper.hpp b/sycl/include/sycl/detail/property_helper.hpp index 12837c482fbbd..0799280632841 100644 --- a/sycl/include/sycl/detail/property_helper.hpp +++ b/sycl/include/sycl/detail/property_helper.hpp @@ -61,7 +61,8 @@ enum PropWithDataKind { AccPropBufferLocation = 5, QueueComputeIndex = 6, GraphNodeDependencies = 7, - PropWithDataKindSize = 8 + QueuePriority = 8, + PropWithDataKindSize = 9 }; // Base class for dataless properties, needed to check that the type of an diff --git a/sycl/include/sycl/ext/oneapi/info/device.hpp b/sycl/include/sycl/ext/oneapi/info/device.hpp index d4c6660482510..f51d07e5d04e8 100644 --- a/sycl/include/sycl/ext/oneapi/info/device.hpp +++ b/sycl/include/sycl/ext/oneapi/info/device.hpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace sycl { @@ -23,6 +24,11 @@ struct num_compute_units using return_type = size_t; }; +struct priority_range + : sycl::detail::rt_traits_base { + using return_type = std::array; +}; + } // namespace ext::oneapi::info::device } // namespace _V1 } // namespace sycl diff --git a/sycl/include/sycl/properties/queue_properties.def b/sycl/include/sycl/properties/queue_properties.def index 34235f151b6eb..6cdd71ffb1907 100644 --- a/sycl/include/sycl/properties/queue_properties.def +++ b/sycl/include/sycl/properties/queue_properties.def @@ -31,6 +31,7 @@ __SYCL_MANUALLY_DEFINED_PROP(property::queue::cuda, use_default_stream) // Contains data field, defined explicitly. __SYCL_MANUALLY_DEFINED_PROP(ext::intel::property::queue, compute_index) +__SYCL_MANUALLY_DEFINED_PROP(ext::oneapi::property::queue, priority) #ifndef __INTEL_PREVIEW_BREAKING_CHANGES /// Inform the implementation that the application will not use events returned diff --git a/sycl/include/sycl/properties/queue_properties.hpp b/sycl/include/sycl/properties/queue_properties.hpp index cb20761069376..dc135e1367941 100644 --- a/sycl/include/sycl/properties/queue_properties.hpp +++ b/sycl/include/sycl/properties/queue_properties.hpp @@ -50,6 +50,18 @@ class compute_index : public sycl::detail::PropertyWithData< }; } // namespace ext::intel::property::queue +namespace ext::oneapi::property::queue { +class priority : public sycl::detail::PropertyWithData< + sycl::detail::PropWithDataKind::QueuePriority> { +public: + priority(int priority_value) : value(priority_value) {} + int get_priority() const { return value; } + +private: + int value; +}; +} // namespace ext::oneapi::property::queue + // Queue property trait specializations. class queue; diff --git a/sycl/source/detail/device_impl.hpp b/sycl/source/detail/device_impl.hpp index b966ab113782c..f5d7efe71dc3f 100644 --- a/sycl/source/detail/device_impl.hpp +++ b/sycl/source/detail/device_impl.hpp @@ -1011,6 +1011,15 @@ class device_impl { return static_cast( get_info_impl()); } + CASE(ext::oneapi::info::device::priority_range) { + // Devices without priority levels only support the default priority. + if (!get_info_impl_nocheck() + .value_or(false)) + return std::array{0, 0}; + return std::array{ + get_info_impl(), + get_info_impl()}; + } // ext::intel device traits (defined under sycl/ext/intel/info/device.hpp). diff --git a/sycl/source/detail/queue_impl.cpp b/sycl/source/detail/queue_impl.cpp index 1983fe81ab310..3917e69a10154 100644 --- a/sycl/source/detail/queue_impl.cpp +++ b/sycl/source/detail/queue_impl.cpp @@ -1649,15 +1649,17 @@ void queue_impl::verifyProps(const property_list &Props) const { return false; } }; + // Manually defined properties also include dataless deprecated aliases, + // whose kinds overlap with the kinds of properties with data. auto CheckPropertiesWithData = [](int PropertyKind) { #define __SYCL_MANUALLY_DEFINED_PROP(NS_QUALIFIER, PROP_NAME) \ - case NS_QUALIFIER::PROP_NAME::getKind(): \ - return true; - switch (PropertyKind) { + if constexpr (std::is_base_of_v) { \ + if (PropertyKind == NS_QUALIFIER::PROP_NAME::getKind()) \ + return true; \ + } #include - default: - return false; - } + return false; }; detail::PropertyValidator::checkPropsAndThrow(Props, CheckDataLessProperties, CheckPropertiesWithData); diff --git a/sycl/source/detail/queue_impl.hpp b/sycl/source/detail/queue_impl.hpp index ac5e0d28a93bf..00132123a6d1a 100644 --- a/sycl/source/detail/queue_impl.hpp +++ b/sycl/source/detail/queue_impl.hpp @@ -31,8 +31,10 @@ #include #include +#include #include #include +#include #include #ifdef XPTI_ENABLE_INSTRUMENTATION @@ -541,26 +543,25 @@ class queue_impl : public std::enable_shared_from_this { } // Track that priority settings are not ambiguous. bool PrioritySeen = false; - if (PropList - .has_property()) { - // Normal is the default priority, don't pass anything. - PrioritySeen = true; - } - if (PropList.has_property()) { - if (PrioritySeen) { + auto CheckPriority = [&PrioritySeen]() { + if (PrioritySeen) throw sycl::exception( make_error_code(errc::invalid), "Queue cannot be constructed with different priorities."); - } - CreationFlags |= UR_QUEUE_FLAG_PRIORITY_LOW; PrioritySeen = true; + }; + // The numeric priority is passed separately, see getPriorityLevel(). + if (PropList.has_property()) + CheckPriority(); + // Normal is the default priority, don't pass anything. + if (PropList.has_property()) + CheckPriority(); + if (PropList.has_property()) { + CheckPriority(); + CreationFlags |= UR_QUEUE_FLAG_PRIORITY_LOW; } if (PropList.has_property()) { - if (PrioritySeen) { - throw sycl::exception( - make_error_code(errc::invalid), - "Queue cannot be constructed with different priorities."); - } + CheckPriority(); CreationFlags |= UR_QUEUE_FLAG_PRIORITY_HIGH; } { @@ -582,6 +583,21 @@ class queue_impl : public std::enable_shared_from_this { return CreationFlags; } + /// \return the numeric priority to pass to UR, or std::nullopt if the queue + /// uses the default priority. + std::optional getPriorityLevel() const { + namespace prop = ext::oneapi::property::queue; + if (!has_property()) + return std::nullopt; + + // {0, 0} if the device doesn't support priority levels. + auto [Lowest, Highest] = + MDevice.get_info(); + int Level = std::clamp(get_property().get_priority(), + Highest, Lowest); + return Level == 0 ? std::nullopt : std::optional{Level}; + } + /// Creates UR queue. /// /// \param Order specifies whether the queue being constructed as in-order @@ -601,6 +617,13 @@ class queue_impl : public std::enable_shared_from_this { .get_index(); Properties.pNext = &IndexProperties; } + ur_exp_queue_priority_properties_t PriorityProperties = { + UR_STRUCTURE_TYPE_EXP_QUEUE_PRIORITY_PROPERTIES, nullptr, 0}; + if (std::optional Level = getPriorityLevel()) { + PriorityProperties.priority = *Level; + PriorityProperties.pNext = Properties.pNext; + Properties.pNext = &PriorityProperties; + } getAdapter().call(Context, Device, &Properties, &Queue); diff --git a/sycl/source/detail/ur_device_info_ret_types.inc b/sycl/source/detail/ur_device_info_ret_types.inc index 10c63f1e4dcee..553d989b7f17d 100644 --- a/sycl/source/detail/ur_device_info_ret_types.inc +++ b/sycl/source/detail/ur_device_info_ret_types.inc @@ -214,4 +214,7 @@ MAP(UR_DEVICE_INFO_DEVICE_WAIT_SUPPORT_EXP, ur_bool_t) MAP(UR_DEVICE_INFO_IPC_PHYSICAL_MEMORY_SUPPORT_EXP, ur_bool_t) MAP(UR_DEVICE_INFO_USM_HOST_ALLOC_REGISTER_SUPPORT_EXP, ur_bool_t) MAP(UR_DEVICE_INFO_PER_EVENT_PROFILING_SUPPORT_EXP, ur_bool_t) +MAP(UR_DEVICE_INFO_QUEUE_PRIORITY_SUPPORT_EXP, ur_bool_t) +MAP(UR_DEVICE_INFO_QUEUE_PRIORITY_HIGHEST_EXP, int32_t) +MAP(UR_DEVICE_INFO_QUEUE_PRIORITY_LOWEST_EXP, int32_t) // clang-format on diff --git a/sycl/source/device.cpp b/sycl/source/device.cpp index 9f899a8d2a000..55ca1d4c2aa99 100644 --- a/sycl/source/device.cpp +++ b/sycl/source/device.cpp @@ -396,6 +396,9 @@ __SYCL_ONEAPI_DEVICE_INST(ext::oneapi::experimental::info::device, __SYCL_ONEAPI_DEVICE_INST(ext::oneapi::experimental::info::device, composite_device, sycl::device) __SYCL_ONEAPI_DEVICE_INST(ext::oneapi::info::device, num_compute_units, size_t) +__SYCL_ONEAPI_DEVICE_INST( + ext::oneapi::info::device, priority_range, + ext::oneapi::info::device::priority_range::return_type) #undef __SYCL_ONEAPI_DEVICE_INST #define __SYCL_KHR_DEVICE_INST(NS, NAME, RETURN_T) \ diff --git a/sycl/source/feature_test.hpp.in b/sycl/source/feature_test.hpp.in index 298f86f1e6196..b349de9457e0c 100644 --- a/sycl/source/feature_test.hpp.in +++ b/sycl/source/feature_test.hpp.in @@ -41,7 +41,7 @@ inline namespace _V1 { // builds. #define SYCL_EXT_ONEAPI_DISCARD_QUEUE_EVENTS 1 #endif // __INTEL_PREVIEW_BREAKING_CHANGES -#define SYCL_EXT_ONEAPI_QUEUE_PRIORITY 1 +#define SYCL_EXT_ONEAPI_QUEUE_PRIORITY 2 #define SYCL_EXT_ONEAPI_ENQUEUE_BARRIER 1 #define SYCL_EXT_ONEAPI_FREE_FUNCTION_QUERIES 1 #define SYCL_EXT_ONEAPI_BINDLESS_IMAGES 6 diff --git a/sycl/test-e2e/Basic/queue_priority_levels.cpp b/sycl/test-e2e/Basic/queue_priority_levels.cpp new file mode 100644 index 0000000000000..f6d237b87c75a --- /dev/null +++ b/sycl/test-e2e/Basic/queue_priority_levels.cpp @@ -0,0 +1,72 @@ +// RUN: %{build} -o %t.out +// RUN: %{run} %t.out + +// Checks sycl::ext::oneapi::property::queue::priority and the priority_range +// device descriptor. + +#include +#include +#include + +#include +#include +#include + +namespace prop = sycl::ext::oneapi::property::queue; + +static_assert(SYCL_EXT_ONEAPI_QUEUE_PRIORITY >= 2); + +static void checkQueue(const sycl::device &Dev, + const sycl::property_list &Props) { + sycl::queue Q{Dev, Props}; + constexpr size_t N = 64; + std::vector Data(N, -1); + { + sycl::buffer Buf{Data.data(), sycl::range<1>{N}}; + Q.submit([&](sycl::handler &CGH) { + sycl::accessor Acc{Buf, CGH, sycl::write_only}; + CGH.parallel_for(sycl::range<1>{N}, + [=](sycl::id<1> I) { Acc[I] = static_cast(I[0]); }); + }); + } + for (size_t I = 0; I < N; ++I) + assert(Data[I] == static_cast(I)); +} + +static void checkThrows(const sycl::device &Dev, + const sycl::property_list &Props) { + try { + sycl::queue Q{Dev, Props}; + } catch (sycl::exception &E) { + assert(E.code() == sycl::errc::invalid); + return; + } + assert(false && "Expected an exception"); +} + +int main() { + sycl::device Dev; + auto [Lowest, Highest] = + Dev.get_info(); + std::cout << "priority range: lowest=" << Lowest << " highest=" << Highest + << std::endl; + assert(Highest <= 0 && Lowest >= 0); + + // Values outside of the range are clamped. + for (int P = Highest - 1; P <= Lowest + 1; ++P) { + checkQueue(Dev, {prop::priority{P}}); + checkQueue(Dev, {prop::priority{P}, sycl::property::queue::in_order{}}); + } + checkQueue(Dev, {prop::priority_normal{}}); + checkQueue(Dev, {prop::priority_low{}}); + checkQueue(Dev, {prop::priority_high{}}); + + checkThrows(Dev, {prop::priority{0}, prop::priority_normal{}}); + checkThrows(Dev, {prop::priority{Highest}, prop::priority_high{}}); + checkThrows(Dev, {prop::priority{Lowest}, prop::priority_low{}}); + + sycl::queue Q{Dev, {prop::priority{Highest}}}; + assert(Q.has_property()); + assert(Q.get_property().get_priority() == Highest); + return 0; +} diff --git a/sycl/test/abi/sycl_symbols_linux.dump b/sycl/test/abi/sycl_symbols_linux.dump index 90f8c64e451d0..c7e8163f41f10 100644 --- a/sycl/test/abi/sycl_symbols_linux.dump +++ b/sycl/test/abi/sycl_symbols_linux.dump @@ -3852,6 +3852,7 @@ _ZNK4sycl3_V16device13get_info_implINS0_3ext6oneapi12experimental4info6device31w _ZNK4sycl3_V16device13get_info_implINS0_3ext6oneapi12experimental4info6device31work_item_progress_capabilitiesILNS5_15execution_scopeE2EEEEENS0_6detail11ABINeutralTINSB_19is_device_info_descIT_E11return_typeEE4typeEv _ZNK4sycl3_V16device13get_info_implINS0_3ext6oneapi12experimental4info6device31work_item_progress_capabilitiesILNS5_15execution_scopeE3EEEEENS0_6detail11ABINeutralTINSB_19is_device_info_descIT_E11return_typeEE4typeEv _ZNK4sycl3_V16device13get_info_implINS0_3ext6oneapi12experimental4info6device32work_group_progress_capabilitiesILNS5_15execution_scopeE3EEEEENS0_6detail11ABINeutralTINSB_19is_device_info_descIT_E11return_typeEE4typeEv +_ZNK4sycl3_V16device13get_info_implINS0_3ext6oneapi4info6device14priority_rangeEEENS0_6detail11ABINeutralTINS8_19is_device_info_descIT_E11return_typeEE4typeEv _ZNK4sycl3_V16device13get_info_implINS0_3ext6oneapi4info6device17num_compute_unitsEEENS0_6detail11ABINeutralTINS8_19is_device_info_descIT_E11return_typeEE4typeEv _ZNK4sycl3_V16device13get_info_implINS0_3ext8codeplay12experimental4info6device28max_registers_per_work_groupEEENS0_6detail11ABINeutralTINS9_19is_device_info_descIT_E11return_typeEE4typeEv _ZNK4sycl3_V16device13get_info_implINS0_3khr4info6device20max_work_group_rangeILi1EEEEENS0_6detail11ABINeutralTINS8_19is_device_info_descIT_E11return_typeEE4typeEv diff --git a/sycl/test/abi/sycl_symbols_windows.dump b/sycl/test/abi/sycl_symbols_windows.dump index e4674b15c4575..7604b27d167c0 100644 --- a/sycl/test/abi/sycl_symbols_windows.dump +++ b/sycl/test/abi/sycl_symbols_windows.dump @@ -197,6 +197,7 @@ ??$get_info_impl@Upreferred_vector_width_long_long@device@info@_V1@sycl@@@device@_V1@sycl@@AEBAIXZ ??$get_info_impl@Upreferred_vector_width_short@device@info@_V1@sycl@@@device@_V1@sycl@@AEBAIXZ ??$get_info_impl@Uprintf_buffer_size@device@info@_V1@sycl@@@device@_V1@sycl@@AEBA_KXZ +??$get_info_impl@Upriority_range@device@info@oneapi@ext@_V1@sycl@@@device@_V1@sycl@@AEBA?AV?$array@H$01@std@@XZ ??$get_info_impl@Uprofile@device@info@_V1@sycl@@@device@_V1@sycl@@AEBA?AVstring@detail@12@XZ ??$get_info_impl@Uprofile@platform@info@_V1@sycl@@@platform@_V1@sycl@@AEBA?AVstring@detail@12@XZ ??$get_info_impl@Uprofiling_timer_resolution@device@info@_V1@sycl@@@device@_V1@sycl@@AEBA_KXZ diff --git a/sycl/unittests/queue/CMakeLists.txt b/sycl/unittests/queue/CMakeLists.txt index 1045a1865cb29..e1360aaa36c3a 100644 --- a/sycl/unittests/queue/CMakeLists.txt +++ b/sycl/unittests/queue/CMakeLists.txt @@ -7,6 +7,7 @@ add_sycl_unittest(QueueTests OBJECT ShortcutFunctions.cpp InOrderQueue.cpp InteropRetain.cpp + Priority.cpp Properties.cpp Barrier.cpp ) diff --git a/sycl/unittests/queue/Priority.cpp b/sycl/unittests/queue/Priority.cpp new file mode 100644 index 0000000000000..f542bc616f331 --- /dev/null +++ b/sycl/unittests/queue/Priority.cpp @@ -0,0 +1,185 @@ +//==---------- Priority.cpp --- check queue priority handling in RT --------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +#include + +namespace prop = sycl::ext::oneapi::property::queue; + +namespace { + +bool PrioritySupported = false; +constexpr int32_t HighestPriority = -3; +constexpr int32_t LowestPriority = 2; + +ur_queue_flags_t CreatedFlags = 0; +std::optional CreatedPriority; +bool CreatedWithIndex = false; + +template ur_result_t returnValue(void *pParams, T Value) { + auto *Params = static_cast(pParams); + if (*Params->ppPropValue) + *static_cast(*Params->ppPropValue) = Value; + if (*Params->ppPropSizeRet) + **Params->ppPropSizeRet = sizeof(T); + return UR_RESULT_SUCCESS; +} + +ur_result_t urDeviceGetInfoAfter(void *pParams) { + auto *Params = static_cast(pParams); + switch (*Params->ppropName) { + case UR_DEVICE_INFO_QUEUE_PRIORITY_SUPPORT_EXP: + return returnValue(pParams, PrioritySupported); + case UR_DEVICE_INFO_QUEUE_PRIORITY_HIGHEST_EXP: + return PrioritySupported ? returnValue(pParams, HighestPriority) + : UR_RESULT_ERROR_UNSUPPORTED_ENUMERATION; + case UR_DEVICE_INFO_QUEUE_PRIORITY_LOWEST_EXP: + return PrioritySupported ? returnValue(pParams, LowestPriority) + : UR_RESULT_ERROR_UNSUPPORTED_ENUMERATION; + case UR_DEVICE_INFO_MAX_COMPUTE_QUEUE_INDICES: + return returnValue(pParams, 8); + default: + return UR_RESULT_SUCCESS; + } +} + +ur_result_t urQueueCreateBefore(void *pParams) { + auto *Params = static_cast(pParams); + const ur_queue_properties_t *Props = *Params->ppProperties; + CreatedFlags = Props ? Props->flags : 0; + CreatedPriority.reset(); + CreatedWithIndex = false; + for (auto *Next = + Props ? static_cast(Props->pNext) + : nullptr; + Next; Next = static_cast(Next->pNext)) { + if (Next->stype == UR_STRUCTURE_TYPE_EXP_QUEUE_PRIORITY_PROPERTIES) + CreatedPriority = + reinterpret_cast(Next) + ->priority; + else if (Next->stype == UR_STRUCTURE_TYPE_QUEUE_INDEX_PROPERTIES) + CreatedWithIndex = true; + } + return UR_RESULT_SUCCESS; +} + +class QueuePriorityTest : public ::testing::TestWithParam { +protected: + void SetUp() override { + PrioritySupported = GetParam(); + mock::getCallbacks().set_after_callback("urDeviceGetInfo", + &urDeviceGetInfoAfter); + mock::getCallbacks().set_before_callback("urQueueCreate", + &urQueueCreateBefore); + } + + void expectCreatedWith(std::optional Priority, + ur_queue_flags_t PriorityFlags) { + EXPECT_EQ(CreatedPriority, Priority); + EXPECT_EQ(CreatedFlags & + (UR_QUEUE_FLAG_PRIORITY_LOW | UR_QUEUE_FLAG_PRIORITY_HIGH), + PriorityFlags); + } + + sycl::unittest::UrMock<> Mock; +}; + +} // namespace + +TEST_P(QueuePriorityTest, PriorityRange) { + sycl::device Dev; + auto Range = Dev.get_info(); + if (PrioritySupported) { + EXPECT_EQ(Range[0], LowestPriority); + EXPECT_EQ(Range[1], HighestPriority); + } else { + EXPECT_EQ(Range[0], 0); + EXPECT_EQ(Range[1], 0); + } +} + +TEST_P(QueuePriorityTest, NumericPriority) { + sycl::queue Q{prop::priority{-2}}; + ASSERT_TRUE(Q.has_property()); + EXPECT_EQ(Q.get_property().get_priority(), -2); + // Without priority levels the range is {0, 0}, so -2 clamps to default. + expectCreatedWith( + PrioritySupported ? std::optional{-2} : std::nullopt, 0); +} + +TEST_P(QueuePriorityTest, NumericPriorityIsClamped) { + { + sycl::queue Q{prop::priority{-100}}; + expectCreatedWith(PrioritySupported + ? std::optional{HighestPriority} + : std::nullopt, + 0); + } + { + sycl::queue Q{prop::priority{100}}; + expectCreatedWith(PrioritySupported ? std::optional{LowestPriority} + : std::nullopt, + 0); + } +} + +TEST_P(QueuePriorityTest, DefaultPriorityIsNotPassed) { + { + sycl::queue Q{prop::priority{0}}; + expectCreatedWith(std::nullopt, 0); + } + { + sycl::queue Q{prop::priority_normal{}}; + expectCreatedWith(std::nullopt, 0); + } +} + +// The pre-defined priorities use the UR priority flags, which adapters map to +// the ends of the priority range. +TEST_P(QueuePriorityTest, PriorityHighUsesFlag) { + sycl::queue Q{prop::priority_high{}}; + expectCreatedWith(std::nullopt, UR_QUEUE_FLAG_PRIORITY_HIGH); +} + +TEST_P(QueuePriorityTest, PriorityLowUsesFlag) { + sycl::queue Q{prop::priority_low{}}; + expectCreatedWith(std::nullopt, UR_QUEUE_FLAG_PRIORITY_LOW); +} + +TEST_P(QueuePriorityTest, WithComputeIndex) { + sycl::queue Q{{prop::priority{-1}, + sycl::ext::intel::property::queue::compute_index{1}}}; + EXPECT_TRUE(CreatedWithIndex); + expectCreatedWith( + PrioritySupported ? std::optional{-1} : std::nullopt, 0); +} + +TEST_P(QueuePriorityTest, ConflictingPriorities) { + auto ExpectInvalid = [](sycl::property_list Props) { + try { + sycl::queue Q{Props}; + FAIL() << "Expected an exception"; + } catch (sycl::exception &E) { + EXPECT_EQ(E.code(), sycl::errc::invalid); + } + }; + ExpectInvalid({prop::priority{1}, prop::priority_normal{}}); + ExpectInvalid({prop::priority{1}, prop::priority_low{}}); + ExpectInvalid({prop::priority{1}, prop::priority_high{}}); + ExpectInvalid({prop::priority_low{}, prop::priority_high{}}); +} + +INSTANTIATE_TEST_SUITE_P(QueuePriority, QueuePriorityTest, ::testing::Bool(), + [](const ::testing::TestParamInfo &Info) { + return Info.param ? "LevelsSupported" + : "LevelsUnsupported"; + }); From 42028daf21a5e639d03c79942ced74dff6dd4cfe Mon Sep 17 00:00:00 2001 From: Artur Gainullin Date: Wed, 14 Oct 2026 13:52:21 -0700 Subject: [PATCH 2/4] [SYCL][Doc] Move sycl_ext_oneapi_queue_priority revision 2 to supported Replace the supported extension document with the proposed revision 2, which is now implemented, and remove the proposed document. Only the status section and the copyright years are changed. Assisted-By: Claude --- .../sycl_ext_oneapi_queue_priority.asciidoc | 186 ------------------ .../sycl_ext_oneapi_queue_priority.asciidoc | 152 ++++++++++---- 2 files changed, 115 insertions(+), 223 deletions(-) delete mode 100644 sycl/doc/extensions/proposed/sycl_ext_oneapi_queue_priority.asciidoc diff --git a/sycl/doc/extensions/proposed/sycl_ext_oneapi_queue_priority.asciidoc b/sycl/doc/extensions/proposed/sycl_ext_oneapi_queue_priority.asciidoc deleted file mode 100644 index 3dcc096c03a14..0000000000000 --- a/sycl/doc/extensions/proposed/sycl_ext_oneapi_queue_priority.asciidoc +++ /dev/null @@ -1,186 +0,0 @@ -= sycl_ext_oneapi_queue_priority - -:source-highlighter: coderay -:coderay-linenums-mode: table - -// This section needs to be after the document title. -:doctype: book -:toc2: -:toc: left -:encoding: utf-8 -:lang: en -:dpcpp: pass:[DPC++] -:endnote: —{nbsp}end{nbsp}note - -// Set the default source code type in this document to C++, -// for syntax highlighting purposes. This is needed because -// docbook uses c++ and html5 uses cpp. -:language: {basebackend@docbook:c++:cpp} - - -== Notice - -[%hardbreaks] -Copyright (C) 2026 Intel Corporation. All rights reserved. - -Khronos(R) is a registered trademark and SYCL(TM) and SPIR(TM) are trademarks -of The Khronos Group Inc. OpenCL(TM) is a trademark of Apple Inc. used by -permission by Khronos. - - -== Contact - -To report problems with this extension, please open a new issue at: - -https://github.com/intel/llvm/issues - - -== Dependencies - -This extension is written against the SYCL 2020 revision 11 specification. All -references below to the "core SYCL specification" or to section numbers in the -SYCL specification refer to that revision. - -== Status - -This is a proposed update to an existing extension. Interfaces defined in this -specification may not be implemented yet or may be in a preliminary state. The -specification itself may also change in incompatible ways before it is -finalized. *Shipping software products should not rely on APIs defined in this -specification.* See -link:../supported/sycl_ext_oneapi_queue_priority.asciidoc[here] for the existing -extension, which is implemented. - -== Overview - -This extension defines additional queue properties that permit a backend, -when supported, to create command queues with different levels of priority. -Priorities are hints from the programmer that certain command submissions -to a device may execute before others of lesser priority. - -The extension defines a property for a numeric priority value and several -pre-defined priority properties with fixed numeric values. - -== Specification - -=== Feature test macro - -This extension provides a feature-test macro as described in the core SYCL -specification. An implementation supporting this extension must predefine the -macro `SYCL_EXT_ONEAPI_QUEUE_PRIORITY` to one of the values defined in the table -below. Applications can test for the existence of this macro to determine if -the implementation supports this feature, or applications can test the macro's -value to determine which of the extension's features the implementation -supports. - -[%header,cols="1,5"] -|=== -|Value -|Description - -|1 -|Initial version of this extension. - -|2 -|Adds `sycl::ext::oneapi::property::queue::priority` property and -`sycl::ext::oneapi::info::device::priority_range` information descriptor. - -|=== - -=== New queue property for numeric priority value - -Added in revision 2 of this specification. - -This extension defines the following new property that may be passed to -the constructor of the `queue` class: - -''' - -[source,c++] ----- -namespace sycl::ext::oneapi::property::queue { - -class priority { -public: - priority(int priority_value); -}; - -} // namespace sycl::ext::oneapi::property::queue ----- - -_Effects:_ Specifies that the queue should be constructed with the specified -`priority_value`. Commands executed by this queue may take priority over -commands in queues with lower priority on this device. If the user specifies -`priority_value` outside the range returned by -`sycl::ext::oneapi::info::device::priority_range` for the device associated -with the queue, `priority_value` will be clamped down or up to the lowest or -highest priority in the range. Lower numbers imply greater priorities. -If the property is not set for a queue, the default priority for that queue is -assumed to be 0. - -=== New queue properties for pre-defined priority values - -This extension defines the following properties that may be passed to -the constructor of the `queue` class: - -''' - -[source,c++] ----- -namespace sycl::ext::oneapi::property::queue { - -// Equivalent to: priority(0) -class priority_normal { -public: - priority_normal() = default; -}; - -// Equivalent to the lowest possible priority for the device -class priority_low { -public: - priority_low() = default; -}; - -// Equivalent to the highest possible priority for the device -class priority_high { -public: - priority_high() = default; -}; - -} // namespace sycl::ext::oneapi::property::queue ----- - -_Effects:_ Specifies that the queue should be constructed with the specified -pre-defined priority. The properties map to the numeric priority values in the -following way: `priority_normal` has a priority value of 0, `priority_low` has -a priority value of the lowest possible priority (highest numerical value) for -the device as reported by the `priority_range` information descriptor, and -`priority_high` has a priority value of the highest possible priority (lowest -numerical value) for the device as reported by the `priority_range` information -descriptor. - -=== New device information descriptor - -Added in revision 2 of this specification. - -''' - -[source, c++] ----- -namespace sycl::ext::oneapi::info::device { - -struct priority_range { - using return_type = std::array; -}; - -} // namespace sycl::ext::oneapi::info::device ----- - -_Remarks:_ Template parameter to `device::get_info`. - -_Returns:_ `std::array` that represents the `{lowestPriority, -highestPriority}` for this device. The first element is the numerical value -corresponding to the lowest (least urgent) queue priority supported by the -device, and the second element is the numerical value corresponding to the -highest (most urgent) queue priority supported by the device. Lower numerical -values imply greater priorities. diff --git a/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc b/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc index aa73c0f03e175..cf73f34c67b01 100644 --- a/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc +++ b/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc @@ -10,6 +10,7 @@ :encoding: utf-8 :lang: en :dpcpp: pass:[DPC++] +:endnote: —{nbsp}end{nbsp}note // Set the default source code type in this document to C++, // for syntax highlighting purposes. This is needed because @@ -20,12 +21,13 @@ == Notice [%hardbreaks] -Copyright (C) 2022 Intel Corporation. All rights reserved. +Copyright (C) 2022-2026 Intel Corporation. All rights reserved. Khronos(R) is a registered trademark and SYCL(TM) and SPIR(TM) are trademarks of The Khronos Group Inc. OpenCL(TM) is a trademark of Apple Inc. used by permission by Khronos. + == Contact To report problems with this extension, please open a new issue at: @@ -35,7 +37,7 @@ https://github.com/intel/llvm/issues == Dependencies -This extension is written against the SYCL 2020 revision 6 specification. All +This extension is written against the SYCL 2020 revision 11 specification. All references below to the "core SYCL specification" or to section numbers in the SYCL specification refer to that revision. @@ -51,21 +53,25 @@ Other backends ignore the properties defined in this specification. == Overview -Introduce SYCL queue properties specifying the desired priority of a queue. -These priorities are a hint and may be ignored if not supported by -underlying backends. +This extension defines additional queue properties that permit a backend, +when supported, to create command queues with different levels of priority. +Priorities are hints from the programmer that certain command submissions +to a device may execute before others of lesser priority. + +The extension defines a property for a numeric priority value and several +pre-defined priority properties with fixed numeric values. == Specification === Feature test macro This extension provides a feature-test macro as described in the core SYCL -specification. An implementation supporting this extension must predefine -the macro `SYCL_EXT_ONEAPI_QUEUE_PRIORITY` to one of the values defined -in the table below. Applications can test for the existence of this macro -to determine if the implementation supports this feature, or applications -can test the macro's value to determine which of the extension's features -the implementation supports. +specification. An implementation supporting this extension must predefine the +macro `SYCL_EXT_ONEAPI_QUEUE_PRIORITY` to one of the values defined in the table +below. Applications can test for the existence of this macro to determine if +the implementation supports this feature, or applications can test the macro's +value to determine which of the extension's features the implementation +supports. [%header,cols="1,5"] |=== @@ -74,35 +80,107 @@ the implementation supports. |1 |Initial version of this extension. + +|2 +|Adds `sycl::ext::oneapi::property::queue::priority` property and +`sycl::ext::oneapi::info::device::priority_range` information descriptor. + |=== -=== API of the extension +=== New queue property for numeric priority value + +Added in revision 2 of this specification. + +This extension defines the following new property that may be passed to +the constructor of the `queue` class: + +''' + +[source,c++] +---- +namespace sycl::ext::oneapi::property::queue { + +class priority { +public: + priority(int priority_value); +}; + +} // namespace sycl::ext::oneapi::property::queue +---- + +_Effects:_ Specifies that the queue should be constructed with the specified +`priority_value`. Commands executed by this queue may take priority over +commands in queues with lower priority on this device. If the user specifies +`priority_value` outside the range returned by +`sycl::ext::oneapi::info::device::priority_range` for the device associated +with the queue, `priority_value` will be clamped down or up to the lowest or +highest priority in the range. Lower numbers imply greater priorities. +If the property is not set for a queue, the default priority for that queue is +assumed to be 0. + +=== New queue properties for pre-defined priority values + +This extension defines the following properties that may be passed to +the constructor of the `queue` class: -This extension adds support for new properties for SYCL queue constructors -taking properties list: +''' -```c++ +[source,c++] +---- namespace sycl::ext::oneapi::property::queue { - class priority_normal { - public: - priority_normal() = default; - }; - class priority_low { - public: - priority_low() = default; - }; - class priority_high { - public: - priority_high() = default; - }; - -} // namespace -``` -The new properties hint the SYCL runtime that the queue gets the specified -priority for execution if supported by underlying target runtimes. These -properties are hints and may safely be ignored by an implementation. - -It is illegal to specify multiple differrent priority hints for the same queue. -Doing so causes the `queue` constructor to throw a synchronous `exception` with -the `errc::invalid` error code. +// Equivalent to: priority(0) +class priority_normal { +public: + priority_normal() = default; +}; + +// Equivalent to the lowest possible priority for the device +class priority_low { +public: + priority_low() = default; +}; + +// Equivalent to the highest possible priority for the device +class priority_high { +public: + priority_high() = default; +}; + +} // namespace sycl::ext::oneapi::property::queue +---- + +_Effects:_ Specifies that the queue should be constructed with the specified +pre-defined priority. The properties map to the numeric priority values in the +following way: `priority_normal` has a priority value of 0, `priority_low` has +a priority value of the lowest possible priority (highest numerical value) for +the device as reported by the `priority_range` information descriptor, and +`priority_high` has a priority value of the highest possible priority (lowest +numerical value) for the device as reported by the `priority_range` information +descriptor. + +=== New device information descriptor + +Added in revision 2 of this specification. + +''' + +[source, c++] +---- +namespace sycl::ext::oneapi::info::device { + +struct priority_range { + using return_type = std::array; +}; + +} // namespace sycl::ext::oneapi::info::device +---- + +_Remarks:_ Template parameter to `device::get_info`. + +_Returns:_ `std::array` that represents the `{lowestPriority, +highestPriority}` for this device. The first element is the numerical value +corresponding to the lowest (least urgent) queue priority supported by the +device, and the second element is the numerical value corresponding to the +highest (most urgent) queue priority supported by the device. Lower numerical +values imply greater priorities. From 1ec7d5f4bdcc6fd633f8b0c5eae02ba709c22ffc Mon Sep 17 00:00:00 2001 From: Artur Gainullin Date: Wed, 14 Oct 2026 15:36:22 -0700 Subject: [PATCH 3/4] [SYCL][Doc] Adjust queue priority revision 2 to the implementation Changes to the moved revision 2 text and the reasons for them: - Add priority::get_priority(). Without a getter the value of the property can't be read back with queue::get_property, and other properties with data, such as compute_index, provide one. - Document the priority constructor and get_priority in separate member blocks, as other extension documents do. - Add a "Conflicting priorities" section. Revision 1 forbids combining the pre-defined priorities, but revision 2 doesn't specify combining them with the numeric priority. Extend the existing errc::invalid rule to all priority properties. - Allow priority_low and priority_high to specify lower and higher priorities than the default one on devices without numeric priorities, whose priority_range is {0, 0}. Backends such as CUDA, HIP and Level Zero v1 keep supporting these hints. - State that the priority range always contains the default priority 0. Otherwise priority(0) and priority_normal could be clamped to a non-default priority. - Specify that devices without numeric priorities return {0, 0}, so that priority_range can be queried on any device and clamping to it is consistent with the default priority. - Mention in the status note that numeric priorities are only used by the Level Zero v2 adapter. - Drop "additional" from the overview, since the document now describes all properties, and "with fixed numeric values", which doesn't hold for priority_low and priority_high whose values depend on the device. Assisted-By: Claude --- .../sycl_ext_oneapi_queue_priority.asciidoc | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc b/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc index cf73f34c67b01..68a355dac009c 100644 --- a/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc +++ b/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc @@ -46,15 +46,16 @@ SYCL specification refer to that revision. This extension is implemented and fully supported by {dpcpp}. [NOTE] ==== -Although {dpcpp} supports this extension on all backends, it is currently used -on Level Zero, CUDA, and HIP. -Other backends ignore the properties defined in this specification. +Although {dpcpp} supports this extension on all backends, the pre-defined +priorities are currently used on Level Zero, CUDA, and HIP, and numeric +priorities are currently used on Level Zero with the v2 adapter. Other +backends ignore the properties defined in this specification. ==== == Overview -This extension defines additional queue properties that permit a backend, -when supported, to create command queues with different levels of priority. +This extension defines queue properties that permit a backend, when +supported, to create command queues with different levels of priority. Priorities are hints from the programmer that certain command submissions to a device may execute before others of lesser priority. @@ -103,11 +104,20 @@ namespace sycl::ext::oneapi::property::queue { class priority { public: priority(int priority_value); + + int get_priority() const; }; } // namespace sycl::ext::oneapi::property::queue ---- +''' + +[source,c++] +---- +priority(int priority_value); +---- + _Effects:_ Specifies that the queue should be constructed with the specified `priority_value`. Commands executed by this queue may take priority over commands in queues with lower priority on this device. If the user specifies @@ -118,6 +128,15 @@ highest priority in the range. Lower numbers imply greater priorities. If the property is not set for a queue, the default priority for that queue is assumed to be 0. +''' + +[source,c++] +---- +int get_priority() const; +---- + +_Returns:_ The `priority_value` this property was constructed with. + === New queue properties for pre-defined priority values This extension defines the following properties that may be passed to @@ -157,7 +176,15 @@ a priority value of the lowest possible priority (highest numerical value) for the device as reported by the `priority_range` information descriptor, and `priority_high` has a priority value of the highest possible priority (lowest numerical value) for the device as reported by the `priority_range` information -descriptor. +descriptor. On devices that don't support numeric priorities, +`priority_range` returns `{0, 0}`, and `priority_low` and `priority_high` may +still specify a lower or higher priority than the default one. + +=== Conflicting priorities + +It is illegal to specify more than one of the properties defined in this +specification for the same queue. Doing so causes the `queue` constructor to +throw a synchronous `exception` with the `errc::invalid` error code. === New device information descriptor @@ -183,4 +210,5 @@ highestPriority}` for this device. The first element is the numerical value corresponding to the lowest (least urgent) queue priority supported by the device, and the second element is the numerical value corresponding to the highest (most urgent) queue priority supported by the device. Lower numerical -values imply greater priorities. +values imply greater priorities. The range always contains the default +priority 0. Devices that don't support numeric priorities return `{0, 0}`. From caf8cc4269ad50365acd3903c523dbad5713e409 Mon Sep 17 00:00:00 2001 From: Artur Gainullin Date: Mon, 19 Oct 2026 13:23:58 -0700 Subject: [PATCH 4/4] [SYCL][Doc] Address queue priority review comments - Don't distinguish between devices supporting numeric priorities and devices supporting the pre-defined priorities. Devices without priority support return {0, 0} from priority_range, so all pre-defined priorities are 0 there, which follows from the existing wording. - Delete the status note listing the backends using each kind of priority. - Keep the copyright year of the original publication. - Write the extension against SYCL 2020 revision 12. Assisted-By: Claude --- .../sycl_ext_oneapi_queue_priority.asciidoc | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc b/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc index 68a355dac009c..6cf5513ab08ef 100644 --- a/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc +++ b/sycl/doc/extensions/supported/sycl_ext_oneapi_queue_priority.asciidoc @@ -21,7 +21,7 @@ == Notice [%hardbreaks] -Copyright (C) 2022-2026 Intel Corporation. All rights reserved. +Copyright (C) 2022 Intel Corporation. All rights reserved. Khronos(R) is a registered trademark and SYCL(TM) and SPIR(TM) are trademarks of The Khronos Group Inc. OpenCL(TM) is a trademark of Apple Inc. used by @@ -37,20 +37,13 @@ https://github.com/intel/llvm/issues == Dependencies -This extension is written against the SYCL 2020 revision 11 specification. All +This extension is written against the SYCL 2020 revision 12 specification. All references below to the "core SYCL specification" or to section numbers in the SYCL specification refer to that revision. == Status This extension is implemented and fully supported by {dpcpp}. -[NOTE] -==== -Although {dpcpp} supports this extension on all backends, the pre-defined -priorities are currently used on Level Zero, CUDA, and HIP, and numeric -priorities are currently used on Level Zero with the v2 adapter. Other -backends ignore the properties defined in this specification. -==== == Overview @@ -176,9 +169,7 @@ a priority value of the lowest possible priority (highest numerical value) for the device as reported by the `priority_range` information descriptor, and `priority_high` has a priority value of the highest possible priority (lowest numerical value) for the device as reported by the `priority_range` information -descriptor. On devices that don't support numeric priorities, -`priority_range` returns `{0, 0}`, and `priority_low` and `priority_high` may -still specify a lower or higher priority than the default one. +descriptor. === Conflicting priorities @@ -211,4 +202,4 @@ corresponding to the lowest (least urgent) queue priority supported by the device, and the second element is the numerical value corresponding to the highest (most urgent) queue priority supported by the device. Lower numerical values imply greater priorities. The range always contains the default -priority 0. Devices that don't support numeric priorities return `{0, 0}`. +priority 0.