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

Commit 5464870

Browse files
authored
[SYCL][ESIMD] Add a runtime test for BFN function (#1671)
1 parent 2b11eb1 commit 5464870

File tree

1 file changed

+259
-0
lines changed

1 file changed

+259
-0
lines changed

SYCL/ESIMD/bfn.cpp

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
//==---------------- bfn.cpp - DPC++ ESIMD binary function test ------------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM
4+
// Exceptions. See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// REQUIRES: intel-gpu-dg2 || intel-gpu-pvc
9+
// RUN: %clangxx -fsycl-device-code-split=per_kernel -fsycl %s -o %t.out
10+
// RUN: %GPU_RUN_PLACEHOLDER %t.out
11+
12+
// This test checks binary function (bfn) operations. Combinations of
13+
// - argument type - uint16_t, uint32_t.
14+
// - binary function - several binary functins with three operands (~, &, |, ^).
15+
16+
#include "esimd_test_utils.hpp"
17+
18+
#include <sycl/ext/intel/esimd.hpp>
19+
#include <sycl/sycl.hpp>
20+
21+
#include <iostream>
22+
23+
using namespace sycl;
24+
using namespace sycl::ext::intel;
25+
26+
// --- Initialization function for source operands of binary functions.
27+
28+
template <class T> struct InitOps {
29+
void operator()(T *In0, T *In1, T *In2, T *Out, size_t Size) const {
30+
for (auto I = 0; I < Size; ++I) {
31+
In0[I] = I * 3;
32+
In1[I] = I * 3 + 1;
33+
In2[I] = I * 3 + 2;
34+
Out[I] = (T)0;
35+
}
36+
}
37+
};
38+
39+
// --- Test boolean control functions.
40+
41+
using bfn_t = experimental::esimd::bfn_t;
42+
43+
constexpr experimental::esimd::bfn_t F1 = bfn_t::x | bfn_t::y | bfn_t::z;
44+
constexpr experimental::esimd::bfn_t F2 = bfn_t::x & bfn_t::y & bfn_t::z;
45+
constexpr experimental::esimd::bfn_t F3 = ~bfn_t::x | bfn_t::y ^ bfn_t::z;
46+
47+
// --- Template functions calculating given boolean operation on host and device
48+
49+
enum ArgKind {
50+
AllVec,
51+
AllSca,
52+
};
53+
54+
template <class T, experimental::esimd::bfn_t Op> struct HostFunc;
55+
56+
#define DEFINE_HOST_OP(FUNC_CTRL) \
57+
template <class T> struct HostFunc<T, FUNC_CTRL> { \
58+
T operator()(T X0, T X1, T X2) { \
59+
T res = 0; \
60+
for (unsigned i = 0; i < sizeof(X0) * 8; i++) { \
61+
T mask = 0x1UL << i; \
62+
res = (res & ~mask) | \
63+
((static_cast<uint8_t>(FUNC_CTRL) >> \
64+
((((X0 >> i) & 0x1UL)) + (((X1 >> i) & 0x1UL) << 1) + \
65+
(((X2 >> i) & 0x1UL) << 2)) & \
66+
0x1UL) \
67+
<< i); \
68+
} \
69+
return res; \
70+
} \
71+
};
72+
73+
DEFINE_HOST_OP(F1);
74+
DEFINE_HOST_OP(F2);
75+
DEFINE_HOST_OP(F3);
76+
77+
// --- Specializations per each boolean operation.
78+
79+
template <class T, int N, experimental::esimd::bfn_t Op, int Args = AllVec>
80+
struct ESIMDf;
81+
82+
#define DEFINE_ESIMD_DEVICE_OP(FUNC_CTRL) \
83+
template <class T, int N> struct ESIMDf<T, N, FUNC_CTRL, AllVec> { \
84+
esimd::simd<T, N> \
85+
operator()(esimd::simd<T, N> X0, esimd::simd<T, N> X1, \
86+
esimd::simd<T, N> X2) const SYCL_ESIMD_FUNCTION { \
87+
return experimental::esimd::bfn<FUNC_CTRL, T, N>(X0, X1, X2); \
88+
} \
89+
}; \
90+
template <class T, int N> struct ESIMDf<T, N, FUNC_CTRL, AllSca> { \
91+
esimd::simd<T, N> operator()(T X0, T X1, T X2) const SYCL_ESIMD_FUNCTION { \
92+
return experimental::esimd::bfn<FUNC_CTRL, T, N>(X0, X1, X2); \
93+
} \
94+
};
95+
96+
DEFINE_ESIMD_DEVICE_OP(F1);
97+
DEFINE_ESIMD_DEVICE_OP(F2);
98+
DEFINE_ESIMD_DEVICE_OP(F3);
99+
100+
// --- Generic kernel calculating a binary function operation on array elements.
101+
102+
template <class T, int N, experimental::esimd::bfn_t Op,
103+
template <class, int, experimental::esimd::bfn_t, int> class Kernel>
104+
struct DeviceFunc {
105+
const T *In0, *In1, *In2;
106+
T *Out;
107+
108+
DeviceFunc(const T *In0, const T *In1, const T *In2, T *Out)
109+
: In0(In0), In1(In1), In2(In2), Out(Out) {}
110+
111+
void operator()(id<1> I) const SYCL_ESIMD_KERNEL {
112+
unsigned int Offset = I * N;
113+
esimd::simd<T, N> V0;
114+
esimd::simd<T, N> V1;
115+
esimd::simd<T, N> V2;
116+
V0.copy_from(In0 + Offset);
117+
V1.copy_from(In1 + Offset);
118+
V2.copy_from(In2 + Offset);
119+
120+
if (I.get(0) % 2 == 0) {
121+
for (int J = 0; J < N; J++) {
122+
Kernel<T, N, Op, AllSca> DevF{};
123+
T Val0 = V0[J];
124+
T Val1 = V1[J];
125+
T Val2 = V2[J];
126+
esimd::simd<T, N> V = DevF(Val0, Val1, Val2); // scalar arg
127+
V0[J] = V[J];
128+
}
129+
} else {
130+
Kernel<T, N, Op, AllVec> DevF{};
131+
V0 = DevF(V0, V1, V2); // vector arg
132+
}
133+
V0.copy_to(Out + Offset);
134+
};
135+
};
136+
137+
// --- Generic test function for boolean function.
138+
139+
template <class T, int N, experimental::esimd::bfn_t Op, int Range,
140+
template <class, int, experimental::esimd::bfn_t, int> class Kernel,
141+
typename InitF = InitOps<T>>
142+
bool test(queue &Q, const std::string &Name, InitF Init = InitOps<T>{}) {
143+
constexpr size_t Size = Range * N;
144+
145+
auto UA = esimd_test::usm_malloc_shared<T>(Q, Size);
146+
T *A = UA.get();
147+
auto UB = esimd_test::usm_malloc_shared<T>(Q, Size);
148+
T *B = UB.get();
149+
auto UC = esimd_test::usm_malloc_shared<T>(Q, Size);
150+
T *C = UC.get();
151+
auto UD = esimd_test::usm_malloc_shared<T>(Q, Size);
152+
T *D = UD.get();
153+
Init(A, B, C, D, Size);
154+
155+
std::cout << " " << Name << " test"
156+
<< "...\n";
157+
158+
try {
159+
// number of workgroups
160+
sycl::range<1> GlobalRange{Range};
161+
162+
// threads (workitems) in each workgroup
163+
sycl::range<1> LocalRange{1};
164+
165+
auto E = Q.submit([=](handler &CGH) {
166+
DeviceFunc<T, N, Op, Kernel> F(A, B, C, D);
167+
CGH.parallel_for(nd_range<1>{GlobalRange, LocalRange}, F);
168+
});
169+
E.wait();
170+
} catch (sycl::exception &Exc) {
171+
std::cout << " *** ERROR. SYCL exception caught: << " << Exc.what()
172+
<< "\n";
173+
return false;
174+
}
175+
176+
int ErrCnt = 0;
177+
178+
for (unsigned I = 0; I < Size; ++I) {
179+
T Gold;
180+
181+
Gold = HostFunc<T, Op>{}((T)A[I], (T)B[I], (T)C[I]);
182+
T Test = D[I];
183+
184+
if (Test != Gold) {
185+
if (++ErrCnt < 10) {
186+
std::cout << "\tfailed at index " << I << ", " << std::hex << Test
187+
<< " != " << Gold << " (gold); "
188+
<< "Input was: " << (T)A[I] << ", " << (T)B[I] << ", "
189+
<< (T)C[I] << "; "
190+
<< "FuncCtrl: " << int(Op) << std::dec << "\n";
191+
}
192+
}
193+
}
194+
195+
if (ErrCnt > 0) {
196+
std::cout << " pass rate: "
197+
<< ((float)(Size - ErrCnt) / (float)Size) * 100.0f << "% ("
198+
<< (Size - ErrCnt) << "/" << Size << ")\n";
199+
}
200+
201+
std::cout << (ErrCnt > 0 ? " FAILED\n" : " Passed\n");
202+
return ErrCnt == 0;
203+
}
204+
205+
// --- Tests all boolean operations with given vector length.
206+
207+
template <class T, int N, int Range> bool testESIMD(queue &Q) {
208+
bool Pass = true;
209+
210+
std::cout << "--- TESTING ESIMD functions, T=" << typeid(T).name()
211+
<< ", N = " << N << ", Range: " << Range << "...\n";
212+
213+
Pass &= test<T, N, F1, Range, ESIMDf>(Q, "F1");
214+
Pass &= test<T, N, F2, Range, ESIMDf>(Q, "F2");
215+
Pass &= test<T, N, F3, Range, ESIMDf>(Q, "F3");
216+
return Pass;
217+
}
218+
219+
template <class T, int N> bool testESIMDRanges(queue &Q) {
220+
bool Pass = true;
221+
// Test vector API.
222+
Pass &= testESIMD<T, N, 128>(Q);
223+
// Test scalar API with odd size.
224+
Pass &= testESIMD<T, N, 101>(Q);
225+
return Pass;
226+
}
227+
228+
template <class T> bool testESIMDGroup(queue &Q) {
229+
bool Pass = true;
230+
Pass &= testESIMDRanges<T, 1>(Q);
231+
Pass &= testESIMDRanges<T, 5>(Q);
232+
Pass &= testESIMDRanges<T, 8>(Q);
233+
Pass &= testESIMDRanges<T, 16>(Q);
234+
Pass &= testESIMDRanges<T, 32>(Q);
235+
return Pass;
236+
}
237+
238+
// --- The entry point.
239+
240+
int main(void) {
241+
queue Q(esimd_test::ESIMDSelector, esimd_test::createExceptionHandler());
242+
auto Dev = Q.get_device();
243+
std::cout << "Running on " << Dev.get_info<sycl::info::device::name>()
244+
<< "\n";
245+
bool Pass = true;
246+
247+
Pass &= testESIMDGroup<uint16_t>(Q);
248+
Pass &= testESIMDGroup<uint32_t>(Q);
249+
Pass &= testESIMDGroup<int16_t>(Q);
250+
Pass &= testESIMDGroup<int32_t>(Q);
251+
252+
Pass &= testESIMDGroup<uint8_t>(Q);
253+
Pass &= testESIMDGroup<int8_t>(Q);
254+
Pass &= testESIMDGroup<uint64_t>(Q);
255+
Pass &= testESIMDGroup<int64_t>(Q);
256+
257+
std::cout << (Pass ? "Test Passed\n" : "Test FAILED\n");
258+
return Pass ? 0 : 1;
259+
}

0 commit comments

Comments
 (0)