1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #define EIGEN_TEST_NO_LONGDOUBLE
11 #define EIGEN_TEST_NO_COMPLEX
12 #define EIGEN_TEST_FUNC cxx11_tensor_of_float16_cuda
13 #define EIGEN_DEFAULT_DENSE_INDEX_TYPE int
14 #define EIGEN_USE_GPU
15 
16 #include "main.h"
17 #include <unsupported/Eigen/CXX11/Tensor>
18 
19 using Eigen::Tensor;
20 
21 template<typename>
test_cuda_numext()22 void test_cuda_numext() {
23   Eigen::CudaStreamDevice stream;
24   Eigen::GpuDevice gpu_device(&stream);
25   int num_elem = 101;
26 
27   float* d_float = (float*)gpu_device.allocate(num_elem * sizeof(float));
28   bool* d_res_half = (bool*)gpu_device.allocate(num_elem * sizeof(bool));
29   bool* d_res_float = (bool*)gpu_device.allocate(num_elem * sizeof(bool));
30 
31   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float(
32       d_float, num_elem);
33   Eigen::TensorMap<Eigen::Tensor<bool, 1>, Eigen::Aligned> gpu_res_half(
34       d_res_half, num_elem);
35   Eigen::TensorMap<Eigen::Tensor<bool, 1>, Eigen::Aligned> gpu_res_float(
36       d_res_float, num_elem);
37 
38   gpu_float.device(gpu_device) = gpu_float.random() - gpu_float.constant(0.5f);
39   gpu_res_float.device(gpu_device) = gpu_float.unaryExpr(Eigen::internal::scalar_isnan_op<float>());
40   gpu_res_half.device(gpu_device) = gpu_float.cast<Eigen::half>().unaryExpr(Eigen::internal::scalar_isnan_op<Eigen::half>());
41 
42   Tensor<bool, 1> half_prec(num_elem);
43   Tensor<bool, 1> full_prec(num_elem);
44   gpu_device.memcpyDeviceToHost(half_prec.data(), d_res_half, num_elem*sizeof(bool));
45   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, num_elem*sizeof(bool));
46   gpu_device.synchronize();
47 
48   for (int i = 0; i < num_elem; ++i) {
49     std::cout << "Checking numext " << i << std::endl;
50     VERIFY_IS_EQUAL(full_prec(i), half_prec(i));
51   }
52 
53   gpu_device.deallocate(d_float);
54   gpu_device.deallocate(d_res_half);
55   gpu_device.deallocate(d_res_float);
56 }
57 
58 
59 #ifdef EIGEN_HAS_CUDA_FP16
60 
61 template<typename>
test_cuda_conversion()62 void test_cuda_conversion() {
63   Eigen::CudaStreamDevice stream;
64   Eigen::GpuDevice gpu_device(&stream);
65   int num_elem = 101;
66 
67   float* d_float = (float*)gpu_device.allocate(num_elem * sizeof(float));
68   Eigen::half* d_half = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
69   float* d_conv = (float*)gpu_device.allocate(num_elem * sizeof(float));
70 
71   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float(
72       d_float, num_elem);
73   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_half(
74       d_half, num_elem);
75   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_conv(
76       d_conv, num_elem);
77 
78   gpu_float.device(gpu_device) = gpu_float.random();
79   gpu_half.device(gpu_device) = gpu_float.cast<Eigen::half>();
80   gpu_conv.device(gpu_device) = gpu_half.cast<float>();
81 
82   Tensor<float, 1> initial(num_elem);
83   Tensor<float, 1> final(num_elem);
84   gpu_device.memcpyDeviceToHost(initial.data(), d_float, num_elem*sizeof(float));
85   gpu_device.memcpyDeviceToHost(final.data(), d_conv, num_elem*sizeof(float));
86 
87   for (int i = 0; i < num_elem; ++i) {
88     VERIFY_IS_APPROX(initial(i), final(i));
89   }
90 
91   gpu_device.deallocate(d_float);
92   gpu_device.deallocate(d_half);
93   gpu_device.deallocate(d_conv);
94 }
95 
96 template<typename>
test_cuda_unary()97 void test_cuda_unary() {
98   Eigen::CudaStreamDevice stream;
99   Eigen::GpuDevice gpu_device(&stream);
100   int num_elem = 101;
101 
102   float* d_float = (float*)gpu_device.allocate(num_elem * sizeof(float));
103   float* d_res_half = (float*)gpu_device.allocate(num_elem * sizeof(float));
104   float* d_res_float = (float*)gpu_device.allocate(num_elem * sizeof(float));
105 
106   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float(
107       d_float, num_elem);
108   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_res_half(
109       d_res_half, num_elem);
110   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_res_float(
111       d_res_float, num_elem);
112 
113   gpu_float.device(gpu_device) = gpu_float.random() - gpu_float.constant(0.5f);
114   gpu_res_float.device(gpu_device) = gpu_float.abs();
115   gpu_res_half.device(gpu_device) = gpu_float.cast<Eigen::half>().abs().cast<float>();
116 
117   Tensor<float, 1> half_prec(num_elem);
118   Tensor<float, 1> full_prec(num_elem);
119   gpu_device.memcpyDeviceToHost(half_prec.data(), d_res_half, num_elem*sizeof(float));
120   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, num_elem*sizeof(float));
121   gpu_device.synchronize();
122 
123   for (int i = 0; i < num_elem; ++i) {
124     std::cout << "Checking unary " << i << std::endl;
125     VERIFY_IS_APPROX(full_prec(i), half_prec(i));
126   }
127 
128   gpu_device.deallocate(d_float);
129   gpu_device.deallocate(d_res_half);
130   gpu_device.deallocate(d_res_float);
131 }
132 
133 template<typename>
test_cuda_elementwise()134 void test_cuda_elementwise() {
135   Eigen::CudaStreamDevice stream;
136   Eigen::GpuDevice gpu_device(&stream);
137   int num_elem = 101;
138 
139   float* d_float1 = (float*)gpu_device.allocate(num_elem * sizeof(float));
140   float* d_float2 = (float*)gpu_device.allocate(num_elem * sizeof(float));
141   float* d_res_half = (float*)gpu_device.allocate(num_elem * sizeof(float));
142   float* d_res_float = (float*)gpu_device.allocate(num_elem * sizeof(float));
143 
144   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float1(
145       d_float1, num_elem);
146   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float2(
147       d_float2, num_elem);
148   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_res_half(
149       d_res_half, num_elem);
150   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_res_float(
151       d_res_float, num_elem);
152 
153   gpu_float1.device(gpu_device) = gpu_float1.random();
154   gpu_float2.device(gpu_device) = gpu_float2.random();
155   gpu_res_float.device(gpu_device) = (gpu_float1 + gpu_float2) * gpu_float1;
156   gpu_res_half.device(gpu_device) = ((gpu_float1.cast<Eigen::half>() + gpu_float2.cast<Eigen::half>()) * gpu_float1.cast<Eigen::half>()).cast<float>();
157 
158   Tensor<float, 1> half_prec(num_elem);
159   Tensor<float, 1> full_prec(num_elem);
160   gpu_device.memcpyDeviceToHost(half_prec.data(), d_res_half, num_elem*sizeof(float));
161   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, num_elem*sizeof(float));
162   gpu_device.synchronize();
163 
164   for (int i = 0; i < num_elem; ++i) {
165     std::cout << "Checking elemwise " << i << ": full prec = " << full_prec(i) << " vs half prec = " << half_prec(i) << std::endl;
166     VERIFY_IS_APPROX(static_cast<Eigen::half>(full_prec(i)), static_cast<Eigen::half>(half_prec(i)));
167   }
168 
169   gpu_device.deallocate(d_float1);
170   gpu_device.deallocate(d_float2);
171   gpu_device.deallocate(d_res_half);
172   gpu_device.deallocate(d_res_float);
173 }
174 
175 template<typename>
test_cuda_trancendental()176 void test_cuda_trancendental() {
177   Eigen::CudaStreamDevice stream;
178   Eigen::GpuDevice gpu_device(&stream);
179   int num_elem = 101;
180 
181   float* d_float1 = (float*)gpu_device.allocate(num_elem * sizeof(float));
182   float* d_float2 = (float*)gpu_device.allocate(num_elem * sizeof(float));
183   float* d_float3 = (float*)gpu_device.allocate(num_elem * sizeof(float));
184   Eigen::half* d_res1_half = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
185   Eigen::half* d_res1_float = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
186   Eigen::half* d_res2_half = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
187   Eigen::half* d_res2_float = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
188   Eigen::half* d_res3_half = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
189   Eigen::half* d_res3_float = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
190 
191   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float1(d_float1, num_elem);
192   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float2(d_float2, num_elem);
193   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float3(d_float3, num_elem);
194   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res1_half(d_res1_half, num_elem);
195   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res1_float(d_res1_float, num_elem);
196   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res2_half(d_res2_half, num_elem);
197   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res2_float(d_res2_float, num_elem);
198   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res3_half(d_res3_half, num_elem);
199   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res3_float(d_res3_float, num_elem);
200 
201   gpu_float1.device(gpu_device) = gpu_float1.random() - gpu_float1.constant(0.5f);
202   gpu_float2.device(gpu_device) = gpu_float2.random() + gpu_float1.constant(0.5f);
203   gpu_float3.device(gpu_device) = gpu_float3.random();
204   gpu_res1_float.device(gpu_device) = gpu_float1.exp().cast<Eigen::half>();
205   gpu_res2_float.device(gpu_device) = gpu_float2.log().cast<Eigen::half>();
206   gpu_res3_float.device(gpu_device) = gpu_float3.log1p().cast<Eigen::half>();
207 
208   gpu_res1_half.device(gpu_device) = gpu_float1.cast<Eigen::half>();
209   gpu_res1_half.device(gpu_device) = gpu_res1_half.exp();
210 
211   gpu_res2_half.device(gpu_device) = gpu_float2.cast<Eigen::half>();
212   gpu_res2_half.device(gpu_device) = gpu_res2_half.log();
213 
214   gpu_res3_half.device(gpu_device) = gpu_float3.cast<Eigen::half>();
215   gpu_res3_half.device(gpu_device) = gpu_res3_half.log1p();
216 
217   Tensor<float, 1> input1(num_elem);
218   Tensor<Eigen::half, 1> half_prec1(num_elem);
219   Tensor<Eigen::half, 1> full_prec1(num_elem);
220   Tensor<float, 1> input2(num_elem);
221   Tensor<Eigen::half, 1> half_prec2(num_elem);
222   Tensor<Eigen::half, 1> full_prec2(num_elem);
223   Tensor<float, 1> input3(num_elem);
224   Tensor<Eigen::half, 1> half_prec3(num_elem);
225   Tensor<Eigen::half, 1> full_prec3(num_elem);
226   gpu_device.memcpyDeviceToHost(input1.data(), d_float1, num_elem*sizeof(float));
227   gpu_device.memcpyDeviceToHost(input2.data(), d_float2, num_elem*sizeof(float));
228   gpu_device.memcpyDeviceToHost(input3.data(), d_float3, num_elem*sizeof(float));
229   gpu_device.memcpyDeviceToHost(half_prec1.data(), d_res1_half, num_elem*sizeof(Eigen::half));
230   gpu_device.memcpyDeviceToHost(full_prec1.data(), d_res1_float, num_elem*sizeof(Eigen::half));
231   gpu_device.memcpyDeviceToHost(half_prec2.data(), d_res2_half, num_elem*sizeof(Eigen::half));
232   gpu_device.memcpyDeviceToHost(full_prec2.data(), d_res2_float, num_elem*sizeof(Eigen::half));
233   gpu_device.memcpyDeviceToHost(half_prec3.data(), d_res3_half, num_elem*sizeof(Eigen::half));
234   gpu_device.memcpyDeviceToHost(full_prec3.data(), d_res3_float, num_elem*sizeof(Eigen::half));
235   gpu_device.synchronize();
236 
237   for (int i = 0; i < num_elem; ++i) {
238     std::cout << "Checking elemwise exp " << i << " input = " << input1(i) << " full = " << full_prec1(i) << " half = " << half_prec1(i) << std::endl;
239     VERIFY_IS_APPROX(full_prec1(i), half_prec1(i));
240   }
241   for (int i = 0; i < num_elem; ++i) {
242     std::cout << "Checking elemwise log " << i << " input = " << input2(i) << " full = " << full_prec2(i) << " half = " << half_prec2(i) << std::endl;
243     if(std::abs(input2(i)-1.f)<0.05f) // log lacks accurary nearby 1
244       VERIFY_IS_APPROX(full_prec2(i)+Eigen::half(0.1f), half_prec2(i)+Eigen::half(0.1f));
245     else
246       VERIFY_IS_APPROX(full_prec2(i), half_prec2(i));
247   }
248   for (int i = 0; i < num_elem; ++i) {
249     std::cout << "Checking elemwise plog1 " << i << " input = " << input3(i) << " full = " << full_prec3(i) << " half = " << half_prec3(i) << std::endl;
250     VERIFY_IS_APPROX(full_prec3(i), half_prec3(i));
251   }
252   gpu_device.deallocate(d_float1);
253   gpu_device.deallocate(d_float2);
254   gpu_device.deallocate(d_float3);
255   gpu_device.deallocate(d_res1_half);
256   gpu_device.deallocate(d_res1_float);
257   gpu_device.deallocate(d_res2_half);
258   gpu_device.deallocate(d_res2_float);
259   gpu_device.deallocate(d_res3_float);
260   gpu_device.deallocate(d_res3_half);
261 }
262 
263 template<typename>
test_cuda_contractions()264 void test_cuda_contractions() {
265   Eigen::CudaStreamDevice stream;
266   Eigen::GpuDevice gpu_device(&stream);
267   int rows = 23;
268   int cols = 23;
269   int num_elem = rows*cols;
270 
271   float* d_float1 = (float*)gpu_device.allocate(num_elem * sizeof(float));
272   float* d_float2 = (float*)gpu_device.allocate(num_elem * sizeof(float));
273   Eigen::half* d_res_half = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
274   Eigen::half* d_res_float = (Eigen::half*)gpu_device.allocate(num_elem * sizeof(Eigen::half));
275 
276   Eigen::TensorMap<Eigen::Tensor<float, 2>, Eigen::Aligned> gpu_float1(
277       d_float1, rows, cols);
278   Eigen::TensorMap<Eigen::Tensor<float, 2>, Eigen::Aligned> gpu_float2(
279       d_float2, rows, cols);
280   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 2>, Eigen::Aligned> gpu_res_half(
281       d_res_half, rows, cols);
282   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 2>, Eigen::Aligned> gpu_res_float(
283       d_res_float, rows, cols);
284 
285   gpu_float1.device(gpu_device) = gpu_float1.random() - gpu_float1.constant(0.5f);
286   gpu_float2.device(gpu_device) = gpu_float2.random() - gpu_float2.constant(0.5f);
287 
288   typedef Tensor<float, 2>::DimensionPair DimPair;
289   Eigen::array<DimPair, 1> dims(DimPair(1, 0));
290   gpu_res_float.device(gpu_device) = gpu_float1.contract(gpu_float2, dims).cast<Eigen::half>();
291   gpu_res_half.device(gpu_device) = gpu_float1.cast<Eigen::half>().contract(gpu_float2.cast<Eigen::half>(), dims);
292 
293   Tensor<Eigen::half, 2> half_prec(rows, cols);
294   Tensor<Eigen::half, 2> full_prec(rows, cols);
295   gpu_device.memcpyDeviceToHost(half_prec.data(), d_res_half, num_elem*sizeof(Eigen::half));
296   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, num_elem*sizeof(Eigen::half));
297   gpu_device.synchronize();
298 
299   for (int i = 0; i < rows; ++i) {
300     for (int j = 0; j < cols; ++j) {
301       std::cout << "Checking contract " << i << " " << j << full_prec(i, j) << " " << half_prec(i, j) << std::endl;
302       if (numext::abs(full_prec(i, j) - half_prec(i, j)) > Eigen::half(1e-2f)) {
303         VERIFY_IS_APPROX(full_prec(i, j), half_prec(i, j));
304       }
305     }
306   }
307 
308   gpu_device.deallocate(d_float1);
309   gpu_device.deallocate(d_float2);
310   gpu_device.deallocate(d_res_half);
311   gpu_device.deallocate(d_res_float);
312 }
313 
314 template<typename>
test_cuda_reductions(int size1,int size2,int redux)315 void test_cuda_reductions(int size1, int size2, int redux) {
316 
317    std::cout << "Reducing " << size1 << " by " << size2
318              << " tensor along dim " << redux << std::endl;
319 
320   Eigen::CudaStreamDevice stream;
321   Eigen::GpuDevice gpu_device(&stream);
322   int num_elem = size1*size2;
323   int result_size = (redux == 1 ? size1 : size2);
324 
325   float* d_float1 = (float*)gpu_device.allocate(num_elem * sizeof(float));
326   float* d_float2 = (float*)gpu_device.allocate(num_elem * sizeof(float));
327   Eigen::half* d_res_half = (Eigen::half*)gpu_device.allocate(result_size * sizeof(Eigen::half));
328   Eigen::half* d_res_float = (Eigen::half*)gpu_device.allocate(result_size * sizeof(Eigen::half));
329 
330   Eigen::TensorMap<Eigen::Tensor<float, 2>, Eigen::Aligned> gpu_float1(
331       d_float1, size1, size2);
332   Eigen::TensorMap<Eigen::Tensor<float, 2>, Eigen::Aligned> gpu_float2(
333       d_float2, size1, size2);
334   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res_half(
335       d_res_half, result_size);
336   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 1>, Eigen::Aligned> gpu_res_float(
337       d_res_float, result_size);
338 
339   gpu_float1.device(gpu_device) = gpu_float1.random() * 2.0f;
340   gpu_float2.device(gpu_device) = gpu_float2.random() * 2.0f;
341 
342   Eigen::array<int, 1> redux_dim = {{redux}};
343   gpu_res_float.device(gpu_device) = gpu_float1.sum(redux_dim).cast<Eigen::half>();
344   gpu_res_half.device(gpu_device) = gpu_float1.cast<Eigen::half>().sum(redux_dim);
345 
346   Tensor<Eigen::half, 1> half_prec(result_size);
347   Tensor<Eigen::half, 1> full_prec(result_size);
348   gpu_device.memcpyDeviceToHost(half_prec.data(), d_res_half, result_size*sizeof(Eigen::half));
349   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, result_size*sizeof(Eigen::half));
350   gpu_device.synchronize();
351 
352   for (int i = 0; i < result_size; ++i) {
353     std::cout << "EXPECTED " << full_prec(i) << " GOT " << half_prec(i) << std::endl;
354     VERIFY_IS_APPROX(full_prec(i), half_prec(i));
355   }
356 
357   gpu_device.deallocate(d_float1);
358   gpu_device.deallocate(d_float2);
359   gpu_device.deallocate(d_res_half);
360   gpu_device.deallocate(d_res_float);
361 }
362 
363 template<typename>
test_cuda_reductions()364 void test_cuda_reductions() {
365   test_cuda_reductions<void>(13, 13, 0);
366   test_cuda_reductions<void>(13, 13, 1);
367 
368   test_cuda_reductions<void>(35, 36, 0);
369   test_cuda_reductions<void>(35, 36, 1);
370 
371   test_cuda_reductions<void>(36, 35, 0);
372   test_cuda_reductions<void>(36, 35, 1);
373 }
374 
375 template<typename>
test_cuda_full_reductions()376 void test_cuda_full_reductions() {
377   Eigen::CudaStreamDevice stream;
378   Eigen::GpuDevice gpu_device(&stream);
379   int size = 13;
380   int num_elem = size*size;
381 
382   float* d_float1 = (float*)gpu_device.allocate(num_elem * sizeof(float));
383   float* d_float2 = (float*)gpu_device.allocate(num_elem * sizeof(float));
384   Eigen::half* d_res_half = (Eigen::half*)gpu_device.allocate(1 * sizeof(Eigen::half));
385   Eigen::half* d_res_float = (Eigen::half*)gpu_device.allocate(1 * sizeof(Eigen::half));
386 
387   Eigen::TensorMap<Eigen::Tensor<float, 2>, Eigen::Aligned> gpu_float1(
388       d_float1, size, size);
389   Eigen::TensorMap<Eigen::Tensor<float, 2>, Eigen::Aligned> gpu_float2(
390       d_float2, size, size);
391   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 0>, Eigen::Aligned> gpu_res_half(
392       d_res_half);
393   Eigen::TensorMap<Eigen::Tensor<Eigen::half, 0>, Eigen::Aligned> gpu_res_float(
394       d_res_float);
395 
396   gpu_float1.device(gpu_device) = gpu_float1.random();
397   gpu_float2.device(gpu_device) = gpu_float2.random();
398 
399   gpu_res_float.device(gpu_device) = gpu_float1.sum().cast<Eigen::half>();
400   gpu_res_half.device(gpu_device) = gpu_float1.cast<Eigen::half>().sum();
401 
402   Tensor<Eigen::half, 0> half_prec;
403   Tensor<Eigen::half, 0> full_prec;
404   gpu_device.memcpyDeviceToHost(half_prec.data(), d_res_half, sizeof(Eigen::half));
405   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, sizeof(Eigen::half));
406   gpu_device.synchronize();
407 
408   VERIFY_IS_APPROX(full_prec(), half_prec());
409 
410   gpu_res_float.device(gpu_device) = gpu_float1.maximum().cast<Eigen::half>();
411   gpu_res_half.device(gpu_device) = gpu_float1.cast<Eigen::half>().maximum();
412   gpu_device.memcpyDeviceToHost(half_prec.data(), d_res_half, sizeof(Eigen::half));
413   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, sizeof(Eigen::half));
414   gpu_device.synchronize();
415 
416   VERIFY_IS_APPROX(full_prec(), half_prec());
417 
418   gpu_device.deallocate(d_float1);
419   gpu_device.deallocate(d_float2);
420   gpu_device.deallocate(d_res_half);
421   gpu_device.deallocate(d_res_float);
422 }
423 
424 template<typename>
test_cuda_forced_evals()425 void test_cuda_forced_evals() {
426 
427   Eigen::CudaStreamDevice stream;
428   Eigen::GpuDevice gpu_device(&stream);
429   int num_elem = 101;
430 
431   float* d_float = (float*)gpu_device.allocate(num_elem * sizeof(float));
432   float* d_res_half1 = (float*)gpu_device.allocate(num_elem * sizeof(float));
433   float* d_res_half2 = (float*)gpu_device.allocate(num_elem * sizeof(float));
434   float* d_res_float = (float*)gpu_device.allocate(num_elem * sizeof(float));
435 
436   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_float(
437       d_float, num_elem);
438   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_res_half1(
439       d_res_half1, num_elem);
440  Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Unaligned> gpu_res_half2(
441       d_res_half2, num_elem);
442   Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_res_float(
443       d_res_float, num_elem);
444 
445   Eigen::array<int, 1> no_bcast;
446   no_bcast[0] = 1;
447 
448   gpu_float.device(gpu_device) = gpu_float.random() - gpu_float.constant(0.5f);
449   gpu_res_float.device(gpu_device) = gpu_float.abs();
450   gpu_res_half1.device(gpu_device) = gpu_float.cast<Eigen::half>().abs().eval().cast<float>();
451   gpu_res_half2.device(gpu_device) = gpu_float.cast<Eigen::half>().abs().broadcast(no_bcast).eval().cast<float>();
452 
453   Tensor<float, 1> half_prec1(num_elem);
454   Tensor<float, 1> half_prec2(num_elem);
455   Tensor<float, 1> full_prec(num_elem);
456   gpu_device.memcpyDeviceToHost(half_prec1.data(), d_res_half1, num_elem*sizeof(float));
457   gpu_device.memcpyDeviceToHost(half_prec2.data(), d_res_half1, num_elem*sizeof(float));
458   gpu_device.memcpyDeviceToHost(full_prec.data(), d_res_float, num_elem*sizeof(float));
459   gpu_device.synchronize();
460 
461   for (int i = 0; i < num_elem; ++i) {
462     std::cout << "Checking forced eval " << i << full_prec(i) << " vs " << half_prec1(i) << " vs " << half_prec2(i) << std::endl;
463     VERIFY_IS_APPROX(full_prec(i), half_prec1(i));
464     VERIFY_IS_APPROX(full_prec(i), half_prec2(i));
465   }
466 
467   gpu_device.deallocate(d_float);
468   gpu_device.deallocate(d_res_half1);
469   gpu_device.deallocate(d_res_half2);
470   gpu_device.deallocate(d_res_float);
471 }
472 #endif
473 
474 
test_cxx11_tensor_of_float16_cuda()475 void test_cxx11_tensor_of_float16_cuda()
476 {
477   CALL_SUBTEST_1(test_cuda_numext<void>());
478 
479 #ifdef EIGEN_HAS_CUDA_FP16
480   CALL_SUBTEST_1(test_cuda_conversion<void>());
481   CALL_SUBTEST_1(test_cuda_unary<void>());
482   CALL_SUBTEST_1(test_cuda_elementwise<void>());
483   CALL_SUBTEST_1(test_cuda_trancendental<void>());
484   CALL_SUBTEST_2(test_cuda_contractions<void>());
485   CALL_SUBTEST_3(test_cuda_reductions<void>());
486   CALL_SUBTEST_4(test_cuda_full_reductions<void>());
487   CALL_SUBTEST_5(test_cuda_forced_evals<void>());
488 #else
489   std::cout << "Half floats are not supported by this version of cuda: skipping the test" << std::endl;
490 #endif
491 }
492