Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
unit_testing.cpp
Go to the documentation of this file.
1
2/* This is a unit testing suite for the Vlasiator unified CPU-GPU loop interface.
3 * Compile for host execution with
4 * `nvcc -O3 unit_testing.cpp`
5 * and for device execution with
6 * `nvcc -x cu -O3 -extended-lambda -DUSE_CUDA=1 unit_testing.cpp`
7 */
8
9/* Included standard headers */
10#include <algorithm>
11#include <iostream>
12#include <limits>
13#include <stdint.h>
14#include <stdio.h>
15#include <time.h>
16#include <tuple>
17#include <vector>
18
19/* Include the tested architecture-specific header */
20#include "arch_device_api.h"
21
22/* Host execution of min() and max() require using std namespace */
23using namespace std;
24
25/* Auxiliary function for result evaluation and printing */
26void result_eval(std::tuple<bool, double, double> res, const uint test_id){
27 std::string success = std::get<0>(res) == true ? "PASSED" : "FAILED";
28 printf("Test %d %s - Arch: %9.2f µs, Host: %9.2f µs\n", test_id, success.c_str(), std::get<1>(res), std::get<2>(res));
29}
30
31/* The test functions are all named as `test`, and only differentiated
32 * by their ascending template id number `I`. This allows executing tests
33 * nicely using an array of function pointers, and not calling
34 * each test by a separate name. New tests can be added by inserting
35 * a new function named `test` with the `std::enable_if<I == value, ...`
36 * construct with the next unused value for `I`.
37 */
38template<uint I>
39typename std::enable_if<I == 0, std::tuple<bool, double, double>>::type test(){
40
41 /* The number of reductions per thread */
42 constexpr uint n_redu = 6;
43 /* Problem size normally not known at compile time -> volatile */
44 volatile uint size = 1e8;
45 /* The number of elements along i-dim */
46 const uint ni = size;
47
48 /* Storage for the reduction aggregates */
49 uint sum_arch[n_redu] = {};
50 uint sum_host[n_redu] = {};
51
52 /* Run a timed loop on the chosen architecture */
53 clock_t arch_start = clock();
55 ARCH_LOOP_LAMBDA(uint i, uint *lsum ){
56 for(uint n = 0; n < n_redu; ++n)
57 lsum[n] += (n + 1);
58 }, sum_arch);
59 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
60
61 /* Run a timed loop on the host */
62 clock_t host_start = clock();
63 for (uint i = 0; i < ni; ++i)
64 for(uint n = 0; n < n_redu; ++n)
65 sum_host[n] += (n + 1);
66 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
67
68 /* Create vectors and check for equality */
69 std::vector<uint> v_arch(sum_arch, sum_arch + n_redu);
70 std::vector<uint> v_host(sum_host, sum_host + n_redu);
71 bool success = (v_arch == v_host) ? true : false;
72
73 /* Return the results in tuple */
74 return std::make_tuple(success, arch_time, host_time);
75}
76
77template<uint I>
78typename std::enable_if<I == 1, std::tuple<bool, double, double>>::type test(){
79
80 constexpr uint n_redu = 5;
81 volatile uint size = 1e6;
82 const uint ni = size, nj = size;
83 uint sum_arch[n_redu] = {1, 3, 5, 7, 9};
84 uint sum_host[n_redu] = {1, 3, 5, 7, 9};
85
86 clock_t arch_start = clock();
88 ARCH_LOOP_LAMBDA(uint i, uint j, uint *lsum ){
89 for(uint n = 0; n < n_redu; ++n)
90 lsum[n] += (n + 1);
91 }, sum_arch);
92 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
93
94 clock_t host_start = clock();
95 for (uint j = 0; j < nj; ++j)
96 for (uint i = 0; i < ni; ++i)
97 for(uint n = 0; n < n_redu; ++n)
98 sum_host[n] += (n + 1);
99 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
100
101 std::vector<uint> v_arch(sum_arch, sum_arch + n_redu);
102 std::vector<uint> v_host(sum_host, sum_host + n_redu);
103 bool success = (v_arch == v_host) ? true : false;
104
105 return std::make_tuple(success, arch_time, host_time);
106}
107
108template<uint I>
109typename std::enable_if<I == 2, std::tuple<bool, double, double>>::type test(){
110
111 constexpr uint n_redu = 5;
112 volatile uint size = 1e6;
113 const uint ni = size, nj = size;
114 uint sum_arch[n_redu] = {11, 22, 33, 44, 55};
115 uint sum_host[n_redu] = {11, 22, 33, 44, 55};
116
117 clock_t arch_start = clock();
119 ARCH_LOOP_LAMBDA(uint i, uint j, uint *lsum ){
120 const uint val = 2;
121 ARCH_INNER_BODY(i, j, lsum) {
122 for(uint n = 0; n < n_redu; ++n)
123 lsum[n] += (n + val);
124 };
125 }, sum_arch);
126 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
127
128 clock_t host_start = clock();
129 for (uint j = 0; j < nj; ++j){
130 const uint val = 2;
131 for (uint i = 0; i < ni; ++i)
132 for(uint n = 0; n < n_redu; ++n)
133 sum_host[n] += (n + val);
134 }
135 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
136
137 std::vector<uint> v_arch(sum_arch, sum_arch + n_redu);
138 std::vector<uint> v_host(sum_host, sum_host + n_redu);
139 bool success = (v_arch == v_host) ? true : false;
140
141 return std::make_tuple(success, arch_time, host_time);
142}
143
144template<uint I>
145typename std::enable_if<I == 3, std::tuple<bool, double, double>>::type test(){
146
147 constexpr uint n_redu = 4;
148 volatile uint size = 5 * 1e3;
149 const uint ni = size, nj = size, nk = size;
150 std::vector<uint> sum_arch(n_redu, 0.0);
151 std::vector<uint> sum_host(n_redu, 0.0);
152
153 clock_t arch_start = clock();
155 ARCH_LOOP_LAMBDA(uint i, uint j, uint k, uint *lsum ){
156 for(uint n = 0; n < n_redu; ++n)
157 lsum[n] += (n + 1);
158 }, sum_arch);
159 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
160
161 clock_t host_start = clock();
162 for (uint k = 0; k < nk; ++k)
163 for (uint j = 0; j < nj; ++j)
164 for (uint i = 0; i < ni; ++i)
165 for(uint n = 0; n < n_redu; ++n)
166 sum_host[n] += (n + 1);
167 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
168
169 bool success = (sum_arch == sum_host) ? true : false;
170
171 return std::make_tuple(success, arch_time, host_time);
172}
173
174template<uint I>
175typename std::enable_if<I == 4, std::tuple<bool, double, double>>::type test(){
176
177 constexpr uint n_redu = 4;
178 volatile uint size = 5 * 1e3;
179 const uint ni = size, nj = size, nk = size;
180 std::vector<uint> sum_arch(n_redu, 0.0);
181 std::vector<uint> sum_host(n_redu, 0.0);
182
183 clock_t arch_start = clock();
185 ARCH_LOOP_LAMBDA(uint i, uint j, uint k, uint *lsum ){
186 const uint val = 2;
187 ARCH_INNER_BODY(i, j, k, lsum) {
188 for(uint n = 0; n < n_redu; ++n)
189 lsum[n] += (n + val);
190 };
191 }, sum_arch);
192 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
193
194 clock_t host_start = clock();
195 for (uint k = 0; k < nk; ++k){
196 const uint val = 2;
197 for (uint j = 0; j < nj; ++j)
198 for (uint i = 0; i < ni; ++i)
199 for(uint n = 0; n < n_redu; ++n)
200 sum_host[n] += (n + val);
201 }
202 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
203
204 bool success = (sum_arch == sum_host) ? true : false;
205
206 return std::make_tuple(success, arch_time, host_time);
207}
208
209template<uint I>
210typename std::enable_if<I == 5, std::tuple<bool, double, double>>::type test(){
211
212 constexpr uint n_redu = 3;
213 volatile uint size = 1e3;
214 const uint ni = size, nj = size, nk = size, nl = size;
215 uint sum_arch[n_redu] = {};
216 uint sum_host[n_redu] = {};
217
218 clock_t arch_start = clock();
219 arch::parallel_reduce<arch::sum>({ni, nj, nk, nl},
220 ARCH_LOOP_LAMBDA(uint i, uint j, uint k, uint l, uint *lsum ){
221 for(uint n = 0; n < n_redu; ++n)
222 lsum[n] += (n + 1);
223 }, sum_arch);
224 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
225
226 clock_t host_start = clock();
227 for (uint l = 0; l < nl; ++l)
228 for (uint k = 0; k < nk; ++k)
229 for (uint j = 0; j < nj; ++j)
230 for (uint i = 0; i < ni; ++i)
231 for(uint n = 0; n < n_redu; ++n)
232 sum_host[n] += (n + 1);
233 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
234
235 std::vector<uint> v_arch(sum_arch, sum_arch + n_redu);
236 std::vector<uint> v_host(sum_host, sum_host + n_redu);
237 bool success = (v_arch == v_host) ? true : false;
238
239 return std::make_tuple(success, arch_time, host_time);
240}
241
242template<uint I>
243typename std::enable_if<I == 6, std::tuple<bool, double, double>>::type test(){
244
245 constexpr uint n_redu = 3;
246 volatile uint size = 1e3;
247 const uint ni = size, nj = size, nk = size, nl = size;
248 uint sum_arch[n_redu] = {};
249 uint sum_host[n_redu] = {};
250
251 clock_t arch_start = clock();
252 arch::parallel_reduce<arch::sum>({ni, nj, nk, nl},
253 ARCH_LOOP_LAMBDA(uint i, uint j, uint k, uint l, uint *lsum ){
254 const uint val = 2;
255 ARCH_INNER_BODY(i, j, k, l, lsum) {
256 for(uint n = 0; n < n_redu; ++n)
257 lsum[n] += (n + val);
258 };
259 }, sum_arch);
260 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
261
262 clock_t host_start = clock();
263 for (uint l = 0; l < nl; ++l){
264 const uint val = 2;
265 for (uint k = 0; k < nk; ++k)
266 for (uint j = 0; j < nj; ++j)
267 for (uint i = 0; i < ni; ++i)
268 for(uint n = 0; n < n_redu; ++n)
269 sum_host[n] += (n + val);
270 }
271 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
272
273 std::vector<uint> v_arch(sum_arch, sum_arch + n_redu);
274 std::vector<uint> v_host(sum_host, sum_host + n_redu);
275 bool success = (v_arch == v_host) ? true : false;
276
277 return std::make_tuple(success, arch_time, host_time);
278}
279
280template<uint I>
281typename std::enable_if<I == 7, std::tuple<bool, double, double>>::type test(){
282
283 volatile uint size = 1e8;
284 const uint ni = size;
285 uint max_arch = std::numeric_limits<uint>::min();
286 uint max_host = std::numeric_limits<uint>::min();
287
288 uint *data = (uint*)arch::allocate(size * sizeof(uint));
289 for(uint n = 0; n < size; ++n)
290 data[n] = n;
291
292 clock_t arch_start = clock();
294 ARCH_LOOP_LAMBDA(uint i, uint *lmax ){
295 *lmax = max(data[i], *lmax);
296 }, max_arch);
297 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
298
299 clock_t host_start = clock();
300 for (uint i = 0; i < ni; ++i)
301 max_host = max(data[i], max_host);
302 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
303 arch::free(data);
304
305 std::vector<uint> v_arch(&max_arch, &max_arch + 1);
306 std::vector<uint> v_host(&max_host, &max_host + 1);
307 bool success = (v_arch == v_host) ? true : false;
308
309 return std::make_tuple(success, arch_time, host_time);
310}
311
312template<uint I>
313typename std::enable_if<I == 8, std::tuple<bool, double, double>>::type test(){
314
315 volatile uint size = 1e4;
316 const uint ni = size, nj = size;
317 int min_arch = std::numeric_limits<int>::max();
318 int min_host = std::numeric_limits<int>::max();
319
320 int *data = (int*)arch::allocate(size * size * sizeof(int));
321 for(uint n = 0; n < size * size; ++n)
322 data[n] = -(int)n;
323
324 clock_t arch_start = clock();
326 ARCH_LOOP_LAMBDA(uint i, uint j, int *lmin ){
327 const uint idx = ni * j;
328 ARCH_INNER_BODY(i, j, lmin) {
329 *lmin = min(data[idx + i], *lmin);
330 };
331 }, min_arch);
332 double arch_time = (double)((clock() - arch_start) * 1e6 / CLOCKS_PER_SEC);
333
334 clock_t host_start = clock();
335 for (uint j = 0; j < nj; ++j){
336 const uint idx = ni * j;
337 for (uint i = 0; i < ni; ++i)
338 min_host = min(data[idx + i], min_host);
339 }
340 double host_time = (double)((clock() - host_start) * 1e6 / CLOCKS_PER_SEC);
341 arch::free(data);
342
343 std::vector<int> v_arch(&min_arch, &min_arch + 1);
344 std::vector<int> v_host(&min_host, &min_host + 1);
345 bool success = (v_arch == v_host) ? true : false;
346
347 return std::make_tuple(success, arch_time, host_time);
348}
349
350/* Instantiate each test function by recursively calling the
351 * driver function in a descending order beginning from `N - 1`
352 */
353template<uint N, uint I>
355 static void driver(std::tuple<bool, double, double>(*fptr_test[N])()){
356 fptr_test[I - 1] = &test<I - 1>;
358 }
359};
360
361/* Specialization for the instantiation end condition `I = 0` */
362template<uint N>
363struct test_instatiator<N, 0> {
364 static void driver(std::tuple<bool, double, double>(*fptr_test[N])()){}
365};
366
367/* The main function */
368int main(){
369
370 /* Specify the number of tests and set function pointers */
371 constexpr uint n_tests = 9;
372 std::tuple<bool, double, double>(*fptr_test[n_tests])();
374
375 /* Indicate for what backend option the test suite is compiled */
376#ifdef USE_CUDA
377 printf("Run tests for Arch = CUDA (USE_CUDA defined)\n");
378#else
379 printf("Run tests for Arch = HOST (USE_CUDA not defined)\n");
380#endif
381
382 /* Evaluate all test cases using the array of function pointers */
383 for(uint i = 0; i < n_tests; i++)
384 result_eval(fptr_test[i](), i);
385}
for i
Definition Dispersion.m:24
#define ARCH_INNER_BODY(...)
#define ARCH_LOOP_LAMBDA
const int j
const int k
function res
Definition hamming.m:1
__host__ static __forceinline__ void * allocate(size_t bytes)
static void parallel_reduce(const uint(&limits)[NDim], Lambda loop_body, T &sum)
__host__ static __forceinline__ void free(T *ptr)
static void driver(std::tuple< bool, double, double >(*fptr_test[N])())
static void driver(std::tuple< bool, double, double >(*fptr_test[N])())
std::enable_if< I==0, std::tuple< bool, double, double > >::type test()
void result_eval(std::tuple< bool, double, double > res, const uint test_id)
int main()
static ARCH_HOSTDEV VecSimple< T > min(VecSimple< T > const &l, VecSimple< T > const &r)
static ARCH_HOSTDEV VecSimple< T > max(VecSimple< T > const &l, VecSimple< T > const &r)