Skip to content
This repository was archived by the owner on Mar 28, 2023. It is now read-only.

Commit 034142e

Browse files
authored
[SYCL][ESIMD] Add tests on simd load from accessors (#921)
Based on idea that offset should be a multiplier of alignment flag, and alignment flag should be less strict than simd element alignment itself for simd load constructor to work as expected. Signed-off-by: Kochetkov, Yuriy [email protected]
1 parent ad6cd42 commit 034142e

9 files changed

+768
-9
lines changed

SYCL/ESIMD/api/functional/ctors/ctor_load.hpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include "common.hpp"
1818

19+
#include <string>
20+
1921
namespace esimd = sycl::ext::intel::esimd;
2022

2123
namespace esimd_test::api::functional::ctors {
@@ -29,31 +31,38 @@ namespace alignment {
2931

3032
struct element {
3133
static std::string to_string() { return "element_aligned"; }
32-
template <typename DataT, int> static size_t get_size() {
34+
template <typename DataT, int> static constexpr size_t get_size() {
3335
return alignof(DataT);
3436
}
3537
static constexpr auto get_value() { return esimd::element_aligned; }
3638
};
3739

3840
struct vector {
3941
static std::string to_string() { return "vector_aligned"; }
40-
template <typename DataT, int NumElems> static size_t get_size() {
42+
template <typename DataT, int NumElems> static constexpr size_t get_size() {
4143
// Referring to the simd class specialization on the host side is by design.
4244
return alignof(esimd::simd<DataT, NumElems>);
4345
}
4446
static constexpr auto get_value() { return esimd::vector_aligned; }
4547
};
4648

47-
struct overal {
48-
static std::string to_string() { return "overaligned"; }
49+
template <unsigned int size = 16 /*oword alignment*/> struct overal {
4950
// Use 16 instead of std::max_align_t because of the fact that long double is
5051
// not a native type in Intel GPUs. So 16 is not driven by any type, but
5152
// rather the "oword alignment" requirement for all block loads. In that
5253
// sense, std::max_align_t would give wrong idea.
53-
static constexpr int oword_align = 16;
54-
template <typename, int> static size_t get_size() { return oword_align; }
5554

56-
static constexpr auto get_value() { return esimd::overaligned<oword_align>; }
55+
static std::string to_string() {
56+
return "overaligned<" + std::to_string(size) + ">";
57+
}
58+
59+
template <typename DataT, int> static constexpr size_t get_size() {
60+
static_assert(size % alignof(DataT) == 0,
61+
"Unsupported data type alignment");
62+
return size;
63+
}
64+
65+
static constexpr auto get_value() { return esimd::overaligned<size>; }
5766
};
5867

5968
} // namespace alignment
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
//===-- ctor_load_acc.hpp - Generic code for tests on simd load constructors
2+
// from accessor -----------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
///
10+
/// \file
11+
/// This file provides functions for tests on simd load constructor from
12+
/// accessor
13+
///
14+
//===----------------------------------------------------------------------===//
15+
16+
#pragma once
17+
#define ESIMD_TESTS_DISABLE_DEPRECATED_TEST_DESCRIPTION_FOR_LOGS
18+
19+
#include "../sycl_accessor.hpp"
20+
#include "../sycl_range.hpp"
21+
#include "ctor_load.hpp"
22+
23+
#include <cassert>
24+
#include <cstring>
25+
#include <memory>
26+
#include <sstream>
27+
28+
namespace esimd_test::api::functional::ctors {
29+
30+
// Utility class for offset generation based on alignment and multiplicator
31+
// given
32+
// Offset should be a multiplier of alignment for simd load constructor to work
33+
// as expected
34+
template <unsigned int multiplicator> struct offset_generator {
35+
template <typename AlignmentT, typename DataT, int VecSize>
36+
static constexpr unsigned int get() {
37+
return multiplicator * AlignmentT::template get_size<DataT, VecSize>();
38+
}
39+
};
40+
41+
// Descriptor class for constructor call within initializer context
42+
struct initializer {
43+
static std::string get_description() { return "initializer"; }
44+
45+
template <typename DataT, int NumElems, typename AccessorT,
46+
typename AlignmentT>
47+
static void call_simd_ctor(DataT *const out, const AccessorT &acc,
48+
unsigned int offset, AlignmentT alignment) {
49+
const auto instance = esimd::simd<DataT, NumElems>(acc, offset, alignment);
50+
instance.copy_to(out);
51+
}
52+
};
53+
54+
// Descriptor class for constructor call within variable declaration context
55+
struct var_decl {
56+
static std::string get_description() { return "variable declaration"; }
57+
58+
template <typename DataT, int NumElems, typename AccessorT,
59+
typename AlignmentT>
60+
static void call_simd_ctor(DataT *const out, const AccessorT &acc,
61+
unsigned int offset, AlignmentT alignment) {
62+
esimd::simd<DataT, NumElems> instance(acc, offset, alignment);
63+
instance.copy_to(out);
64+
}
65+
};
66+
67+
// Descriptor class for constructor call within r-value in an expression context
68+
struct rval_in_expr {
69+
static std::string get_description() { return "rvalue in an expression"; }
70+
71+
template <typename DataT, int NumElems, typename AccessorT,
72+
typename AlignmentT>
73+
static void call_simd_ctor(DataT *const out, const AccessorT &acc,
74+
unsigned int offset, AlignmentT alignment) {
75+
esimd::simd<DataT, NumElems> instance;
76+
instance = esimd::simd<DataT, NumElems>(acc, offset, alignment);
77+
instance.copy_to(out);
78+
}
79+
};
80+
81+
// Descriptor class for constructor call within const reference context
82+
class const_ref {
83+
public:
84+
static std::string get_description() { return "const reference"; }
85+
86+
template <typename DataT, int NumElems, typename AccessorT,
87+
typename AlignmentT>
88+
static void call_simd_ctor(DataT *const out, const AccessorT &acc,
89+
unsigned int offset, AlignmentT alignment) {
90+
call_simd_by_const_ref<DataT, NumElems>(
91+
esimd::simd<DataT, NumElems>(acc, offset, alignment), out);
92+
}
93+
94+
private:
95+
template <typename DataT, int NumElems>
96+
static void
97+
call_simd_by_const_ref(const esimd::simd<DataT, NumElems> &simd_by_const_ref,
98+
DataT *const out) {
99+
simd_by_const_ref.copy_to(out);
100+
}
101+
};
102+
103+
// Test case description for logging purposes
104+
template <int VecSize, typename ContextT>
105+
struct LoadCtorAccTestDescription : public ITestDescription {
106+
using BaseT = ctors::LoadCtorTestDescription<VecSize, ContextT>;
107+
108+
public:
109+
LoadCtorAccTestDescription(const std::string &vec_data_name, int dims,
110+
const std::string &acc_mode_name,
111+
const std::string &acc_target_name,
112+
unsigned int offset,
113+
const std::string &alignment_name) {
114+
std::ostringstream stream;
115+
116+
using BaseDescriptionT = ctors::LoadCtorTestDescription<VecSize, ContextT>;
117+
const BaseDescriptionT base_description(vec_data_name, alignment_name);
118+
const AccessorDescription accessor_description(
119+
vec_data_name, dims, acc_mode_name, acc_target_name);
120+
121+
stream << base_description.to_string();
122+
stream << ", with offset " << log::stringify(offset);
123+
stream << ", from " << accessor_description.to_string();
124+
125+
m_description = stream.str();
126+
}
127+
128+
std::string to_string() const override { return m_description; }
129+
130+
private:
131+
std::string m_description;
132+
};
133+
134+
// The main test routine
135+
//
136+
template <typename DataT, typename VecSizeT, typename AccDimsT,
137+
typename AccModeT, typename AccTargetT, typename ContextT,
138+
typename OffsetGeneratorT, typename AlignmentT>
139+
class run_test {
140+
static constexpr int VecSize = VecSizeT::value;
141+
static constexpr int AccDims = AccDimsT::value;
142+
static constexpr sycl::access_mode AccMode = AccModeT::value;
143+
static constexpr sycl::target AccTarget = AccTargetT::value;
144+
static constexpr auto Offset =
145+
OffsetGeneratorT::template get<AlignmentT, DataT, VecSize>();
146+
147+
using SimdT = esimd::simd<DataT, VecSize>;
148+
using AccessorT = sycl::accessor<DataT, AccDims, AccMode, AccTarget>;
149+
using TestDescriptionT = LoadCtorAccTestDescription<VecSize, ContextT>;
150+
using KernelT = Kernel<DataT, VecSize, AccDimsT, AccModeT, AccTargetT,
151+
ContextT, OffsetGeneratorT, AlignmentT>;
152+
153+
static_assert(AccTarget == sycl::target::device,
154+
"Accessor target is not supported");
155+
156+
public:
157+
bool operator()(sycl::queue &queue, const std::string &vec_data_name,
158+
const std::string &acc_mode_name,
159+
const std::string &acc_target_name,
160+
const std::string &alignment_name) {
161+
// Define the mapping between parameters retrieved and test descriptor
162+
// arguments for logging purposes
163+
return run(queue, /* The rest are the test description parameters */
164+
vec_data_name, AccDims, acc_mode_name, acc_target_name, Offset,
165+
alignment_name);
166+
}
167+
168+
private:
169+
template <typename... TestDescriptionArgsT>
170+
inline bool run(sycl::queue &queue, TestDescriptionArgsT &&...args) {
171+
bool passed = true;
172+
log::trace<TestDescriptionT>(std::forward<TestDescriptionArgsT>(args)...);
173+
174+
const std::vector<DataT> ref_data = generate_ref_data<DataT, VecSize>();
175+
176+
if constexpr (VecSize == 1) {
177+
// Ensure simd load constructor works as expected with every value from
178+
// reference data
179+
for (size_t i = 0; i < ref_data.size(); ++i) {
180+
passed = run_with_data(queue, {ref_data[i]},
181+
std::forward<TestDescriptionArgsT>(args)...);
182+
}
183+
} else {
184+
passed = run_with_data(queue, ref_data,
185+
std::forward<TestDescriptionArgsT>(args)...);
186+
}
187+
return passed;
188+
}
189+
190+
template <typename... TestDescriptionArgsT>
191+
bool run_with_data(sycl::queue &queue, const std::vector<DataT> &ref_data,
192+
TestDescriptionArgsT... args) {
193+
assert(ref_data.size() == VecSize &&
194+
"Reference data size is not equal to the simd vector length.");
195+
196+
bool passed = true;
197+
std::vector<DataT> container;
198+
199+
shared_allocator<DataT> allocator(queue);
200+
shared_vector<DataT> result(VecSize, allocator);
201+
202+
// Fill container with reference data using the pointer modified accordingly
203+
// to the offset parameter of the load constructor
204+
{
205+
const size_t extra_space =
206+
(Offset / sizeof(DataT)) + (Offset % sizeof(DataT) > 0);
207+
container.resize(VecSize + extra_space);
208+
log::debug([&]() {
209+
return " ... using container with " + log::stringify(container.size()) +
210+
" elements";
211+
});
212+
213+
// We don't break the strict aliasing rule according to the C++17
214+
// [basic.lval]
215+
auto ptr = reinterpret_cast<unsigned char *>(container.data());
216+
std::memset(ptr, 0, Offset);
217+
ptr += Offset;
218+
std::memcpy(ptr, ref_data.data(), VecSize * sizeof(DataT));
219+
// No initialization for the rest of the bytes to simplify the code
220+
221+
// Now container has the reference elements starting from the offset byte,
222+
// with all preceding bytes filled by zero.
223+
// No other modification required, as the simd load constructor doesn't
224+
// actually work with alignment of input data
225+
}
226+
227+
// Call simd constructor and fill the result vector
228+
{
229+
const auto range = get_sycl_range<AccDims>(container.size());
230+
sycl::buffer<DataT, AccDims> buffer(container.data(), range);
231+
log::debug([&]() {
232+
return " ... using sycl::buffer with " + log::stringify(range) +
233+
" to access container";
234+
});
235+
assert((container.size() == range.size()) && "Unexpected range");
236+
237+
queue.submit([&](sycl::handler &cgh) {
238+
const AccessorT acc =
239+
buffer.template get_access<AccMode, AccTarget>(cgh);
240+
DataT *const out = result.data();
241+
242+
cgh.single_task<KernelT>([=]() SYCL_ESIMD_KERNEL {
243+
// This alignment affect only the internal simd storage
244+
// efficiency; no any test failure expected with any alignment
245+
// provided for every possible case
246+
const auto alignment = AlignmentT::get_value();
247+
ContextT::template call_simd_ctor<DataT, VecSize>(out, acc, Offset,
248+
alignment);
249+
});
250+
});
251+
queue.wait_and_throw();
252+
}
253+
254+
// Validate results
255+
for (size_t i = 0; i < result.size(); ++i) {
256+
const auto &expected = ref_data[i];
257+
const auto &retrieved = result[i];
258+
259+
if (!are_bitwise_equal(expected, retrieved)) {
260+
passed = false;
261+
262+
log::fail(TestDescriptionT(std::forward<TestDescriptionArgsT>(args)...),
263+
"Unexpected value at index ", i, ", retrieved: ", retrieved,
264+
", expected: ", expected);
265+
}
266+
}
267+
return passed;
268+
}
269+
};
270+
271+
} // namespace esimd_test::api::functional::ctors

0 commit comments

Comments
 (0)