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

Commit 7794b50

Browse files
authored
[SYCL][ESIMD] Add tests on simd select 1D functions (#875)
Provides tests for select r-value, l-value and for 1D simd_view functions.
1 parent b8ee3a5 commit 7794b50

8 files changed

+570
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===-- common.hpp - Define common code for simd functions tests ----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// This file provides common code for simd functions tests.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#pragma once
15+
16+
#include "../common.hpp"
17+
18+
namespace esimd_test::api::functional::functions {
19+
20+
namespace esimd = sycl::ext::intel::esimd;
21+
22+
template <int NumElems, int NumSelectedElems, int Stride, int Offset,
23+
typename ContextT>
24+
class TestDescription : public ITestDescription {
25+
public:
26+
TestDescription(const std::string &data_type) : m_data_type(data_type) {}
27+
28+
std::string to_string() const override {
29+
std::string test_description("simd<");
30+
31+
test_description += m_data_type + ", " + std::to_string(NumElems) + ">";
32+
test_description += ", with context: " + ContextT::get_description();
33+
test_description +=
34+
", with size selected elems: " + std::to_string(NumSelectedElems);
35+
test_description += ", with stride: " + std::to_string(Stride);
36+
test_description += ", with offset: " + std::to_string(Offset);
37+
38+
return test_description;
39+
}
40+
41+
private:
42+
const std::string m_data_type;
43+
};
44+
45+
} // namespace esimd_test::api::functional::functions
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
//===-- functions_1d_select.hpp - Functions for tests on simd rvalue select
2+
// function. ---------------------------------------------------------===//
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 1d select function.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#pragma once
16+
17+
#include "../shared_element.hpp"
18+
#include "common.hpp"
19+
20+
// for std::numeric_limits
21+
#include <limits>
22+
// for std::iota
23+
#include <numeric>
24+
25+
namespace esimd_test::api::functional::functions {
26+
27+
namespace details {
28+
29+
constexpr int ceil(int a, int b) {
30+
return ((a % b) > 0) ? (a / b + 1) : (a / b);
31+
}
32+
33+
} // namespace details
34+
35+
using use_offset = std::true_type;
36+
using do_not_use_offset = std::true_type;
37+
38+
// Descriptor class for the case of calling simd<T,N>::select function.
39+
struct select_rval {
40+
static std::string get_description() { return "select rvalue"; }
41+
42+
template <typename DataT, int NumElems, int NumSelectedElems, int Stride>
43+
static bool call_operator(const DataT *const initial_data,
44+
const DataT *const data_for_change,
45+
DataT *const out, size_t offset) {
46+
esimd::simd<DataT, NumElems> simd_obj;
47+
simd_obj.copy_from(initial_data);
48+
auto select_result =
49+
simd_obj.template select<NumSelectedElems, Stride>(offset);
50+
51+
for (size_t i = 0; i < NumSelectedElems; ++i) {
52+
select_result[i] = data_for_change[i];
53+
}
54+
simd_obj.copy_to(out);
55+
56+
return std::is_same_v<
57+
decltype(select_result),
58+
esimd::simd_view<esimd::simd<DataT, NumElems>,
59+
esimd::region1d_t<DataT, NumSelectedElems, Stride>>>;
60+
}
61+
};
62+
63+
// Descriptor class for the case of calling simd<T,N>::select function.
64+
struct select_lval {
65+
static std::string get_description() { return "select lvalue"; }
66+
67+
template <typename DataT, int NumElems, int NumSelectedElems, int Stride>
68+
static bool call_operator(const DataT *const initial_data,
69+
const DataT *const data_for_change,
70+
DataT *const out, size_t offset) {
71+
esimd::simd<DataT, NumElems> src_simd_obj;
72+
src_simd_obj.copy_from(initial_data);
73+
74+
esimd::simd<DataT, NumSelectedElems> simd_for_change_values;
75+
simd_for_change_values.copy_from(data_for_change);
76+
77+
src_simd_obj.template select<NumSelectedElems, Stride>(offset) =
78+
simd_for_change_values;
79+
src_simd_obj.copy_to(out);
80+
81+
return true;
82+
}
83+
};
84+
85+
// Descriptor class for the case of calling simd<T,N>::select function.
86+
struct select_simd_view_rval {
87+
static std::string get_description() { return "select simd view rvalue"; }
88+
89+
template <typename DataT, int NumElems, int NumSelectedElems, int Stride>
90+
static bool call_operator(const DataT *const initial_data,
91+
const DataT *const data_for_change,
92+
DataT *const out, size_t offset) {
93+
esimd::simd<DataT, NumElems> src_simd_obj;
94+
src_simd_obj.copy_from(initial_data);
95+
96+
auto simd_view_instance = src_simd_obj.template bit_cast_view<DataT>();
97+
98+
auto selected_elems =
99+
simd_view_instance.template select<NumSelectedElems, Stride>(offset);
100+
101+
for (size_t i = 0; i < NumSelectedElems; ++i) {
102+
selected_elems[i] = data_for_change[i];
103+
}
104+
105+
src_simd_obj.copy_to(out);
106+
107+
return true;
108+
}
109+
};
110+
111+
// The main test routine.
112+
// Using functor class to be able to iterate over the pre-defined data types.
113+
template <typename TestCaseT, typename NumSelectedElemsT, typename StrideT,
114+
typename OffsetT, typename DataT, typename DimT>
115+
class run_test {
116+
static constexpr int NumElems = DimT::value;
117+
static constexpr int NumSelectedElems = NumSelectedElemsT::value;
118+
static constexpr int Stride = StrideT::value;
119+
static constexpr int Offset = OffsetT::value;
120+
using TestDescriptionT =
121+
TestDescription<NumElems, NumSelectedElems, Stride, Offset, TestCaseT>;
122+
123+
public:
124+
bool operator()(sycl::queue &queue, const std::string &data_type) {
125+
log::trace<TestDescriptionT>(data_type);
126+
static_assert(NumElems >= NumSelectedElems * Stride + Offset &&
127+
"Number selected elements should be less than simd size.");
128+
bool passed = true;
129+
size_t alignment_value = alignof(DataT);
130+
131+
constexpr size_t value_for_increase_ref_data_for_change = 50;
132+
static_assert(std::numeric_limits<signed char>::max() >
133+
value_for_increase_ref_data_for_change + NumElems);
134+
135+
shared_allocator<DataT> allocator(queue);
136+
shared_vector<DataT> result(NumElems, allocator);
137+
shared_vector<DataT> initial_ref_data(NumElems, allocator);
138+
shared_vector<DataT> ref_data_for_change(NumElems, allocator);
139+
140+
shared_element<bool> is_correct_type(queue, true);
141+
142+
std::iota(initial_ref_data.begin(), initial_ref_data.end(), 0);
143+
// We should have different values in the first reference data and in the
144+
// second reference data.
145+
std::iota(ref_data_for_change.begin(), ref_data_for_change.end(),
146+
initial_ref_data.back() + value_for_increase_ref_data_for_change);
147+
148+
queue.submit([&](sycl::handler &cgh) {
149+
DataT *init_ref_ptr = initial_ref_data.data();
150+
DataT *ref_data_for_change_ptr = ref_data_for_change.data();
151+
DataT *const out_ptr = result.data();
152+
auto is_correct_type_ptr = is_correct_type.data();
153+
154+
cgh.single_task<
155+
Kernel<DataT, NumElems, TestCaseT, NumSelectedElemsT, StrideT>>(
156+
[=]() SYCL_ESIMD_KERNEL {
157+
*is_correct_type_ptr =
158+
TestCaseT::template call_operator<DataT, NumElems,
159+
NumSelectedElems, Stride>(
160+
init_ref_ptr, ref_data_for_change_ptr, out_ptr, Offset);
161+
});
162+
});
163+
queue.wait_and_throw();
164+
165+
std::vector<size_t> selected_indexes;
166+
// Collect the indexess that has been selected.
167+
for (size_t i = Offset; i < Stride * NumSelectedElems + Offset;
168+
i += Stride) {
169+
selected_indexes.push_back(i);
170+
}
171+
172+
// Push the largest value to avoid the following error: can't dereference
173+
// out of range vector iterator.
174+
selected_indexes.push_back(std::numeric_limits<size_t>::max());
175+
auto next_selected_index = selected_indexes.begin();
176+
177+
// Verify that values, that do not was selected has initial values.
178+
for (size_t i = 0; i < NumElems; ++i) {
179+
// If current index is less than selected index verify that this element
180+
// hasn't been selected and changed.
181+
if (i < *next_selected_index) {
182+
const DataT &expected = initial_ref_data[i];
183+
const DataT &retrieved = result[i];
184+
if (expected != retrieved) {
185+
passed = fail_test(i, expected, retrieved, data_type);
186+
}
187+
} else {
188+
const DataT &expected = ref_data_for_change[(i - Offset) / Stride];
189+
const DataT &retrieved = result[i];
190+
if (expected != retrieved) {
191+
passed = fail_test(i, expected, retrieved, data_type);
192+
}
193+
next_selected_index++;
194+
}
195+
}
196+
197+
if (!is_correct_type.value()) {
198+
passed = false;
199+
log::fail(TestDescriptionT(data_type), "Unexpected return type.");
200+
}
201+
202+
return passed;
203+
}
204+
205+
private:
206+
bool fail_test(size_t i, DataT expected, DataT retrieved,
207+
const std::string &data_type) {
208+
log::fail(TestDescriptionT(data_type), "Unexpected value at index ", i,
209+
", retrieved: ", retrieved, ", expected: ", expected);
210+
211+
return false;
212+
}
213+
};
214+
215+
// Aliases to provide size or stride values to test.
216+
// This is the syntax sugar just for code readability.
217+
template <int N> using stride_type = std::integral_constant<int, N>;
218+
template <int N> using size_type = std::integral_constant<int, N>;
219+
template <int N> using offset_type = std::integral_constant<int, N>;
220+
221+
template <typename SelectT, int NumSelectedElems, int Stride, int Offset,
222+
typename... ArgsT>
223+
bool run_with_size_stride_offset(ArgsT &&...args) {
224+
bool passed =
225+
for_all_combinations<run_test, SelectT, size_type<NumSelectedElems>,
226+
stride_type<Stride>, offset_type<Offset>>(
227+
std::forward<ArgsT>(args)...);
228+
229+
return passed;
230+
}
231+
232+
template <tested_types TestedTypes, typename SelectT>
233+
bool run_test_for_types(sycl::queue &queue) {
234+
bool passed = true;
235+
constexpr int desired_simd_small_size = 1;
236+
constexpr int desired_simd_large_size = 16;
237+
constexpr int coefficient_of_division = 3;
238+
constexpr int zero_offset_value = 0;
239+
constexpr int small_offset_value = 1;
240+
constexpr int large_offset_value =
241+
desired_simd_large_size - details::ceil(2 * desired_simd_large_size, 3);
242+
243+
const auto small_size = get_dimensions<desired_simd_small_size>();
244+
const auto great_size = get_dimensions<desired_simd_large_size>();
245+
246+
#if SIMD_RUN_TEST_WITH_CHAR_TYPES
247+
const auto types = get_tested_types<TestedTypes>();
248+
#else
249+
const auto types =
250+
named_type_pack<short, unsigned short, int, unsigned int, long,
251+
unsigned long, float, long long,
252+
unsigned long long>::generate("short", "unsigned short",
253+
"int", "unsigned int",
254+
"long", "unsigned long",
255+
"float", "long long",
256+
"unsigned long long");
257+
#endif
258+
259+
// Checks are run for specific combinations of types, sizes, strides and
260+
// offsets.
261+
passed &= run_with_size_stride_offset<SelectT, 1, 1, zero_offset_value>(
262+
types, small_size, queue);
263+
264+
passed &= run_with_size_stride_offset<
265+
SelectT, desired_simd_large_size / coefficient_of_division,
266+
coefficient_of_division, zero_offset_value>(types, great_size, queue);
267+
268+
passed &= run_with_size_stride_offset<
269+
SelectT, desired_simd_large_size / coefficient_of_division,
270+
coefficient_of_division, zero_offset_value>(types, great_size, queue);
271+
272+
passed &= run_with_size_stride_offset<
273+
SelectT, coefficient_of_division,
274+
desired_simd_large_size / coefficient_of_division, zero_offset_value>(
275+
types, great_size, queue);
276+
277+
passed &=
278+
run_with_size_stride_offset<SelectT,
279+
desired_simd_large_size - small_offset_value,
280+
desired_simd_small_size, small_offset_value>(
281+
types, great_size, queue);
282+
283+
passed &= run_with_size_stride_offset<
284+
SelectT, desired_simd_large_size / coefficient_of_division, 2,
285+
large_offset_value>(types, great_size, queue);
286+
287+
return passed;
288+
}
289+
290+
} // namespace esimd_test::api::functional::functions
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//==------- functions_select_lvalue_core.cpp - DPC++ ESIMD on-device test
2+
// ----------------------------------------------------------------==//
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+
// REQUIRES: gpu, level_zero
10+
// XREQUIRES: gpu
11+
// TODO gpu and level_zero in REQUIRES due to only this platforms supported yet.
12+
// The current "REQUIRES" should be replaced with "gpu" only as mentioned in
13+
// "XREQUIRES".
14+
// UNSUPPORTED: cuda, hip
15+
// RUN: %clangxx -fsycl %s -fsycl-device-code-split=per_kernel -o %t.out
16+
// RUN: %GPU_RUN_PLACEHOLDER %t.out
17+
//
18+
// TODO simd<char, 16> fills with unexpected values. The
19+
// SIMD_RUN_TEST_WITH_CHAR_TYPES macros must be enabled when it is resolved.
20+
//
21+
// Test for simd lvalue select function.
22+
// The test creates source simd instance with reference data and then calls
23+
// lvalue select function.
24+
// The test verifies that selected values can be changed with avoid to change
25+
// values, that hasn't beed selected.
26+
27+
#include "functions_1d_select.hpp"
28+
29+
using namespace sycl::ext::intel::experimental::esimd;
30+
using namespace esimd_test::api::functional;
31+
32+
int main(int, char **) {
33+
sycl::queue queue(esimd_test::ESIMDSelector{},
34+
esimd_test::createExceptionHandler());
35+
36+
bool passed =
37+
functions::run_test_for_types<tested_types::core, functions::select_lval>(
38+
queue);
39+
40+
std::cout << (passed ? "=== Test passed\n" : "=== Test FAILED\n");
41+
return passed ? 0 : 1;
42+
}

0 commit comments

Comments
 (0)