Skip to content

Commit 899072c

Browse files
committed
Add SYCL parallel backend integration
1 parent e389154 commit 899072c

5 files changed

Lines changed: 139 additions & 26 deletions

File tree

app/SYCL/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ if(WIN32)
2828
"--acpp-cpu-cxx=${_sycl_cpu_cxx}"
2929
-std=c++20
3030
-fms-runtime-lib=dll
31+
-DITLABAI_HAS_SYCL
3132
-D_USE_MATH_DEFINES
3233
-I "${CMAKE_SOURCE_DIR}/include"
3334
)
@@ -58,6 +59,8 @@ if(WIN32)
5859
target_sources(SYCL_Example PRIVATE "${SYCL_EXAMPLE_KERNEL_OBJECT}")
5960
else()
6061
add_library(SYCL_Example_kernel OBJECT sycl_kernel.cpp)
62+
target_compile_definitions(SYCL_Example_kernel PRIVATE ITLABAI_HAS_SYCL)
63+
target_link_libraries(SYCL_Example_kernel PRIVATE AdaptiveCpp::acpp-rt TBB_unified Kokkos_imported)
6164
add_sycl_to_target(TARGET SYCL_Example_kernel SOURCES
6265
sycl_kernel.cpp
6366
)

app/SYCL/sycl_example.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <cmath>
22
#include <iostream>
3-
#include <sycl/sycl.hpp>
3+
#include <string>
44
#include <vector>
55

66
#include "graph/runtime_options.hpp"
@@ -15,8 +15,8 @@ bool almost_equal(float lhs, float rhs) {
1515

1616
} // namespace
1717

18-
void run_sycl_kernel(sycl::queue& queue, const float* input, float* output,
19-
std::size_t count);
18+
std::string sycl_device_name();
19+
void run_sycl_kernel(const float* input, float* output, std::size_t count);
2020

