Skip to content

Commit 32bf607

Browse files
authored
[SYCL][ESIMD] Add on-device tests (#2222)
Add ESIMD tests which can run on Intel GPU devices. Authors: Gang Chen <[email protected]> Kaiyu Chen <[email protected]> Pratik Ashar <[email protected]> Konstantin S Bobrovsky <[email protected]> Signed-off-by: Konstantin S Bobrovsky <[email protected]>
1 parent 0f33f7a commit 32bf607

27 files changed

+5561
-0
lines changed

sycl/test/esimd/on-device/BitonicSortK.cpp

Lines changed: 687 additions & 0 deletions
Large diffs are not rendered by default.

sycl/test/esimd/on-device/BitonicSortKv2.cpp

Lines changed: 602 additions & 0 deletions
Large diffs are not rendered by default.

sycl/test/esimd/on-device/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This directory contains ESIMD tests which are run on Intel GPU device only.
2+
Some of them can run on host device too, but in general it is not always
3+
possible as some of ESIMD APIs (e.g. memory access via accessors) is not
4+
implemented for the host device.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//==---------------- accessor.cpp - DPC++ ESIMD on-device test ------------==//
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+
// TODO enable on Windows
9+
// REQUIRES: linux
10+
// REQUIRES: gpu
11+
// RUN: %clangxx-esimd -fsycl -D_CRT_SECURE_NO_WARNINGS=1 %s -o %t.out
12+
// RUN: %ESIMD_RUN_PLACEHOLDER %t.out
13+
14+
// This test checks that accessor-based memory accesses work correctly in ESIMD.
15+
16+
#include "esimd_test_utils.hpp"
17+
18+
#include <CL/sycl.hpp>
19+
#include <CL/sycl/intel/esimd.hpp>
20+
21+
#include <iostream>
22+
23+
using namespace cl::sycl;
24+
25+
constexpr unsigned int VL = 1024 * 128;
26+
27+
using Ty = float;
28+
29+
int main() {
30+
Ty data0[VL] = {0};
31+
Ty data1[VL] = {0};
32+
constexpr Ty VAL = 5;
33+
34+
for (int i = 0; i < VL; i++) {
35+
data0[i] = i;
36+
}
37+
38+
try {
39+
queue q(esimd_test::ESIMDSelector{}, esimd_test::createExceptionHandler());
40+
41+
buffer<Ty, 1> buf0(data0, range<1>(VL));
42+
buffer<Ty, 1> buf1(data1, range<1>(VL));
43+
44+
q.submit([&](handler &cgh) {
45+
std::cout << "Running on "
46+
<< q.get_device().get_info<cl::sycl::info::device::name>()
47+
<< "\n";
48+
49+
auto acc0 = buf0.get_access<access::mode::read_write>(cgh);
50+
auto acc1 = buf1.get_access<access::mode::write>(cgh);
51+
52+
cgh.parallel_for<class Test>(
53+
range<1>(1), [=](sycl::id<1> i) SYCL_ESIMD_KERNEL {
54+
using namespace sycl::intel::gpu;
55+
unsigned int offset = 0;
56+
for (int k = 0; k < VL / 16; k++) {
57+
simd<Ty, 16> var = block_load<Ty, 16>(acc0, offset);
58+
var += VAL;
59+
block_store(acc0, offset, var);
60+
block_store(acc1, offset, var + 1);
61+
offset += 64;
62+
}
63+
});
64+
});
65+
66+
q.wait();
67+
68+
} catch (cl::sycl::exception const &e) {
69+
std::cout << "SYCL exception caught: " << e.what() << '\n';
70+
return 2;
71+
}
72+
73+
int err_cnt = 0;
74+
75+
for (int i = 0; i < VL; i++) {
76+
Ty gold0 = i + VAL;
77+
Ty gold1 = gold0 + 1;
78+
Ty val0 = data0[i];
79+
Ty val1 = data1[i];
80+
81+
if (val0 != gold0) {
82+
if (++err_cnt < 10)
83+
std::cerr << "*** ERROR at data0[" << i << "]: " << val0
84+
<< " != " << gold0 << "(gold)\n";
85+
}
86+
if (val1 != gold1) {
87+
if (++err_cnt < 10)
88+
std::cerr << "*** ERROR at data1[" << i << "]: " << val1
89+
<< " != " << gold1 << "(gold)\n";
90+
}
91+
}
92+
if (err_cnt == 0) {
93+
std::cout << "Passed\n";
94+
return 0;
95+
} else {
96+
std::cout << "Failed: " << err_cnt << " of " << VL << " errors\n";
97+
return 1;
98+
}
99+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//==--------- esimd_test_utils.hpp - DPC++ ESIMD on-device test utilities --==//
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+
#include <CL/sycl.hpp>
10+
11+
#define NOMINMAX
12+
13+
#include <algorithm>
14+
#include <cstring>
15+
#include <fstream>
16+
#include <iostream>
17+
#include <iterator>
18+
#include <vector>
19+
20+
using namespace cl::sycl;
21+
22+
namespace esimd_test {
23+
24+
// This is the class provided to SYCL runtime by the application to decide
25+
// on which device to run, or whether to run at all.
26+
// When selecting a device, SYCL runtime first takes (1) a selector provided by
27+
// the program or a default one and (2) the set of all available devices. Then
28+
// it passes each device to the '()' operator of the selector. Device, for
29+
// which '()' returned the highest number, is selected. If a negative number
30+
// was returned for all devices, then the selection process will cause an
31+
// exception.
32+
class ESIMDSelector : public device_selector {
33+
// Require GPU device unless HOST is requested in SYCL_DEVICE_TYPE env
34+
virtual int operator()(const device &device) const {
35+
if (const char *dev_type = getenv("SYCL_DEVICE_TYPE")) {
36+
if (!strcmp(dev_type, "GPU"))
37+
return device.is_gpu() ? 1000 : -1;
38+
if (!strcmp(dev_type, "HOST"))
39+
return device.is_host() ? 1000 : -1;
40+
std::cerr << "Supported 'SYCL_DEVICE_TYPE' env var values are 'GPU' and "
41+
"'HOST', '"
42+
<< dev_type << "' is not.\n";
43+
return -1;
44+
}
45+
// If "SYCL_DEVICE_TYPE" not defined, only allow gpu device
46+
return device.is_gpu() ? 1000 : -1;
47+
}
48+
};
49+
50+
inline auto createExceptionHandler() {
51+
return [](exception_list l) {
52+
for (auto ep : l) {
53+
try {
54+
std::rethrow_exception(ep);
55+
} catch (cl::sycl::exception &e0) {
56+
std::cout << "sycl::exception: " << e0.what() << std::endl;
57+
} catch (std::exception &e) {
58+
std::cout << "std::exception: " << e.what() << std::endl;
59+
} catch (...) {
60+
std::cout << "generic exception\n";
61+
}
62+
}
63+
};
64+
}
65+
66+
template <typename T>
67+
std::vector<T> read_binary_file(const char *fname, size_t num = 0) {
68+
std::vector<T> vec;
69+
std::ifstream ifs(fname, std::ios::in | std::ios::binary);
70+
if (ifs.good()) {
71+
ifs.unsetf(std::ios::skipws);
72+
std::streampos file_size;
73+
ifs.seekg(0, std::ios::end);
74+
file_size = ifs.tellg();
75+
ifs.seekg(0, std::ios::beg);
76+
size_t max_num = file_size / sizeof(T);
77+
vec.resize(num ? (std::min)(max_num, num) : max_num);
78+
ifs.read(reinterpret_cast<char *>(vec.data()), vec.size() * sizeof(T));
79+
}
80+
return vec;
81+
}
82+
83+
template <typename T>
84+
bool write_binary_file(const char *fname, const std::vector<T> &vec,
85+
size_t num = 0) {
86+
std::ofstream ofs(fname, std::ios::out | std::ios::binary);
87+
if (ofs.good()) {
88+
ofs.write(reinterpret_cast<const char *>(&vec[0]),
89+
(num ? num : vec.size()) * sizeof(T));
90+
ofs.close();
91+
}
92+
return !ofs.bad();
93+
}
94+
95+
template <typename T>
96+
bool cmp_binary_files(const char *fname1, const char *fname2, T tolerance) {
97+
const auto vec1 = read_binary_file<T>(fname1);
98+
const auto vec2 = read_binary_file<T>(fname2);
99+
if (vec1.size() != vec2.size()) {
100+
std::cerr << fname1 << " size is " << vec1.size();
101+
std::cerr << " whereas " << fname2 << " size is " << vec2.size()
102+
<< std::endl;
103+
return false;
104+
}
105+
for (size_t i = 0; i < vec1.size(); i++) {
106+
if (abs(vec1[i] - vec2[i]) > tolerance) {
107+
std::cerr << "Mismatch at " << i << ' ';
108+
if (sizeof(T) == 1) {
109+
std::cerr << (int)vec1[i] << " vs " << (int)vec2[i] << std::endl;
110+
} else {
111+
std::cerr << vec1[i] << " vs " << vec2[i] << std::endl;
112+
}
113+
return false;
114+
}
115+
}
116+
return true;
117+
}
118+
119+
// dump every element of sequence [first, last) to std::cout
120+
template <typename ForwardIt> void dump_seq(ForwardIt first, ForwardIt last) {
121+
using ValueT = typename std::iterator_traits<ForwardIt>::value_type;
122+
std::copy(first, last, std::ostream_iterator<ValueT>{std::cout, " "});
123+
std::cout << std::endl;
124+
}
125+
126+
// Checks wether ranges [first, last) and [ref_first, ref_last) are equal.
127+
// If a mismatch is found, dumps elements that differ and returns true,
128+
// otherwise false is returned.
129+
template <typename ForwardIt, typename RefForwardIt, typename BinaryPredicateT>
130+
bool check_fail_seq(ForwardIt first, ForwardIt last, RefForwardIt ref_first,
131+
RefForwardIt ref_last, BinaryPredicateT is_equal) {
132+
auto mism = std::mismatch(first, last, ref_first, is_equal);
133+
if (mism.first != last) {
134+
std::cout << "mismatch: returned " << *mism.first << std::endl;
135+
std::cout << " expected " << *mism.second << std::endl;
136+
return true;
137+
}
138+
return false;
139+
}
140+
141+
template <typename ForwardIt, typename RefForwardIt>
142+
bool check_fail_seq(ForwardIt first, ForwardIt last, RefForwardIt ref_first,
143+
RefForwardIt ref_last) {
144+
return check_fail_seq(
145+
first, last, ref_first, ref_last,
146+
[](const auto &lhs, const auto &rhs) { return lhs == rhs; });
147+
}
148+
149+
// analog to C++20 bit_cast
150+
template <typename To, typename From,
151+
typename std::enable_if<(sizeof(To) == sizeof(From)) &&
152+
std::is_trivially_copyable<From>::value &&
153+
std::is_trivial<To>::value,
154+
int>::type = 0>
155+
To bit_cast(const From &src) noexcept {
156+
To dst;
157+
std::memcpy(&dst, &src, sizeof(To));
158+
return dst;
159+
}
160+
161+
} // namespace esimd_test

0 commit comments

Comments
 (0)