RAJA
RAJA provides a collection of platform portability abstractions for C++ HPC applications.
avx_double.hpp
Go to the documentation of this file.
1 
11 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
12 // Copyright (c) Lawrence Livermore National Security, LLC and other
13 // RAJA Project Developers. See top-level LICENSE and COPYRIGHT
14 // files for dates and other details. No copyright assignment is required
15 // to contribute to RAJA.
16 //
17 // SPDX-License-Identifier: (BSD-3-Clause)
18 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
19 
20 #ifdef __AVX__
21 
22 #ifndef RAJA_policy_vector_register_avx_double_HPP
23 #define RAJA_policy_vector_register_avx_double_HPP
24 
25 #include "RAJA/config.hpp"
26 #include "RAJA/util/macros.hpp"
28 
29 // Include SIMD intrinsics header file
30 #include <immintrin.h>
31 #include <cmath>
32 
33 namespace RAJA
34 {
35 namespace expt
36 {
37 
38 template<>
39 class Register<double, avx_register>
40  : public internal::expt::RegisterBase<Register<double, avx_register>>
41 {
42 public:
43  using base_type =
44  internal::expt::RegisterBase<Register<double, avx_register>>;
45 
46  using register_policy = avx_register;
47  using self_type = Register<double, avx_register>;
48  using element_type = double;
49  using register_type = __m256d;
50 
51  using int_vector_type = Register<int64_t, avx_register>;
52 
53 
54 private:
55  register_type m_value;
56 
57  RAJA_INLINE
58  __m256i createMask(camp::idx_t N) const
59  {
60  // Generate a mask
61  return _mm256_set_epi64x(N >= 4 ? -1 : 0, N >= 3 ? -1 : 0, N >= 2 ? -1 : 0,
62  N >= 1 ? -1 : 0);
63  }
64 
65  RAJA_INLINE
66  __m256i createStridedOffsets(camp::idx_t stride) const
67  {
68  // Generate a strided offset list
69  return _mm256_set_epi64x(3 * stride, 2 * stride, stride, 0);
70  }
71 
72 public:
73  static constexpr camp::idx_t s_num_elem = 4;
74 
78  RAJA_INLINE
79  Register() : base_type(), m_value(_mm256_setzero_pd()) {}
80 
84  RAJA_INLINE
85  Register(element_type x0, element_type x1, element_type x2, element_type x3)
86  : base_type(),
87  m_value(_mm256_set_pd(x3, x2, x1, x0))
88  {}
89 
93  RAJA_INLINE
94  explicit Register(register_type const& c) : base_type(), m_value(c) {}
95 
99  RAJA_INLINE
100  Register(self_type const& c) : base_type(), m_value(c.m_value) {}
101 
105  RAJA_INLINE
106  self_type& operator=(self_type const& c)
107  {
108  m_value = c.m_value;
109  return *this;
110  }
111 
116  RAJA_INLINE
117  Register(element_type const& c) : m_value(_mm256_set1_pd(c)) {}
118 
123  RAJA_INLINE
124  self_type& load_packed(element_type const* ptr)
125  {
126  m_value = _mm256_loadu_pd(ptr);
127  return *this;
128  }
129 
135  RAJA_INLINE
136  self_type& load_packed_n(element_type const* ptr, camp::idx_t N)
137  {
138  m_value = _mm256_maskload_pd(ptr, createMask(N));
139  return *this;
140  }
141 
146  RAJA_INLINE
147  self_type& load_strided(element_type const* ptr, camp::idx_t stride)
148  {
149  for (camp::idx_t i = 0; i < 4; ++i)
150  {
151  m_value[i] = ptr[i * stride];
152  }
153  return *this;
154  }
155 
161  RAJA_INLINE
162  self_type& load_strided_n(element_type const* ptr,
163  camp::idx_t stride,
164  camp::idx_t N)
165  {
166  m_value = _mm256_setzero_pd();
167  for (camp::idx_t i = 0; i < N; ++i)
168  {
169  m_value[i] = ptr[i * stride];
170  };
171  return *this;
172  }
173 
178  RAJA_INLINE
179  self_type const& store_packed(element_type* ptr) const
180  {
181  _mm256_storeu_pd(ptr, m_value);
182  return *this;
183  }
184 
189  RAJA_INLINE
190  self_type const& store_packed_n(element_type* ptr, camp::idx_t N) const
191  {
192  _mm256_maskstore_pd(ptr, createMask(N), m_value);
193  return *this;
194  }
195 
200  RAJA_INLINE
201  self_type const& store_strided(element_type* ptr, camp::idx_t stride) const
202  {
203  for (camp::idx_t i = 0; i < 4; ++i)
204  {
205  ptr[i * stride] = m_value[i];
206  }
207  return *this;
208  }
209 
214  RAJA_INLINE
215  self_type const& store_strided_n(element_type* ptr,
216  camp::idx_t stride,
217  camp::idx_t N) const
218  {
219  for (camp::idx_t i = 0; i < N; ++i)
220  {
221  ptr[i * stride] = m_value[i];
222  }
223  return *this;
224  }
225 
231  RAJA_INLINE
232  element_type get(camp::idx_t i) const { return m_value[i]; }
233 
239  RAJA_INLINE
240  self_type& set(element_type value, camp::idx_t i)
241  {
242  m_value[i] = value;
243  return *this;
244  }
245 
247 
248  RAJA_INLINE
249  self_type& broadcast(element_type const& value)
250  {
251  m_value = _mm256_set1_pd(value);
252  return *this;
253  }
254 
256 
257  RAJA_INLINE
258  self_type& copy(self_type const& src)
259  {
260  m_value = src.m_value;
261  return *this;
262  }
263 
265 
266  RAJA_INLINE
267  self_type add(self_type const& b) const
268  {
269  return self_type(_mm256_add_pd(m_value, b.m_value));
270  }
271 
273 
274  RAJA_INLINE
275  self_type subtract(self_type const& b) const
276  {
277  return self_type(_mm256_sub_pd(m_value, b.m_value));
278  }
279 
281 
282  RAJA_INLINE
283  self_type multiply(self_type const& b) const
284  {
285  return self_type(_mm256_mul_pd(m_value, b.m_value));
286  }
287 
289 
290  RAJA_INLINE
291  self_type divide(self_type const& b) const
292  {
293  return self_type(_mm256_div_pd(m_value, b.m_value));
294  }
295 
297 
298  RAJA_INLINE
299  self_type divide_n(self_type const& b, camp::idx_t N) const
300  {
301  // AVX2 does not supply a masked divide, so do it manually
302  return self_type(_mm256_set_pd(
303  N >= 4 ? get(3) / b.get(3) : 0, N >= 3 ? get(2) / b.get(2) : 0,
304  N >= 2 ? get(1) / b.get(1) : 0, N >= 1 ? get(0) / b.get(0) : 0));
305  }
306 
311  RAJA_INLINE
312  element_type sum() const
313  {
314  auto sh1 = _mm256_permute_pd(m_value, 0x5);
315  auto red1 = _mm256_add_pd(m_value, sh1);
316  return red1[0] + red1[2];
317  }
318 
323  RAJA_INLINE
324  element_type max() const
325  {
326  // permute the first two and last two lanes of the register
327  // A = { v[1], v[0], v[3], v[2] }
328  register_type a = _mm256_shuffle_pd(m_value, m_value, 0x5);
329 
330  // take the maximum value of each lane
331  // B = { max{v[0], v[1]},
332  // max{v[0], v[1]},
333  // max{v[2], v[3]},
334  // max{v[2], v[3]} }
335  register_type b = _mm256_max_pd(m_value, a);
336 
337  // now take the maximum of a lower and upper halves
338  return RAJA::max<element_type>(b[0], b[2]);
339  }
340 
345  RAJA_INLINE
346  element_type max_n(camp::idx_t N) const
347  {
348  if (N == 4)
349  {
350  // permute the first two and last two lanes of the register
351  // A = { v[1], v[0], v[3], v[2] }
352  register_type a = _mm256_shuffle_pd(m_value, m_value, 0x5);
353 
354  // take the maximum value of each lane
355  // B = { max{v[0], v[1]},
356  // max{v[0], v[1]},
357  // max{v[2], v[3]},
358  // max{v[2], v[3]} }
359  register_type b = _mm256_max_pd(m_value, a);
360 
361  // now take the maximum of a lower and upper halves
362  return RAJA::max<element_type>(b[0], b[2]);
363  }
364  else if (N == 3)
365  {
366  // permute the first two and last two lanes of the register
367  // use the third element TWICE, so we effectively remove the 4th
368  // lane
369  // A = { v[1], v[0], v[2], v[2] }
370  register_type a = _mm256_shuffle_pd(m_value, m_value, 0x3);
371 
372  // take the maximum value of each lane
373  // B = { max{v[0], v[1]},
374  // max{v[0], v[1]},
375  // max{v[2], v[2]}, <-- just v[2]
376  // max{v[2], v[3]} }
377  register_type b = _mm256_max_pd(m_value, a);
378 
379  // now take the maximum of a lower and upper lane
380  return RAJA::max<element_type>(b[0], b[2]);
381  }
382  else if (N == 2)
383  {
384  return RAJA::max<element_type>(m_value[0], m_value[1]);
385  }
386  else if (N == 1)
387  {
388  return m_value[0];
389  }
391  }
392 
397  RAJA_INLINE
398  self_type vmax(self_type a) const
399  {
400  return self_type(_mm256_max_pd(m_value, a.m_value));
401  }
402 
407  RAJA_INLINE
408  element_type min() const
409  {
410  // permute the first two and last two lanes of the register
411  // A = { v[1], v[0], v[3], v[2] }
412  register_type a = _mm256_shuffle_pd(m_value, m_value, 0x5);
413 
414  // take the minimum value of each lane
415  // B = { min{v[0], v[1]},
416  // min{v[0], v[1]},
417  // min{v[2], v[3]},
418  // min{v[2], v[3]} }
419  register_type b = _mm256_min_pd(m_value, a);
420 
421  // now take the minimum of a lower and upper halves
422  return RAJA::min<element_type>(b[0], b[2]);
423  }
424 
429  RAJA_INLINE
430  element_type min_n(camp::idx_t N) const
431  {
432  if (N == 4)
433  {
434  // permute the first two and last two lanes of the register
435  // A = { v[1], v[0], v[3], v[2] }
436  register_type a = _mm256_shuffle_pd(m_value, m_value, 0x5);
437 
438  // take the minimum value of each lane
439  // B = { min{v[0], v[1]},
440  // min{v[0], v[1]},
441  // min{v[2], v[3]},
442  // min{v[2], v[3]} }
443  register_type b = _mm256_min_pd(m_value, a);
444 
445  // now take the minimum of a lower and upper halves
446  return RAJA::min<element_type>(b[0], b[2]);
447  }
448  else if (N == 3)
449  {
450  // permute the first two and last two lanes of the register
451  // use the third element TWICE, so we effectively remove the 4th
452  // lane
453  // A = { v[1], v[0], v[2], v[2] }
454  register_type a = _mm256_shuffle_pd(m_value, m_value, 0x3);
455 
456  // take the minimum value of each lane
457  // B = { min{v[0], v[1]},
458  // min{v[0], v[1]},
459  // min{v[2], v[2]}, <-- just v[2]
460  // min{v[2], v[3]} }
461  register_type b = _mm256_min_pd(m_value, a);
462 
463  // now take the minimum of a lower and upper lane
464  return RAJA::min<element_type>(b[0], b[2]);
465  }
466  else if (N == 2)
467  {
468  return RAJA::min<element_type>(m_value[0], m_value[1]);
469  }
470  else if (N == 1)
471  {
472  return m_value[0];
473  }
475  }
476 
481  RAJA_INLINE
482  self_type vmin(self_type a) const
483  {
484  return self_type(_mm256_min_pd(m_value, a.m_value));
485  }
486 };
487 
488 
489 } // namespace expt
490 } // namespace RAJA
491 
492 
493 #endif
494 
495 #endif //__AVX__
RAJA header file defining SIMD/SIMT register operations.
Header file for common RAJA internal macro definitions.
#define RAJA_HOST_DEVICE
Definition: macros.hpp:65
Definition: AlignedRangeIndexSetBuilders.cpp:35
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result min(Args... args)
Definition: foldl.hpp:161
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result sum(Args... args)
Definition: foldl.hpp:143
RAJA_HOST_DEVICE constexpr RAJA_INLINE RAJA::zip_tuple_element_t< I, zip_tuple< is_val, Ts... > > & get(zip_tuple< is_val, Ts... > &z) noexcept
Definition: zip_tuple.hpp:56
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result max(Args... args)
Definition: foldl.hpp:155