2121
int main() {
2222
try {
@@ -40,13 +40,10 @@ int main() {
4040
}
4141

4242
const std::size_t count = relu_output.size();
43-
sycl::queue queue(sycl::default_selector_v);
44-
std::cout << "SYCL device: "
45-
<< queue.get_device().get_info<sycl::info::device::name>()
46-
<< '\n';
43+
std::cout << "SYCL device: " << sycl_device_name() << '\n';
4744

4845
std::vector<float> sycl_output(count);
49-
run_sycl_kernel(queue, relu_output.data(), sycl_output.data(), count);
46+
run_sycl_kernel(relu_output.data(), sycl_output.data(), count);
5047

5148
const std::vector<float> expected_sycl = {1.0F, 1.0F, 1.0F, 3.0F, 5.0F};
5249
for (std::size_t i = 0; i < count; ++i) {

app/SYCL/sycl_kernel.cpp

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
#include <cstddef>
2-
#include <sycl/sycl.hpp>
3-
class SyclExampleTransformKernel;
2+
#include <string>
43

5-
void run_sycl_kernel(sycl::queue& queue, const float* input, float* output,
6-
std::size_t count) {
7-
sycl::buffer<const float, 1> input_buffer(input, sycl::range<1>(count));
8-
sycl::buffer<float, 1> output_buffer(output, sycl::range<1>(count));
4+
#include "parallel/parallel.hpp"
95

10-
queue.submit([&](sycl::handler& handler) {
11-
auto input_acc = input_buffer.get_access<sycl::access::mode::read>(handler);
12-
auto output_acc =
13-
output_buffer.get_access<sycl::access::mode::write>(handler);
14-
handler.parallel_for<SyclExampleTransformKernel>(sycl::range<1>(count),
15-
[=](sycl::id<1> index) {
16-
output_acc[index] = input_acc[index] * 2.0F + 1.0F;
17-
});
18-
});
19-
queue.wait_and_throw();
6+
std::string sycl_device_name() {
7+
return it_lab_ai::parallel::sycl_device_name();
8+
}
9+
10+
void run_sycl_kernel(const float* input, float* output, std::size_t count) {
11+
auto shared_input = it_lab_ai::parallel::make_sycl_shared_buffer<float>(count);
12+
auto shared_output =
13+
it_lab_ai::parallel::make_sycl_shared_buffer<float>(count);
14+
15+
shared_input.copy_from(input);
16+
const float* input_data = shared_input.data();
17+
float* output_data = shared_output.data();
18+
19+
it_lab_ai::parallel::parallel_for(
20+
count,
21+
[=](std::size_t index) {
22+
output_data[index] = input_data[index] * 2.0F + 1.0F;
23+
},
24+
it_lab_ai::parallel::Backend::kSycl);
25+
26+
shared_output.copy_to(output);
2027
}

include/parallel/backends.hpp

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,16 @@
99
#include <cstdint>
1010
#include <functional>
1111
#include <limits>
12+
#include <stdexcept>
13+
#include <string>
1214
#include <thread>
15+
#include <utility>
1316
#include <vector>
1417

18+
#ifdef ITLABAI_HAS_SYCL
19+
# include <sycl/sycl.hpp>
20+
#endif
21+
1522
namespace it_lab_ai {
1623
namespace parallel {
1724

@@ -20,7 +27,8 @@ enum class Backend : std::uint8_t {
2027
kThreads = 1,
2128
kTbb = 2,
2229
kOmp = 3,
23-
kKokkos = 4
30+
kKokkos = 4,
31+
kSycl = 5
2432
};
2533

2634
struct Options {
@@ -149,5 +157,100 @@ inline void impl_kokkos(std::size_t count,
149157
Kokkos::fence();
150158
}
151159

160+
#ifdef ITLABAI_HAS_SYCL
161+
inline sycl::queue& sycl_queue() {
162+
static sycl::queue queue{sycl::default_selector_v};
163+
return queue;
164+
}
165+
166+
inline std::string sycl_device_name() {
167+
return sycl_queue().get_device().get_info<sycl::info::device::name>();
168+
}
169+
170+
template <typename T>
171+
class SyclSharedBuffer {
172+
public:
173+
explicit SyclSharedBuffer(std::size_t count)
174+
: count_(count), data_(count == 0 ? nullptr : sycl::malloc_shared<T>(
175+
count, sycl_queue())) {}
176+
177+
SyclSharedBuffer(const SyclSharedBuffer&) = delete;
178+
SyclSharedBuffer& operator=(const SyclSharedBuffer&) = delete;
179+
180+
SyclSharedBuffer(SyclSharedBuffer&& other) noexcept
181+
: count_(other.count_), data_(other.data_) {
182+
other.count_ = 0;
183+
other.data_ = nullptr;
184+
}
185+
186+
SyclSharedBuffer& operator=(SyclSharedBuffer&& other) noexcept {
187+
if (this != &other) {
188+
release();
189+
count_ = other.count_;
190+
data_ = other.data_;
191+
other.count_ = 0;
192+
other.data_ = nullptr;
193+
}
194+
return *this;
195+
}
196+
197+
~SyclSharedBuffer() { release(); }
198+
199+
T* data() { return data_; }
200+
const T* data() const { return data_; }
201+
std::size_t size() const { return count_; }
202+
203+
void copy_from(const T* source) {
204+
for (std::size_t i = 0; i < count_; ++i) {
205+
data_[i] = source[i];
206+
}
207+
}
208+
209+
void copy_to(T* destination) const {
210+
for (std::size_t i = 0; i < count_; ++i) {
211+
destination[i] = data_[i];
212+
}
213+
}
214+
215+
private:
216+
void release() {
217+
if (data_ != nullptr) {
218+
sycl::free(data_, sycl_queue());
219+
data_ = nullptr;
220+
count_ = 0;
221+
}
222+
}
223+
224+
std::size_t count_;
225+
T* data_;
226+
};
227+
228+
template <typename T>
229+
inline SyclSharedBuffer<T> make_sycl_shared_buffer(std::size_t count) {
230+
return SyclSharedBuffer<T>(count);
231+
}
232+
233+
template <typename Func>
234+
inline void impl_sycl(std::size_t count, Func&& func, const Options&) {
235+
if (count == 0) {
236+
return;
237+
}
238+
239+
auto& queue = sycl_queue();
240+
queue.parallel_for(sycl::range<1>(count),
241+
[func = std::forward<Func>(func)](sycl::id<1> index) {
242+
func(static_cast<std::size_t>(index[0]));
243+
});
244+
queue.wait_and_throw();
245+
}
246+
#else
247+
template <typename Func>
248+
[[noreturn]] inline void impl_sycl(std::size_t, Func&&, const Options&) {
249+
throw std::runtime_error(
250+
"SYCL parallel backend was requested, but this target was not built with "
251+
"ITLABAI_HAS_SYCL");
252+
}
253+
#endif
254+
152255
} // namespace parallel
153256
} // namespace it_lab_ai

include/parallel/parallel.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ inline Backend select_backend(const Options& opt, std::size_t n) {
3030

3131
if (opt.backend == Backend::kSeq || opt.backend == Backend::kThreads ||
3232
opt.backend == Backend::kTbb || opt.backend == Backend::kOmp ||
33-
opt.backend == Backend::kKokkos) {
33+
opt.backend == Backend::kKokkos || opt.backend == Backend::kSycl) {
3434
return opt.backend;
3535
}
3636

@@ -62,6 +62,9 @@ inline void parallel_for(std::size_t count, Func&& func,
6262
case Backend::kKokkos:
6363
impl_kokkos(count, std::forward<Func>(func), opt);
6464
break;
65+
case Backend::kSycl:
66+
impl_sycl(count, std::forward<Func>(func), opt);
67+
break;
6568
}
6669
}
6770

0 commit comments

Comments
 (0)