Open3D (C++ API)  0.20.0
Loading...
Searching...
No Matches
SYCLUtils.h
Go to the documentation of this file.
1// ----------------------------------------------------------------------------
2// - Open3D: www.open3d.org -
3// ----------------------------------------------------------------------------
4// Copyright (c) 2018-2026 www.open3d.org
5// SPDX-License-Identifier: MIT
6// ----------------------------------------------------------------------------
7
35
36#pragma once
37
38#include <cstdint>
39#include <vector>
40
41#include "open3d/core/Device.h"
43
44#ifdef SYCL_LANGUAGE_VERSION
45#include <algorithm>
46#include <sycl/sycl.hpp>
47#endif
48
49namespace open3d {
50namespace core {
51namespace sy {
52
57void PrintSYCLDevices(bool print_all = false);
58
60bool IsAvailable();
61
63bool IsDeviceAvailable(const Device& device);
64
67SYCLDevice GetSYCLDeviceProperties(const Device& device);
68
71bool IsCPUDevice(const Device& device);
72
74std::vector<Device> GetAvailableSYCLDevices();
75
77inline size_t GetDeviceCount() { return GetAvailableSYCLDevices().size(); }
78
82
83#if defined(SYCL_LANGUAGE_VERSION) && defined(BUILD_SYCL_MODULE)
84
93inline size_t PreferredWorkGroupSize(const sycl::device& sycl_device) {
94 size_t max_wg =
95 sycl_device.get_info<sycl::info::device::max_work_group_size>();
96 size_t wg = std::min<size_t>(256, max_wg);
97 auto sg_sizes = sycl_device.get_info<sycl::info::device::sub_group_sizes>();
98 size_t sg = sg_sizes.empty()
99 ? 1
100 : *std::max_element(sg_sizes.begin(), sg_sizes.end());
101 if (sg > 1 && wg >= sg) {
102 wg -= wg % sg;
103 }
104 return std::max<size_t>(wg, 1);
105}
106
110inline size_t PreferredWorkGroupSize(const Device& device) {
111 return PreferredWorkGroupSize(GetQueue(device).get_device());
112}
113
122inline size_t MaxWorkGroupSizeForSLM(const sycl::device& sycl_device,
123 size_t slm_bytes_per_wi,
124 size_t sg_size) {
125 size_t wg = sycl_device.get_info<sycl::info::device::max_work_group_size>();
126 if (slm_bytes_per_wi > 0) {
127 size_t local_mem_size =
128 sycl_device.get_info<sycl::info::device::local_mem_size>();
129 wg = std::min<size_t>(wg, local_mem_size / slm_bytes_per_wi);
130 }
131 size_t sg = std::max<size_t>(sg_size, 1);
132 if (sg > 1 && wg >= sg) {
133 wg -= wg % sg;
134 }
135 return std::max<size_t>(wg, sg);
136}
137
141inline size_t MaxWorkGroupSizeForSLM(const Device& device,
142 size_t slm_bytes_per_wi,
143 size_t sg_size) {
144 return MaxWorkGroupSizeForSLM(GetQueue(device).get_device(),
145 slm_bytes_per_wi, sg_size);
146}
147
154inline size_t MaxWorkGroupSizeForSLM(const Device& device,
155 size_t slm_bytes_per_wi) {
156 const SYCLDevice props = GetSYCLDeviceProperties(device);
157 const size_t sg_size =
158 props.sub_group_sizes.empty()
159 ? 1
160 : *std::max_element(props.sub_group_sizes.begin(),
161 props.sub_group_sizes.end());
162 return MaxWorkGroupSizeForSLM(device, slm_bytes_per_wi, sg_size);
163}
164
192template <int N, typename scalar_t, typename Func>
193inline void PersistentReduce(sycl::queue& queue,
194 int64_t n,
195 size_t wgs,
196 scalar_t* global_sum_ptr,
197 Func&& compute_local_sum) {
198 const size_t compute_units =
199 SYCLContext::GetInstance().GetComputeUnits(queue.get_device());
200 const int64_t natural_num_groups =
201 std::max<int64_t>(1, (n + int64_t(wgs) - 1) / int64_t(wgs));
202 // One work-group per compute unit is enough to saturate the device for
203 // this persistent-kernel pattern; never launch more groups than there is
204 // work for.
205 const size_t num_groups = static_cast<size_t>(std::min<int64_t>(
206 std::max<int64_t>(1, int64_t(compute_units)), natural_num_groups));
207
208 sycl::buffer<scalar_t, 1> partial_sum_buf(sycl::range<1>(num_groups * N));
209 sycl::buffer<int, 1> ticket_buf(sycl::range<1>(1));
210 queue.submit([&](sycl::handler& cgh) {
211 auto ticket_acc =
212 ticket_buf
213 .template get_access<sycl::access::mode::discard_write>(
214 cgh);
215 cgh.single_task([=]() { ticket_acc[0] = 0; });
216 });
217
218 queue.submit([&](sycl::handler& cgh) {
219 // partial_sum layout is [k * num_groups + group_id] (item 17):
220 // transposed from the original [group_id * N + k] so the merge
221 // phase's per-lane reads (below) are contiguous across groups.
222 auto partial_sum_acc = partial_sum_buf.template get_access<
223 sycl::access::mode::read_write>(cgh);
224 auto ticket_acc = ticket_buf.template get_access<
225 sycl::access::mode::read_write>(cgh);
226 sycl::local_accessor<int, 1> is_last(sycl::range<1>(1), cgh);
227 cgh.parallel_for(
228 sycl::nd_range<1>{num_groups * wgs, wgs},
229 [=](sycl::nd_item<1> item) {
230 const size_t group_id = item.get_group(0);
231 const size_t lid = item.get_local_id(0);
232 const int64_t global_stride =
233 int64_t(num_groups) * int64_t(wgs);
234
235 scalar_t local_sum[N] = {};
236 for (int64_t gid = int64_t(group_id * wgs + lid);
237 gid < n; gid += global_stride) {
238 compute_local_sum(gid, local_sum);
239 }
240
241 auto grp = item.get_group();
242 for (int k = 0; k < N; ++k) {
243 scalar_t v = sycl::reduce_over_group(
244 grp, local_sum[k], sycl::plus<scalar_t>{});
245 if (lid == 0) {
246 partial_sum_acc[size_t(k) * num_groups +
247 group_id] = v;
248 }
249 }
250
251 if (lid == 0) {
252 sycl::atomic_ref<int, sycl::memory_order::acq_rel,
253 sycl::memory_scope::device>
254 tick_ref(ticket_acc[0]);
255 int my_ticket = tick_ref.fetch_add(1);
256 is_last[0] = (my_ticket ==
257 static_cast<int>(num_groups) - 1);
258 }
259 item.barrier(sycl::access::fence_space::local_space);
260
261 if (is_last[0]) {
262 // Merge phase (item 17): each lane privately
263 // sums its share of one output k's num_groups
264 // partial sums (no contention -- each lane
265 // touches distinct memory), then
266 // reduce_over_group combines the wgs per-lane
267 // partials. Replaces the old SLM-atomic
268 // accumulation of all num_groups * N partials
269 // into only N addresses.
270 for (int k = 0; k < N; ++k) {
271 scalar_t partial = 0;
272 for (size_t g = lid; g < num_groups;
273 g += wgs) {
274 partial += partial_sum_acc
275 [size_t(k) * num_groups + g];
276 }
277 scalar_t v = sycl::reduce_over_group(
278 grp, partial, sycl::plus<scalar_t>{});
279 if (lid == 0) {
280 global_sum_ptr[k] = v;
281 }
282 }
283 }
284 });
285 }).wait_and_throw();
286}
287
288#endif // SYCL_LANGUAGE_VERSION && BUILD_SYCL_MODULE
289
290} // namespace sy
291} // namespace core
292} // namespace open3d
sycl::device sycl_device
Definition SYCLContext.cpp:87
sycl::queue queue
Definition SYCLContext.cpp:88
SYCL device properties and (when built) queue manager.
void PrintSYCLDevices(bool print_all)
Definition SYCLUtils.cpp:70
std::vector< Device > GetAvailableSYCLDevices()
Return a list of available SYCL devices.
Definition SYCLUtils.cpp:179
void enablePersistentJITCache()
Definition SYCLUtils.cpp:187
sycl::queue GetQueue(const Device &device)
Definition SYCLContext.cpp:183
bool IsCPUDevice(const Device &device)
Definition SYCLUtils.cpp:175
size_t GetDeviceCount()
Return the number of available SYCL devices.
Definition SYCLUtils.h:77
bool IsAvailable()
Returns true if there is at least one SYCL device available.
Definition SYCLUtils.cpp:148
bool IsDeviceAvailable(const Device &device)
Returns true if the specified SYCL device is available.
Definition SYCLUtils.cpp:156
SYCLDevice GetSYCLDeviceProperties(const Device &device)
Definition SYCLUtils.cpp:164
const char const char value recording_handle imu_sample recording_handle uint8_t size_t data_size k4a_record_configuration_t config target_format k4a_capture_t capture_handle k4a_imu_sample_t imu_sample playback_handle k4a_logging_message_cb_t void min_level device_handle k4a_imu_sample_t timeout_in_ms capture_handle capture_handle capture_handle image_handle temperature_c k4a_image_t image_handle uint8_t image_handle image_handle image_handle image_handle image_handle timestamp_usec white_balance image_handle k4a_device_configuration_t config device_handle char size_t serial_number_size bool int32_t int32_t int32_t int32_t k4a_color_control_mode_t default_mode value const const k4a_calibration_t calibration char size_t
Definition K4aPlugin.cpp:719
const char const char value recording_handle imu_sample recording_handle uint8_t size_t data_size k4a_record_configuration_t config target_format k4a_capture_t capture_handle k4a_imu_sample_t imu_sample playback_handle k4a_logging_message_cb_t void min_level device_handle k4a_imu_sample_t timeout_in_ms capture_handle capture_handle capture_handle image_handle temperature_c int
Definition K4aPlugin.cpp:474
Definition PinholeCameraIntrinsic.cpp:16