Open3D (C++ API)  0.19.0
Loading...
Searching...
No Matches
ParallelFor.h
Go to the documentation of this file.
1// ----------------------------------------------------------------------------
2// - Open3D: www.open3d.org -
3// ----------------------------------------------------------------------------
4// Copyright (c) 2018-2024 www.open3d.org
5// SPDX-License-Identifier: MIT
6// ----------------------------------------------------------------------------
7
8#pragma once
9
10#include <cstdint>
11#include <type_traits>
12
13#include "open3d/core/Device.h"
18
19#ifdef __CUDACC__
20#include <cuda.h>
21#include <cuda_runtime.h>
22
24#endif
25
26#if defined(SYCL_LANGUAGE_VERSION)
28#endif
29
30namespace open3d {
31namespace core {
32
33#ifdef __CUDACC__
34
35static constexpr int64_t OPEN3D_PARFOR_BLOCK = 128;
36static constexpr int64_t OPEN3D_PARFOR_THREAD = 4;
37
39template <int64_t block_size, int64_t thread_size, typename func_t>
40__global__ void ElementWiseKernel_(int64_t n, func_t f) {
41 int64_t items_per_block = block_size * thread_size;
42 int64_t idx = blockIdx.x * items_per_block + threadIdx.x;
43#pragma unroll
44 for (int64_t i = 0; i < thread_size; ++i) {
45 if (idx < n) {
46 f(idx);
47 idx += block_size;
48 }
49 }
50}
51
53template <typename func_t>
54void ParallelForCUDA_(const Device& device, int64_t n, const func_t& func) {
55 if (device.GetType() != Device::DeviceType::CUDA) {
56 utility::LogError("ParallelFor for CUDA cannot run on device {}.",
57 device.ToString());
58 }
59 if (n == 0) {
60 return;
61 }
62
63 CUDAScopedDevice scoped_device(device);
64 int64_t items_per_block = OPEN3D_PARFOR_BLOCK * OPEN3D_PARFOR_THREAD;
65 int64_t grid_size = (n + items_per_block - 1) / items_per_block;
66
67 ElementWiseKernel_<OPEN3D_PARFOR_BLOCK, OPEN3D_PARFOR_THREAD>
68 <<<grid_size, OPEN3D_PARFOR_BLOCK, 0, core::cuda::GetStream()>>>(
69 n, func);
70 OPEN3D_GET_LAST_CUDA_ERROR("ParallelFor failed.");
71}
72
73#else
74
76template <typename func_t>
77void ParallelForCPU_(const Device& device, int64_t n, const func_t& func) {
78 if (!device.IsCPU()) {
79 utility::LogError("ParallelFor for CPU cannot run on device {}.",
80 device.ToString());
81 }
82 if (n == 0) {
83 return;
84 }
85
86#pragma omp parallel for num_threads(utility::EstimateMaxThreads())
87 for (int64_t i = 0; i < n; ++i) {
88 func(i);
89 }
90}
91
92#endif
93
94#if defined(SYCL_LANGUAGE_VERSION)
95
97template <typename func_t>
98void ParallelForSYCL_(const Device& device, int64_t n, const func_t& func) {
99 if (!device.IsSYCL()) {
100 utility::LogError("ParallelFor for SYCL cannot run on device {}.",
101 device.ToString());
102 }
103 if (n == 0) {
104 return;
105 }
106 auto queue = core::sy::SYCLContext::GetInstance().GetDefaultQueue(device);
107 size_t wg = core::sy::PreferredWorkGroupSize(device);
108 const size_t global_size = ((static_cast<size_t>(n) + wg - 1) / wg) * wg;
109 sycl::nd_range<1> nd_range{sycl::range<1>(global_size), sycl::range<1>(wg)};
110 queue.parallel_for(nd_range, [=](sycl::nd_item<1> item) {
111 int64_t i = item.get_global_id(0);
112 if (i < n) {
113 func(i);
114 }
115 }).wait_and_throw();
116}
117
118#endif
119
134template <typename func_t>
135void ParallelFor(const Device& device, int64_t n, const func_t& func) {
136#ifdef __CUDACC__
137 ParallelForCUDA_(device, n, func);
138#elif defined(SYCL_LANGUAGE_VERSION)
139 if (device.IsSYCL()) {
140 ParallelForSYCL_(device, n, func);
141 } else {
142 ParallelForCPU_(device, n, func);
143 }
144#else
145 ParallelForCPU_(device, n, func);
146#endif
147}
148
195template <typename vec_func_t, typename func_t>
196void ParallelFor(const Device& device,
197 int64_t n,
198 const func_t& func,
199 const vec_func_t& vec_func) {
200#ifdef BUILD_ISPC_MODULE
201
202#ifdef __CUDACC__
203 ParallelForCUDA_(device, n, func);
204#elif defined(SYCL_LANGUAGE_VERSION)
205 if (device.IsSYCL()) {
206 ParallelForSYCL_(device, n, func);
207 } else {
208 int num_threads = utility::EstimateMaxThreads();
209 ParallelForCPU_(device, num_threads, [&](int64_t i) {
210 int64_t start = n * i / num_threads;
211 int64_t end = std::min<int64_t>(n * (i + 1) / num_threads, n);
212 vec_func(start, end);
213 });
214 }
215#else
216 int num_threads = utility::EstimateMaxThreads();
217 ParallelForCPU_(device, num_threads, [&](int64_t i) {
218 int64_t start = n * i / num_threads;
219 int64_t end = std::min<int64_t>(n * (i + 1) / num_threads, n);
220 vec_func(start, end);
221 });
222#endif
223
224#else
225
226#ifdef __CUDACC__
227 ParallelForCUDA_(device, n, func);
228#elif defined(SYCL_LANGUAGE_VERSION)
229 if (device.IsSYCL()) {
230 ParallelForSYCL_(device, n, func);
231 } else {
232 ParallelForCPU_(device, n, func);
233 }
234#else
235 ParallelForCPU_(device, n, func);
236#endif
237
238#endif
239}
240
241#ifdef BUILD_ISPC_MODULE
242
243// Internal helper macro.
244#define OPEN3D_CALL_ISPC_KERNEL_(ISPCKernel, start, end, ...) \
245 using namespace ispc; \
246 ISPCKernel(start, end, __VA_ARGS__);
247
248#else
249
250// Internal helper macro.
251#define OPEN3D_CALL_ISPC_KERNEL_(ISPCKernel, start, end, ...) \
252 utility::LogError( \
253 "ISPC module disabled. Unable to call vectorized kernel {}", \
254 OPEN3D_STRINGIFY(ISPCKernel));
255
256#endif
257
259#define OPEN3D_OVERLOADED_LAMBDA_(T, ISPCKernel, ...) \
260 [&](T, int64_t start, int64_t end) { \
261 OPEN3D_CALL_ISPC_KERNEL_( \
262 OPEN3D_CONCAT(ISPCKernel, OPEN3D_CONCAT(_, T)), start, end, \
263 __VA_ARGS__); \
264 }
265
275#define OPEN3D_VECTORIZED(ISPCKernel, ...) \
276 [&](int64_t start, int64_t end) { \
277 OPEN3D_CALL_ISPC_KERNEL_(ISPCKernel, start, end, __VA_ARGS__); \
278 }
279
293#define OPEN3D_TEMPLATE_VECTORIZED(T, ISPCKernel, ...) \
294 [&](int64_t start, int64_t end) { \
295 static_assert(std::is_arithmetic<T>::value, \
296 "Data type is not an arithmetic type"); \
297 utility::Overload( \
298 OPEN3D_OVERLOADED_LAMBDA_(bool, ISPCKernel, __VA_ARGS__), \
299 OPEN3D_OVERLOADED_LAMBDA_(uint8_t, ISPCKernel, __VA_ARGS__), \
300 OPEN3D_OVERLOADED_LAMBDA_(int8_t, ISPCKernel, __VA_ARGS__), \
301 OPEN3D_OVERLOADED_LAMBDA_(uint16_t, ISPCKernel, __VA_ARGS__), \
302 OPEN3D_OVERLOADED_LAMBDA_(int16_t, ISPCKernel, __VA_ARGS__), \
303 OPEN3D_OVERLOADED_LAMBDA_(uint32_t, ISPCKernel, __VA_ARGS__), \
304 OPEN3D_OVERLOADED_LAMBDA_(int32_t, ISPCKernel, __VA_ARGS__), \
305 OPEN3D_OVERLOADED_LAMBDA_(uint64_t, ISPCKernel, __VA_ARGS__), \
306 OPEN3D_OVERLOADED_LAMBDA_(int64_t, ISPCKernel, __VA_ARGS__), \
307 OPEN3D_OVERLOADED_LAMBDA_(float, ISPCKernel, __VA_ARGS__), \
308 OPEN3D_OVERLOADED_LAMBDA_(double, ISPCKernel, __VA_ARGS__), \
309 [&](auto&& generic, int64_t start, int64_t end) { \
310 utility::LogError( \
311 "Unsupported data type {} for calling " \
312 "vectorized kernel {}", \
313 typeid(generic).name(), \
314 OPEN3D_STRINGIFY(ISPCKernel)); \
315 })(T{}, start, end); \
316 }
317
318} // namespace core
319} // namespace open3d
Common CUDA utilities.
#define OPEN3D_GET_LAST_CUDA_ERROR(message)
Definition CUDAUtils.h:47
sycl::queue queue
Definition SYCLContext.cpp:51
Common SYCL utilities.
Definition Device.h:18
bool IsSYCL() const
Returns true iff device type is SYCL GPU.
Definition Device.h:54
bool IsCPU() const
Returns true iff device type is CPU.
Definition Device.h:48
std::string ToString() const
Returns string representation of device, e.g. "CPU:0", "CUDA:0".
Definition Device.cpp:94
void ParallelForCPU_(const Device &device, int64_t n, const func_t &func)
Run a function in parallel on CPU.
Definition ParallelFor.h:77
void ParallelFor(const Device &device, int64_t n, const func_t &func)
Definition ParallelFor.h:135
int EstimateMaxThreads()
Estimate the maximum number of threads to be used in a parallel region.
Definition Parallel.cpp:31
Definition PinholeCameraIntrinsic.cpp:16