deal.II version 9.8.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
vectorization.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2012 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
14#ifndef dealii_vectorization_h
15#define dealii_vectorization_h
16
17#include <deal.II/base/config.h>
18
22
23#include <algorithm>
24#include <array>
25#include <cmath>
26
27// Note:
28// The flag DEAL_II_VECTORIZATION_WIDTH_IN_BITS is essentially constructed
29// according to the following scheme (on x86-based architectures)
30// #ifdef __AVX512F__
31// #define DEAL_II_VECTORIZATION_WIDTH_IN_BITS 512
32// #elif defined (__AVX__)
33// #define DEAL_II_VECTORIZATION_WIDTH_IN_BITS 256
34// #elif defined (__SSE2__)
35// #define DEAL_II_VECTORIZATION_WIDTH_IN_BITS 128
36// #else
37// #define DEAL_II_VECTORIZATION_WIDTH_IN_BITS 0
38// #endif
39// In addition to checking the flags __AVX512F__, __AVX__ and __SSE2__, a CMake
40// test, 'check_01_cpu_features.cmake', ensures that these feature are not only
41// present in the compilation unit but also working properly.
42
43#if DEAL_II_VECTORIZATION_WIDTH_IN_BITS > 0
44
45// These error messages try to detect the case that deal.II was compiled with
46// a wider instruction set extension as the current compilation unit, for
47// example because deal.II was compiled with AVX, but a user project does not
48// add -march=native or similar flags, making it fall to SSE2. This leads to
49// very strange errors as the size of data structures differs between the
50// compiled deal.II code sitting in libdeal_II.so and the user code if not
51// detected.
52# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && !defined(__AVX__)
53# error \
54 "Mismatch in vectorization capabilities: AVX was detected during configuration of deal.II and switched on, but it is apparently not available for the file you are trying to compile at the moment. Check compilation flags controlling the instruction set, such as -march=native."
55# endif
56# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 512 && !defined(__AVX512F__)
57# error \
58 "Mismatch in vectorization capabilities: AVX-512F was detected during configuration of deal.II and switched on, but it is apparently not available for the file you are trying to compile at the moment. Check compilation flags controlling the instruction set, such as -march=native."
59# endif
60
61# ifdef _MSC_VER
62# include <intrin.h>
63# elif defined(__ALTIVEC__)
64# include <altivec.h>
65
66// altivec.h defines vector, pixel, bool, but we do not use them, so undefine
67// them before they make trouble
68# undef vector
69# undef pixel
70# undef bool
71# elif defined(__ARM_NEON)
72# include <arm_neon.h>
73# elif defined(__x86_64__)
74# include <x86intrin.h>
75# endif
76
77#endif
78
79
81
82
83// Enable the EnableIfScalar type trait for VectorizedArray<Number> such
84// that it can be used as a Number type in Tensor<rank,dim,Number>, etc.
85
86template <typename Number, std::size_t width>
91
92
93
97template <typename T>
99{
100public:
107 constexpr VectorizedArrayIterator(T &data, const std::size_t lane)
108 : data(&data)
109 , lane(lane)
110 {}
111
115 constexpr bool
117 {
118 Assert(this->data == other.data,
120 "You are trying to compare iterators into different arrays."));
121 return this->lane == other.lane;
122 }
123
127 constexpr bool
129 {
130 Assert(this->data == other.data,
132 "You are trying to compare iterators into different arrays."));
133 return this->lane != other.lane;
134 }
135
140 constexpr const typename T::value_type &
141 operator*() const
142 {
143 AssertIndexRange(lane, T::size());
144 return (*data)[lane];
145 }
146
147
152 template <typename U = T>
153 constexpr std::enable_if_t<!std::is_same_v<U, const U>,
154 typename T::value_type> &
156 {
157 AssertIndexRange(lane, T::size());
158 return (*data)[lane];
159 }
160
168 {
169 AssertIndexRange(lane + 1, T::size() + 1);
170 ++lane;
171 return *this;
172 }
173
179 operator+=(const std::size_t offset)
180 {
181 AssertIndexRange(lane + offset, T::size() + 1);
182 lane += offset;
183 return *this;
184 }
185
193 {
194 Assert(
195 lane > 0,
197 "You can't decrement an iterator that is already at the beginning of the range."));
198 --lane;
199 return *this;
200 }
201
206 operator+(const std::size_t &offset) const
207 {
208 AssertIndexRange(lane + offset, T::size() + 1);
209 return VectorizedArrayIterator<T>(*data, lane + offset);
210 }
211
215 constexpr std::ptrdiff_t
217 {
218 return static_cast<std::ptrdiff_t>(lane) -
219 static_cast<std::ptrdiff_t>(other.lane);
220 }
221
222private:
227
231 std::size_t lane;
232};
233
234
235
248template <typename VectorizedArrayType, std::size_t width>
250{
251public:
255 constexpr VectorizedArrayBase() = default;
256
264 template <typename U>
265 constexpr VectorizedArrayBase(const std::initializer_list<U> &list)
266 {
267 const unsigned int n_initializers = list.size();
268 Assert(n_initializers <= size(),
269 ExcMessage("The initializer list must have at most "
270 "as many elements as the vector length."));
271
272 // Copy what's in the list.
273 std::copy_n(list.begin(), n_initializers, this->begin());
274
275 // Then add zero padding where necessary.
276 if (n_initializers < size())
277 std::fill(this->begin() + n_initializers, this->end(), 0.0);
278 }
279
283 static constexpr std::size_t
285 {
286 return width;
287 }
288
294 {
296 static_cast<VectorizedArrayType &>(*this), 0);
297 }
298
304 begin() const
305 {
307 static_cast<const VectorizedArrayType &>(*this), 0);
308 }
309
315 {
317 static_cast<VectorizedArrayType &>(*this), width);
318 }
319
325 end() const
326 {
328 static_cast<const VectorizedArrayType &>(*this), width);
329 }
330
342 auto
343 dot_product(const VectorizedArrayType &v) const
344 {
345 VectorizedArrayType p = static_cast<const VectorizedArrayType &>(*this);
346 p *= v;
347 return p.sum();
348 }
349};
350
351
352
441template <typename Number, std::size_t width>
443 : public VectorizedArrayBase<VectorizedArray<Number, width>, 1>
444{
445public:
449 using value_type = Number;
450
459 static constexpr bool is_implemented = (width == 1);
460
465 VectorizedArray() = default;
466
470 VectorizedArray(const Number scalar)
471 {
472 static_assert(width == 1,
473 "You specified an illegal width that is not supported.");
474
475 this->operator=(scalar);
476 }
477
481 template <typename U>
482 VectorizedArray(const std::initializer_list<U> &list)
483 : VectorizedArrayBase<VectorizedArray<Number, width>, 1>(list)
484 {
485 static_assert(width == 1,
486 "You specified an illegal width that is not supported.");
487 }
488
494 operator=(const Number scalar) &
495 {
496 data = scalar;
497 return *this;
498 }
499
506 operator=(const Number scalar) && = delete;
507
513 Number &
514 operator[](const unsigned int comp)
515 {
516 (void)comp;
517 AssertIndexRange(comp, 1);
518 return data;
519 }
520
526 const Number &
527 operator[](const unsigned int comp) const
528 {
529 (void)comp;
530 AssertIndexRange(comp, 1);
531 return data;
532 }
533
540 {
541 data += vec.data;
542 return *this;
543 }
544
551 {
552 data -= vec.data;
553 return *this;
554 }
555
562 {
563 data *= vec.data;
564 return *this;
565 }
566
573 {
574 data /= vec.data;
575 return *this;
576 }
577
584 template <typename OtherNumber>
586 load(const OtherNumber *ptr)
587 {
588 data = *ptr;
589 }
590
597 template <typename OtherNumber>
599 store(OtherNumber *ptr) const
600 {
601 *ptr = data;
602 }
603
651 void
652 streaming_store(Number *ptr) const
653 {
654 *ptr = data;
655 }
656
675 void
676 gather(const Number *base_ptr, const unsigned int *offsets)
677 {
678 this->operator=(Number(0));
679 if (offsets[0] != numbers::invalid_unsigned_int)
680 data = base_ptr[offsets[0]];
681 }
682
699 void
700 scatter(const unsigned int *offsets, Number *base_ptr) const
701 {
702 if (offsets[0] != numbers::invalid_unsigned_int)
703 base_ptr[offsets[0]] = data;
704 }
705
711 Number
712 sum() const
713 {
714 return data;
715 }
716
721 get_floor() const
722 {
723 VectorizedArray res;
724 res.data = std::floor(data);
725 return res;
726 }
727
733 Number data;
734
735private:
742 get_sqrt() const
743 {
744 VectorizedArray res;
745 res.data = std::sqrt(data);
746 return res;
747 }
748
755 get_abs() const
756 {
757 VectorizedArray res;
758 res.data = std::fabs(data);
759 return res;
760 }
761
768 get_max(const VectorizedArray &other) const
769 {
770 VectorizedArray res;
771 res.data = std::max(data, other.data);
772 return res;
773 }
774
781 get_min(const VectorizedArray &other) const
782 {
783 VectorizedArray res;
784 res.data = std::min(data, other.data);
785 return res;
786 }
787
788 // Make a few functions friends.
789 template <typename Number2, std::size_t width2>
792 template <typename Number2, std::size_t width2>
795 template <typename Number2, std::size_t width2>
799 template <typename Number2, std::size_t width2>
803};
804
805
806
811
818template <typename Number,
819 std::size_t width =
822 make_vectorized_array(const Number &u)
823{
825 return result;
826}
827
828
829
836template <typename VectorizedArrayType>
837inline DEAL_II_ALWAYS_INLINE VectorizedArrayType
838make_vectorized_array(const typename VectorizedArrayType::value_type &u)
839{
840 static_assert(
841 std::is_same_v<VectorizedArrayType,
842 VectorizedArray<typename VectorizedArrayType::value_type,
843 VectorizedArrayType::size()>>,
844 "VectorizedArrayType is not a VectorizedArray.");
845
846 VectorizedArrayType result = u;
847 return result;
848}
849
850
851
863template <typename Number, std::size_t width>
864inline DEAL_II_ALWAYS_INLINE void
866 const std::array<const Number *, width> &ptrs,
867 const unsigned int offset)
868{
869 for (unsigned int v = 0; v < width; ++v)
870 out.data[v] = ptrs[v][offset];
871}
872
873
874
900template <typename Number, std::size_t width>
901inline DEAL_II_ALWAYS_INLINE void
902vectorized_load_and_transpose(const unsigned int n_entries,
903 const Number *in,
904 const unsigned int *offsets,
906{
907 for (unsigned int i = 0; i < n_entries; ++i)
908 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
909 out[i][v] = in[offsets[v] + i];
910}
911
912
924template <typename Number, std::size_t width>
925inline DEAL_II_ALWAYS_INLINE void
926vectorized_load_and_transpose(const unsigned int n_entries,
927 const std::array<const Number *, width> &in,
929{
930 for (unsigned int i = 0; i < n_entries; ++i)
931 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
932 out[i][v] = in[v][i];
933}
934
935
936
975template <typename Number, std::size_t width>
976inline DEAL_II_ALWAYS_INLINE void
978 const unsigned int n_entries,
980 const unsigned int *offsets,
981 Number *out)
982{
983 if (add_into)
984 for (unsigned int i = 0; i < n_entries; ++i)
985 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
986 out[offsets[v] + i] += in[i][v];
987 else
988 for (unsigned int i = 0; i < n_entries; ++i)
989 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
990 out[offsets[v] + i] = in[i][v];
991}
992
993
1005template <typename Number, std::size_t width>
1006inline DEAL_II_ALWAYS_INLINE void
1008 const unsigned int n_entries,
1010 const std::array<Number *, width> &out)
1011{
1012 if (add_into)
1013 for (unsigned int i = 0; i < n_entries; ++i)
1014 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
1015 out[v][i] += in[i][v];
1016 else
1017 for (unsigned int i = 0; i < n_entries; ++i)
1018 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
1019 out[v][i] = in[i][v];
1020}
1021
1022
1024
1025#ifndef DOXYGEN
1026
1027# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__ARM_NEON)
1028
1032template <>
1033class VectorizedArray<double, 2>
1034 : public VectorizedArrayBase<VectorizedArray<double, 2>, 2>
1035{
1036public:
1040 using value_type = double;
1041
1046 static constexpr bool is_implemented = true;
1047
1052 VectorizedArray() = default;
1053
1057 VectorizedArray(const double scalar)
1058 {
1059 this->operator=(scalar);
1060 }
1061
1065 template <typename U>
1066 VectorizedArray(const std::initializer_list<U> &list)
1067 : VectorizedArrayBase<VectorizedArray<double, 2>, 2>(list)
1068 {}
1069
1074 operator=(const double x) &
1075 {
1076 data = vdupq_n_f64(x);
1077 return *this;
1078 }
1079
1086 operator=(const double scalar) && = delete;
1087
1091 double &
1092 operator[](const unsigned int comp)
1093 {
1094 return *(reinterpret_cast<double *>(&data) + comp);
1095 }
1096
1100 const double &
1101 operator[](const unsigned int comp) const
1102 {
1103 return *(reinterpret_cast<const double *>(&data) + comp);
1104 }
1105
1110 operator+=(const VectorizedArray &vec)
1111 {
1112 data = vaddq_f64(data, vec.data);
1113 return *this;
1114 }
1115
1120 operator-=(const VectorizedArray &vec)
1121 {
1122 data = vsubq_f64(data, vec.data);
1123 return *this;
1124 }
1125
1130 operator*=(const VectorizedArray &vec)
1131 {
1132 data = vmulq_f64(data, vec.data);
1133 return *this;
1134 }
1135
1140 operator/=(const VectorizedArray &vec)
1141 {
1142 data = vdivq_f64(data, vec.data);
1143 return *this;
1144 }
1145
1151 void
1152 load(const double *ptr)
1153 {
1154 data = vld1q_f64(ptr);
1155 }
1156
1158 void
1159 load(const float *ptr)
1160 {
1162 for (unsigned int i = 0; i < 2; ++i)
1163 data[i] = ptr[i];
1164 }
1165
1172 void
1173 store(double *ptr) const
1174 {
1175 vst1q_f64(ptr, data);
1176 }
1177
1179 void
1180 store(float *ptr) const
1181 {
1183 for (unsigned int i = 0; i < 2; ++i)
1184 ptr[i] = data[i];
1185 }
1186
1192 void
1193 streaming_store(double *ptr) const
1194 {
1195 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
1196 ExcMessage("Memory not aligned"));
1197 vst1q_f64(ptr, data);
1198 }
1199
1203 void
1204 gather(const double *base_ptr, const unsigned int *offsets)
1205 {
1206 this->operator=(0.);
1207 for (unsigned int i = 0; i < 2; ++i)
1208 if (offsets[i] != numbers::invalid_unsigned_int)
1209 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
1210 }
1211
1215 void
1216 scatter(const unsigned int *offsets, double *base_ptr) const
1217 {
1218 for (unsigned int i = 0; i < 2; ++i)
1219 if (offsets[i] != numbers::invalid_unsigned_int)
1220 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
1221 }
1222
1227 double
1228 sum() const
1229 {
1230 return vaddvq_f64(data);
1231 }
1232
1237 get_floor() const
1238 {
1239 VectorizedArray res;
1240 res.data = vrndmq_f64(data);
1241 return res;
1242 }
1243
1249 mutable float64x2_t data;
1250
1251private:
1257 get_sqrt() const
1258 {
1259 VectorizedArray res;
1260 res.data = vsqrtq_f64(data);
1261 return res;
1262 }
1263
1269 get_abs() const
1270 {
1271 VectorizedArray res;
1272 res.data = vabsq_f64(data);
1273 return res;
1274 }
1275
1281 get_max(const VectorizedArray &other) const
1282 {
1283 VectorizedArray res;
1284 res.data = vmaxq_f64(data, other.data);
1285 return res;
1286 }
1287
1293 get_min(const VectorizedArray &other) const
1294 {
1295 VectorizedArray res;
1296 res.data = vminq_f64(data, other.data);
1297 return res;
1298 }
1299
1300 // Make a few functions friends.
1301 template <typename Number2, std::size_t width2>
1304 template <typename Number2, std::size_t width2>
1307 template <typename Number2, std::size_t width2>
1311 template <typename Number2, std::size_t width2>
1315};
1316
1320template <>
1321class VectorizedArray<float, 4>
1322 : public VectorizedArrayBase<VectorizedArray<float, 4>, 4>
1323{
1324public:
1328 using value_type = float;
1329
1334 static constexpr bool is_implemented = true;
1335
1340 VectorizedArray() = default;
1341
1345 VectorizedArray(const float scalar)
1346 {
1347 this->operator=(scalar);
1348 }
1349
1353 template <typename U>
1354 VectorizedArray(const std::initializer_list<U> &list)
1355 : VectorizedArrayBase<VectorizedArray<float, 4>, 4>(list)
1356 {}
1357
1362 operator=(const float x) &
1363 {
1364 data = vdupq_n_f32(x);
1365 return *this;
1366 }
1367
1374 operator=(const float scalar) && = delete;
1375
1379 value_type &
1380 operator[](const unsigned int comp)
1381 {
1382 return *(reinterpret_cast<float *>(&data) + comp);
1383 }
1384
1388 const value_type &
1389 operator[](const unsigned int comp) const
1390 {
1391 return *(reinterpret_cast<const float *>(&data) + comp);
1392 }
1393
1398 operator+=(const VectorizedArray &vec)
1399 {
1400 data = vaddq_f32(data, vec.data);
1401 return *this;
1402 }
1403
1408 operator-=(const VectorizedArray &vec)
1409 {
1410 data = vsubq_f32(data, vec.data);
1411 return *this;
1412 }
1413
1418 operator*=(const VectorizedArray &vec)
1419 {
1420 data = vmulq_f32(data, vec.data);
1421 return *this;
1422 }
1423
1428 operator/=(const VectorizedArray &vec)
1429 {
1430 data = vdivq_f32(data, vec.data);
1431 return *this;
1432 }
1433
1439 void
1440 load(const float *ptr)
1441 {
1442 data = vld1q_f32(ptr);
1443 }
1444
1451 void
1452 store(float *ptr) const
1453 {
1454 vst1q_f32(ptr, data);
1455 }
1456
1462 void
1463 streaming_store(float *ptr) const
1464 {
1465 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
1466 ExcMessage("Memory not aligned"));
1467 vst1q_f32(ptr, data);
1468 }
1469
1473 void
1474 gather(const float *base_ptr, const unsigned int *offsets)
1475 {
1476 this->operator=(0.f);
1477 for (unsigned int i = 0; i < 4; ++i)
1478 if (offsets[i] != numbers::invalid_unsigned_int)
1479 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
1480 }
1481
1485 void
1486 scatter(const unsigned int *offsets, float *base_ptr) const
1487 {
1488 for (unsigned int i = 0; i < 4; ++i)
1489 if (offsets[i] != numbers::invalid_unsigned_int)
1490 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
1491 }
1492
1497 float
1498 sum() const
1499 {
1500 return vaddvq_f32(data);
1501 }
1502
1503
1508 get_floor() const
1509 {
1510 VectorizedArray res;
1511 res.data = vrndmq_f32(data);
1512 return res;
1513 }
1514
1520 mutable float32x4_t data;
1521
1522private:
1528 get_sqrt() const
1529 {
1530 VectorizedArray res;
1531 res.data = vsqrtq_f32(data);
1532 return res;
1533 }
1534
1540 get_abs() const
1541 {
1542 VectorizedArray res;
1543 res.data = vabsq_f32(data);
1544 return res;
1545 }
1546
1552 get_max(const VectorizedArray &other) const
1553 {
1554 VectorizedArray res;
1555 res.data = vmaxq_f32(data, other.data);
1556 return res;
1557 }
1558
1564 get_min(const VectorizedArray &other) const
1565 {
1566 VectorizedArray res;
1567 res.data = vminq_f32(data, other.data);
1568 return res;
1569 }
1570
1571 // Make a few functions friends.
1572 template <typename Number2, std::size_t width2>
1575 template <typename Number2, std::size_t width2>
1578 template <typename Number2, std::size_t width2>
1582 template <typename Number2, std::size_t width2>
1586};
1587
1588
1589# endif
1590
1591# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__SSE2__)
1592
1596template <>
1597class VectorizedArray<double, 2>
1598 : public VectorizedArrayBase<VectorizedArray<double, 2>, 2>
1599{
1600public:
1604 using value_type = double;
1605
1610 static constexpr bool is_implemented = true;
1611
1616 VectorizedArray() = default;
1617
1621 VectorizedArray(const double scalar)
1622 {
1623 this->operator=(scalar);
1624 }
1625
1629 template <typename U>
1630 VectorizedArray(const std::initializer_list<U> &list)
1631 : VectorizedArrayBase<VectorizedArray<double, 2>, 2>(list)
1632 {}
1633
1639 operator=(const double x) &
1640 {
1641 data = _mm_set1_pd(x);
1642 return *this;
1643 }
1644
1651 operator=(const double scalar) && = delete;
1652
1657 double &
1658 operator[](const unsigned int comp)
1659 {
1660 AssertIndexRange(comp, 2);
1661 return *(reinterpret_cast<double *>(&data) + comp);
1662 }
1663
1668 const double &
1669 operator[](const unsigned int comp) const
1670 {
1671 AssertIndexRange(comp, 2);
1672 return *(reinterpret_cast<const double *>(&data) + comp);
1673 }
1674
1680 operator+=(const VectorizedArray &vec)
1681 {
1682# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1683 data += vec.data;
1684# else
1685 data = _mm_add_pd(data, vec.data);
1686# endif
1687 return *this;
1688 }
1689
1695 operator-=(const VectorizedArray &vec)
1696 {
1697# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1698 data -= vec.data;
1699# else
1700 data = _mm_sub_pd(data, vec.data);
1701# endif
1702 return *this;
1703 }
1704
1710 operator*=(const VectorizedArray &vec)
1711 {
1712# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1713 data *= vec.data;
1714# else
1715 data = _mm_mul_pd(data, vec.data);
1716# endif
1717 return *this;
1718 }
1719
1725 operator/=(const VectorizedArray &vec)
1726 {
1727# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1728 data /= vec.data;
1729# else
1730 data = _mm_div_pd(data, vec.data);
1731# endif
1732 return *this;
1733 }
1734
1741 void
1742 load(const double *ptr)
1743 {
1744 data = _mm_loadu_pd(ptr);
1745 }
1746
1748 void
1749 load(const float *ptr)
1750 {
1752 for (unsigned int i = 0; i < 2; ++i)
1753 data[i] = ptr[i];
1754 }
1755
1763 void
1764 store(double *ptr) const
1765 {
1766 _mm_storeu_pd(ptr, data);
1767 }
1768
1770 void
1771 store(float *ptr) const
1772 {
1774 for (unsigned int i = 0; i < 2; ++i)
1775 ptr[i] = data[i];
1776 }
1777
1783 void
1784 streaming_store(double *ptr) const
1785 {
1786 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
1787 ExcMessage("Memory not aligned"));
1788 _mm_stream_pd(ptr, data);
1789 }
1790
1795 void
1796 gather(const double *base_ptr, const unsigned int *offsets)
1797 {
1798 this->operator=(0.);
1799 for (unsigned int i = 0; i < 2; ++i)
1800 if (offsets[i] != numbers::invalid_unsigned_int)
1801 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
1802 }
1803
1808 void
1809 scatter(const unsigned int *offsets, double *base_ptr) const
1810 {
1811 for (unsigned int i = 0; i < 2; ++i)
1812 if (offsets[i] != numbers::invalid_unsigned_int)
1813 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
1814 }
1815
1820 double
1821 sum() const
1822 {
1823 __m128d t1 = _mm_unpackhi_pd(data, data);
1824 __m128d t2 = _mm_add_pd(data, t1);
1825 return _mm_cvtsd_f64(t2);
1826 }
1827
1832 get_floor() const
1833 {
1834 VectorizedArray res;
1835 for (std::size_t i = 0; i < 2; ++i)
1836 res.data[i] = std::floor(data[i]);
1837 return res;
1838 }
1839
1845 __m128d data;
1846
1847private:
1854 get_sqrt() const
1855 {
1856 VectorizedArray res;
1857 res.data = _mm_sqrt_pd(data);
1858 return res;
1859 }
1860
1867 get_abs() const
1868 {
1869 // to compute the absolute value, perform
1870 // bitwise andnot with -0. This will leave all
1871 // value and exponent bits unchanged but force
1872 // the sign value to +.
1873 __m128d mask = _mm_set1_pd(-0.);
1874 VectorizedArray res;
1875 res.data = _mm_andnot_pd(mask, data);
1876 return res;
1877 }
1878
1885 get_max(const VectorizedArray &other) const
1886 {
1887 VectorizedArray res;
1888 res.data = _mm_max_pd(data, other.data);
1889 return res;
1890 }
1891
1898 get_min(const VectorizedArray &other) const
1899 {
1900 VectorizedArray res;
1901 res.data = _mm_min_pd(data, other.data);
1902 return res;
1903 }
1904
1905 // Make a few functions friends.
1906 template <typename Number2, std::size_t width2>
1909 template <typename Number2, std::size_t width2>
1912 template <typename Number2, std::size_t width2>
1916 template <typename Number2, std::size_t width2>
1920};
1921
1922
1923
1927template <>
1928inline DEAL_II_ALWAYS_INLINE void
1929vectorized_load_and_transpose(const unsigned int n_entries,
1930 const std::array<const double *, 2> &in,
1932{
1933 const unsigned int n_chunks = n_entries / 2;
1934 for (unsigned int i = 0; i < n_chunks; ++i)
1935 {
1936 __m128d u0 = _mm_loadu_pd(in[0] + 2 * i);
1937 __m128d u1 = _mm_loadu_pd(in[1] + 2 * i);
1938 out[2 * i + 0].data = _mm_unpacklo_pd(u0, u1);
1939 out[2 * i + 1].data = _mm_unpackhi_pd(u0, u1);
1940 }
1941
1942 // remainder loop of work that does not divide by 2
1943 for (unsigned int i = 2 * n_chunks; i < n_entries; ++i)
1944 for (unsigned int v = 0; v < 2; ++v)
1945 out[i][v] = in[v][i];
1946}
1947
1948
1949
1953template <>
1954inline DEAL_II_ALWAYS_INLINE void
1955vectorized_load_and_transpose(const unsigned int n_entries,
1956 const double *in,
1957 const unsigned int *offsets,
1959{
1961 {{in + offsets[0], in + offsets[1]}},
1962 out);
1963}
1964
1965
1966
1970template <>
1971inline DEAL_II_ALWAYS_INLINE void
1972vectorized_transpose_and_store(const bool add_into,
1973 const unsigned int n_entries,
1975 const std::array<double *, 2> &out)
1976{
1977 const unsigned int n_chunks = n_entries / 2;
1978 if (add_into)
1979 {
1980 for (unsigned int i = 0; i < n_chunks; ++i)
1981 {
1982 __m128d u0 = in[2 * i + 0].data;
1983 __m128d u1 = in[2 * i + 1].data;
1984 __m128d res0 = _mm_unpacklo_pd(u0, u1);
1985 __m128d res1 = _mm_unpackhi_pd(u0, u1);
1986 _mm_storeu_pd(out[0] + 2 * i,
1987 _mm_add_pd(_mm_loadu_pd(out[0] + 2 * i), res0));
1988 _mm_storeu_pd(out[1] + 2 * i,
1989 _mm_add_pd(_mm_loadu_pd(out[1] + 2 * i), res1));
1990 }
1991
1992 // remainder loop for lengths that do not divide by 2
1993 for (unsigned int i = 2 * n_chunks; i < n_entries; ++i)
1994 for (unsigned int v = 0; v < 2; ++v)
1995 out[v][i] += in[i][v];
1996 }
1997 else
1998 {
1999 for (unsigned int i = 0; i < n_chunks; ++i)
2000 {
2001 __m128d u0 = in[2 * i + 0].data;
2002 __m128d u1 = in[2 * i + 1].data;
2003 __m128d res0 = _mm_unpacklo_pd(u0, u1);
2004 __m128d res1 = _mm_unpackhi_pd(u0, u1);
2005 _mm_storeu_pd(out[0] + 2 * i, res0);
2006 _mm_storeu_pd(out[1] + 2 * i, res1);
2007 }
2008
2009 // remainder loop for lengths that do not divide by 2
2010 for (unsigned int i = 2 * n_chunks; i < n_entries; ++i)
2011 for (unsigned int v = 0; v < 2; ++v)
2012 out[v][i] = in[i][v];
2013 }
2014}
2015
2016
2017
2021template <>
2022inline DEAL_II_ALWAYS_INLINE void
2023vectorized_transpose_and_store(const bool add_into,
2024 const unsigned int n_entries,
2026 const unsigned int *offsets,
2027 double *out)
2028{
2030 n_entries,
2031 in,
2032 {{out + offsets[0], out + offsets[1]}});
2033}
2034
2035
2036
2040template <>
2041class VectorizedArray<float, 4>
2042 : public VectorizedArrayBase<VectorizedArray<float, 4>, 4>
2043{
2044public:
2048 using value_type = float;
2049
2054 static constexpr bool is_implemented = true;
2055
2060 VectorizedArray() = default;
2061
2065 VectorizedArray(const float scalar)
2066 {
2067 this->operator=(scalar);
2068 }
2069
2073 template <typename U>
2074 VectorizedArray(const std::initializer_list<U> &list)
2075 : VectorizedArrayBase<VectorizedArray<float, 4>, 4>(list)
2076 {}
2077
2083 operator=(const float x) &
2084 {
2085 data = _mm_set1_ps(x);
2086 return *this;
2087 }
2088
2095 operator=(const float scalar) && = delete;
2096
2101 float &
2102 operator[](const unsigned int comp)
2103 {
2104 AssertIndexRange(comp, 4);
2105 return *(reinterpret_cast<float *>(&data) + comp);
2106 }
2107
2112 const float &
2113 operator[](const unsigned int comp) const
2114 {
2115 AssertIndexRange(comp, 4);
2116 return *(reinterpret_cast<const float *>(&data) + comp);
2117 }
2118
2124 operator+=(const VectorizedArray &vec)
2125 {
2126# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2127 data += vec.data;
2128# else
2129 data = _mm_add_ps(data, vec.data);
2130# endif
2131 return *this;
2132 }
2133
2139 operator-=(const VectorizedArray &vec)
2140 {
2141# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2142 data -= vec.data;
2143# else
2144 data = _mm_sub_ps(data, vec.data);
2145# endif
2146 return *this;
2147 }
2148
2154 operator*=(const VectorizedArray &vec)
2155 {
2156# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2157 data *= vec.data;
2158# else
2159 data = _mm_mul_ps(data, vec.data);
2160# endif
2161 return *this;
2162 }
2163
2169 operator/=(const VectorizedArray &vec)
2170 {
2171# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2172 data /= vec.data;
2173# else
2174 data = _mm_div_ps(data, vec.data);
2175# endif
2176 return *this;
2177 }
2178
2185 void
2186 load(const float *ptr)
2187 {
2188 data = _mm_loadu_ps(ptr);
2189 }
2190
2198 void
2199 store(float *ptr) const
2200 {
2201 _mm_storeu_ps(ptr, data);
2202 }
2203
2209 void
2210 streaming_store(float *ptr) const
2211 {
2212 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
2213 ExcMessage("Memory not aligned"));
2214 _mm_stream_ps(ptr, data);
2215 }
2216
2221 void
2222 gather(const float *base_ptr, const unsigned int *offsets)
2223 {
2224 this->operator=(0.f);
2225 for (unsigned int i = 0; i < 4; ++i)
2226 if (offsets[i] != numbers::invalid_unsigned_int)
2227 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
2228 }
2229
2234 void
2235 scatter(const unsigned int *offsets, float *base_ptr) const
2236 {
2237 for (unsigned int i = 0; i < 4; ++i)
2238 if (offsets[i] != numbers::invalid_unsigned_int)
2239 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
2240 }
2241
2246 float
2247 sum() const
2248 {
2249 __m128 t1 = _mm_movehl_ps(data, data);
2250 __m128 t2 = _mm_add_ps(data, t1);
2251 __m128 t3 = _mm_shuffle_ps(t2, t2, 1);
2252 __m128 t4 = _mm_add_ss(t2, t3);
2253 return _mm_cvtss_f32(t4);
2254 }
2255
2260 get_floor() const
2261 {
2262 VectorizedArray res;
2263 for (std::size_t i = 0; i < 4; ++i)
2264 res.data[i] = std::floor(data[i]);
2265 return res;
2266 }
2267
2273 __m128 data;
2274
2275private:
2282 get_sqrt() const
2283 {
2284 VectorizedArray res;
2285 res.data = _mm_sqrt_ps(data);
2286 return res;
2287 }
2288
2295 get_abs() const
2296 {
2297 // to compute the absolute value, perform bitwise andnot with -0. This
2298 // will leave all value and exponent bits unchanged but force the sign
2299 // value to +.
2300 __m128 mask = _mm_set1_ps(-0.f);
2301 VectorizedArray res;
2302 res.data = _mm_andnot_ps(mask, data);
2303 return res;
2304 }
2305
2312 get_max(const VectorizedArray &other) const
2313 {
2314 VectorizedArray res;
2315 res.data = _mm_max_ps(data, other.data);
2316 return res;
2317 }
2318
2325 get_min(const VectorizedArray &other) const
2326 {
2327 VectorizedArray res;
2328 res.data = _mm_min_ps(data, other.data);
2329 return res;
2330 }
2331
2332 // Make a few functions friends.
2333 template <typename Number2, std::size_t width2>
2336 template <typename Number2, std::size_t width2>
2339 template <typename Number2, std::size_t width2>
2343 template <typename Number2, std::size_t width2>
2347};
2348
2349
2350
2354template <>
2355inline DEAL_II_ALWAYS_INLINE void
2356vectorized_load_and_transpose(const unsigned int n_entries,
2357 const std::array<const float *, 4> &in,
2359{
2360 const unsigned int n_chunks = n_entries / 4;
2361 for (unsigned int i = 0; i < n_chunks; ++i)
2362 {
2363 __m128 u0 = _mm_loadu_ps(in[0] + 4 * i);
2364 __m128 u1 = _mm_loadu_ps(in[1] + 4 * i);
2365 __m128 u2 = _mm_loadu_ps(in[2] + 4 * i);
2366 __m128 u3 = _mm_loadu_ps(in[3] + 4 * i);
2367 __m128 v0 = _mm_shuffle_ps(u0, u1, 0x44);
2368 __m128 v1 = _mm_shuffle_ps(u0, u1, 0xee);
2369 __m128 v2 = _mm_shuffle_ps(u2, u3, 0x44);
2370 __m128 v3 = _mm_shuffle_ps(u2, u3, 0xee);
2371 out[4 * i + 0].data = _mm_shuffle_ps(v0, v2, 0x88);
2372 out[4 * i + 1].data = _mm_shuffle_ps(v0, v2, 0xdd);
2373 out[4 * i + 2].data = _mm_shuffle_ps(v1, v3, 0x88);
2374 out[4 * i + 3].data = _mm_shuffle_ps(v1, v3, 0xdd);
2375 }
2376
2377 // remainder loop for lengths not divisible by 4
2378 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2379 for (unsigned int v = 0; v < 4; ++v)
2380 out[i][v] = in[v][i];
2381}
2382
2383
2384
2388template <>
2389inline DEAL_II_ALWAYS_INLINE void
2390vectorized_load_and_transpose(const unsigned int n_entries,
2391 const float *in,
2392 const unsigned int *offsets,
2394{
2396 n_entries,
2397 {{in + offsets[0], in + offsets[1], in + offsets[2], in + offsets[3]}},
2398 out);
2399}
2400
2401
2402
2406template <>
2407inline DEAL_II_ALWAYS_INLINE void
2408vectorized_transpose_and_store(const bool add_into,
2409 const unsigned int n_entries,
2410 const VectorizedArray<float, 4> *in,
2411 const std::array<float *, 4> &out)
2412{
2413 const unsigned int n_chunks = n_entries / 4;
2414 for (unsigned int i = 0; i < n_chunks; ++i)
2415 {
2416 __m128 u0 = in[4 * i + 0].data;
2417 __m128 u1 = in[4 * i + 1].data;
2418 __m128 u2 = in[4 * i + 2].data;
2419 __m128 u3 = in[4 * i + 3].data;
2420 __m128 t0 = _mm_shuffle_ps(u0, u1, 0x44);
2421 __m128 t1 = _mm_shuffle_ps(u0, u1, 0xee);
2422 __m128 t2 = _mm_shuffle_ps(u2, u3, 0x44);
2423 __m128 t3 = _mm_shuffle_ps(u2, u3, 0xee);
2424 u0 = _mm_shuffle_ps(t0, t2, 0x88);
2425 u1 = _mm_shuffle_ps(t0, t2, 0xdd);
2426 u2 = _mm_shuffle_ps(t1, t3, 0x88);
2427 u3 = _mm_shuffle_ps(t1, t3, 0xdd);
2428
2429 // Cannot use the same store instructions in both paths of the 'if'
2430 // because the compiler cannot know that there is no aliasing between
2431 // pointers
2432 if (add_into)
2433 {
2434 u0 = _mm_add_ps(_mm_loadu_ps(out[0] + 4 * i), u0);
2435 _mm_storeu_ps(out[0] + 4 * i, u0);
2436 u1 = _mm_add_ps(_mm_loadu_ps(out[1] + 4 * i), u1);
2437 _mm_storeu_ps(out[1] + 4 * i, u1);
2438 u2 = _mm_add_ps(_mm_loadu_ps(out[2] + 4 * i), u2);
2439 _mm_storeu_ps(out[2] + 4 * i, u2);
2440 u3 = _mm_add_ps(_mm_loadu_ps(out[3] + 4 * i), u3);
2441 _mm_storeu_ps(out[3] + 4 * i, u3);
2442 }
2443 else
2444 {
2445 _mm_storeu_ps(out[0] + 4 * i, u0);
2446 _mm_storeu_ps(out[1] + 4 * i, u1);
2447 _mm_storeu_ps(out[2] + 4 * i, u2);
2448 _mm_storeu_ps(out[3] + 4 * i, u3);
2449 }
2450 }
2451
2452 // remainder loop of work that does not divide by 4
2453 if (add_into)
2454 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2455 for (unsigned int v = 0; v < 4; ++v)
2456 out[v][i] += in[i][v];
2457 else
2458 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2459 for (unsigned int v = 0; v < 4; ++v)
2460 out[v][i] = in[i][v];
2461}
2462
2463
2464
2468template <>
2469inline DEAL_II_ALWAYS_INLINE void
2470vectorized_transpose_and_store(const bool add_into,
2471 const unsigned int n_entries,
2472 const VectorizedArray<float, 4> *in,
2473 const unsigned int *offsets,
2474 float *out)
2475{
2477 add_into,
2478 n_entries,
2479 in,
2480 {{out + offsets[0], out + offsets[1], out + offsets[2], out + offsets[3]}});
2481}
2482
2483
2484
2485# endif // if DEAL_II_VECTORIZATION_WIDTH_IN_BITS > 0 && defined(__SSE2__)
2486
2487# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && defined(__AVX__)
2488
2492template <>
2493class VectorizedArray<double, 4>
2494 : public VectorizedArrayBase<VectorizedArray<double, 4>, 4>
2495{
2496public:
2500 using value_type = double;
2501
2506 static constexpr bool is_implemented = true;
2507
2512 VectorizedArray() = default;
2513
2517 VectorizedArray(const double scalar)
2518 {
2519 this->operator=(scalar);
2520 }
2521
2525 template <typename U>
2526 VectorizedArray(const std::initializer_list<U> &list)
2527 : VectorizedArrayBase<VectorizedArray<double, 4>, 4>(list)
2528 {}
2529
2535 operator=(const double x) &
2536 {
2537 data = _mm256_set1_pd(x);
2538 return *this;
2539 }
2540
2547 operator=(const double scalar) && = delete;
2548
2553 double &
2554 operator[](const unsigned int comp)
2555 {
2556 AssertIndexRange(comp, 4);
2557 return *(reinterpret_cast<double *>(&data) + comp);
2558 }
2559
2564 const double &
2565 operator[](const unsigned int comp) const
2566 {
2567 AssertIndexRange(comp, 4);
2568 return *(reinterpret_cast<const double *>(&data) + comp);
2569 }
2570
2576 operator+=(const VectorizedArray &vec)
2577 {
2578 // if the compiler supports vector arithmetic, we can simply use +=
2579 // operator on the given data type. this allows the compiler to combine
2580 // additions with multiplication (fused multiply-add) if those
2581 // instructions are available. Otherwise, we need to use the built-in
2582 // intrinsic command for __m256d
2583# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2584 data += vec.data;
2585# else
2586 data = _mm256_add_pd(data, vec.data);
2587# endif
2588 return *this;
2589 }
2590
2596 operator-=(const VectorizedArray &vec)
2597 {
2598# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2599 data -= vec.data;
2600# else
2601 data = _mm256_sub_pd(data, vec.data);
2602# endif
2603 return *this;
2604 }
2610 operator*=(const VectorizedArray &vec)
2611 {
2612# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2613 data *= vec.data;
2614# else
2615 data = _mm256_mul_pd(data, vec.data);
2616# endif
2617 return *this;
2618 }
2619
2625 operator/=(const VectorizedArray &vec)
2626 {
2627# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2628 data /= vec.data;
2629# else
2630 data = _mm256_div_pd(data, vec.data);
2631# endif
2632 return *this;
2633 }
2634
2641 void
2642 load(const double *ptr)
2643 {
2644 data = _mm256_loadu_pd(ptr);
2645 }
2646
2648 void
2649 load(const float *ptr)
2650 {
2651 data = _mm256_cvtps_pd(_mm_loadu_ps(ptr));
2652 }
2653
2661 void
2662 store(double *ptr) const
2663 {
2664 _mm256_storeu_pd(ptr, data);
2665 }
2666
2668 void
2669 store(float *ptr) const
2670 {
2671 _mm_storeu_ps(ptr, _mm256_cvtpd_ps(data));
2672 }
2673
2679 void
2680 streaming_store(double *ptr) const
2681 {
2682 Assert(reinterpret_cast<std::size_t>(ptr) % 32 == 0,
2683 ExcMessage("Memory not aligned"));
2684 _mm256_stream_pd(ptr, data);
2685 }
2686
2691 void
2692 gather(const double *base_ptr, const unsigned int *offsets)
2693 {
2694# if defined(__AVX2__) && defined(DEAL_II_USE_VECTORIZATION_GATHER)
2695 // unfortunately, there does not appear to be a 128 bit integer load, so
2696 // do it by some reinterpret casts here. this is allowed because the Intel
2697 // API allows aliasing between different vector types.
2698 const __m128 index_val =
2699 _mm_loadu_ps(reinterpret_cast<const float *>(offsets));
2700 const __m128i index = *reinterpret_cast<const __m128i *>(&index_val);
2701
2702 // work around a warning with gcc-12 about an uninitialized initial state
2703 // for gather by starting with a zero guess, even though all lanes will be
2704 // overwritten
2705 __m256d zero = _mm256_setzero_pd();
2706 __m128i neq32 = _mm_andnot_si128(_mm_cmpeq_epi32(index, _mm_set1_epi32(-1)),
2707 _mm_set1_epi32(-1));
2708
2709 __m256d mask = _mm256_castsi256_pd(_mm256_cvtepi32_epi64(neq32));
2710
2711 data = _mm256_mask_i32gather_pd(zero, base_ptr, index, mask, 8);
2712# else
2713 this->operator=(0.);
2714 for (unsigned int i = 0; i < 4; ++i)
2715 if (offsets[i] != numbers::invalid_unsigned_int)
2716 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
2717# endif
2718 }
2719
2724 void
2725 scatter(const unsigned int *offsets, double *base_ptr) const
2726 {
2727 // no scatter operation in AVX/AVX2
2728 for (unsigned int i = 0; i < 4; ++i)
2729 if (offsets[i] != numbers::invalid_unsigned_int)
2730 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
2731 }
2732
2737 double
2738 sum() const
2739 {
2741 t1.data = _mm_add_pd(this->get_lower(), this->get_upper());
2742 return t1.sum();
2743 }
2744
2749 get_floor() const
2750 {
2751 VectorizedArray res;
2752 res.data = _mm256_floor_pd(data);
2753 return res;
2754 }
2755
2761 __m256d data;
2762
2763private:
2768 __m128d
2769 get_lower() const
2770 {
2771 return _mm256_castpd256_pd128(data);
2772 }
2773
2778 __m128d
2779 get_upper() const
2780 {
2781 return _mm256_extractf128_pd(data, 1);
2782 }
2783
2790 get_sqrt() const
2791 {
2792 VectorizedArray res;
2793 res.data = _mm256_sqrt_pd(data);
2794 return res;
2795 }
2796
2803 get_abs() const
2804 {
2805 // to compute the absolute value, perform bitwise andnot with -0. This
2806 // will leave all value and exponent bits unchanged but force the sign
2807 // value to +.
2808 __m256d mask = _mm256_set1_pd(-0.);
2809 VectorizedArray res;
2810 res.data = _mm256_andnot_pd(mask, data);
2811 return res;
2812 }
2813
2820 get_max(const VectorizedArray &other) const
2821 {
2822 VectorizedArray res;
2823 res.data = _mm256_max_pd(data, other.data);
2824 return res;
2825 }
2826
2833 get_min(const VectorizedArray &other) const
2834 {
2835 VectorizedArray res;
2836 res.data = _mm256_min_pd(data, other.data);
2837 return res;
2838 }
2839
2840 // Make a few functions friends.
2841 template <typename Number2, std::size_t width2>
2844 template <typename Number2, std::size_t width2>
2847 template <typename Number2, std::size_t width2>
2851 template <typename Number2, std::size_t width2>
2855};
2856
2857
2858
2862template <>
2863inline DEAL_II_ALWAYS_INLINE void
2864vectorized_load_and_transpose(const unsigned int n_entries,
2865 const std::array<const double *, 4> &in,
2867{
2868 const unsigned int n_chunks = n_entries / 4;
2869 const double *in0 = in[0];
2870 const double *in1 = in[1];
2871 const double *in2 = in[2];
2872 const double *in3 = in[3];
2873
2874 for (unsigned int i = 0; i < n_chunks; ++i)
2875 {
2876 __m256d u0 = _mm256_loadu_pd(in0 + 4 * i);
2877 __m256d u1 = _mm256_loadu_pd(in1 + 4 * i);
2878 __m256d u2 = _mm256_loadu_pd(in2 + 4 * i);
2879 __m256d u3 = _mm256_loadu_pd(in3 + 4 * i);
2880 __m256d t0 = _mm256_permute2f128_pd(u0, u2, 0x20);
2881 __m256d t1 = _mm256_permute2f128_pd(u1, u3, 0x20);
2882 __m256d t2 = _mm256_permute2f128_pd(u0, u2, 0x31);
2883 __m256d t3 = _mm256_permute2f128_pd(u1, u3, 0x31);
2884 out[4 * i + 0].data = _mm256_unpacklo_pd(t0, t1);
2885 out[4 * i + 1].data = _mm256_unpackhi_pd(t0, t1);
2886 out[4 * i + 2].data = _mm256_unpacklo_pd(t2, t3);
2887 out[4 * i + 3].data = _mm256_unpackhi_pd(t2, t3);
2888 }
2889
2890 // Specialized remainder code
2891 const unsigned int i = 4 * n_chunks;
2892 const unsigned int remainder = n_entries - i;
2893 if (remainder == 3)
2894 {
2895 // Use masked load of 3 doubles (this avoids accessing in[x][3], which
2896 // might be an invalid address)
2897 __m256i mask{-1, -1, -1, 0};
2898 __m256d u0 = _mm256_maskload_pd(in[0] + i, mask);
2899 __m256d u1 = _mm256_maskload_pd(in[1] + i, mask);
2900 __m256d u2 = _mm256_maskload_pd(in[2] + i, mask);
2901 __m256d u3 = _mm256_maskload_pd(in[3] + i, mask);
2902 __m256d t0 = _mm256_permute2f128_pd(u0, u2, 0x20);
2903 __m256d t1 = _mm256_permute2f128_pd(u1, u3, 0x20);
2904 __m256d t2 = _mm256_permute2f128_pd(u0, u2, 0x31);
2905 __m256d t3 = _mm256_permute2f128_pd(u1, u3, 0x31);
2906 out[i + 0].data = _mm256_unpacklo_pd(t0, t1);
2907 out[i + 1].data = _mm256_unpackhi_pd(t0, t1);
2908 out[i + 2].data = _mm256_unpacklo_pd(t2, t3);
2909 }
2910 else if (remainder == 2)
2911 {
2912 // Use 128 bit loads
2913 __m256d t0, t1 = {};
2914 t0 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[0] + i), 0);
2915 t0 = _mm256_insertf128_pd(t0, _mm_loadu_pd(in[2] + i), 1);
2916 t1 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[1] + i), 0);
2917 t1 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[3] + i), 1);
2918
2919 out[i + 0].data = _mm256_unpacklo_pd(t0, t1);
2920 out[i + 1].data = _mm256_unpackhi_pd(t0, t1);
2921 }
2922 else if (remainder == 1)
2923 for (unsigned int v = 0; v < 4; ++v)
2924 out[i][v] = in[v][i];
2925}
2926
2927
2928
2932template <>
2933inline DEAL_II_ALWAYS_INLINE void
2934vectorized_load_and_transpose(const unsigned int n_entries,
2935 const double *in,
2936 const unsigned int *offsets,
2938{
2940 n_entries,
2941 {{in + offsets[0], in + offsets[1], in + offsets[2], in + offsets[3]}},
2942 out);
2943}
2944
2945
2946
2950template <>
2951inline DEAL_II_ALWAYS_INLINE void
2952vectorized_transpose_and_store(const bool add_into,
2953 const unsigned int n_entries,
2955 const std::array<double *, 4> &out)
2956{
2957 const unsigned int n_chunks = n_entries / 4;
2958 double *out0 = out[0];
2959 double *out1 = out[1];
2960 double *out2 = out[2];
2961 double *out3 = out[3];
2962 for (unsigned int i = 0; i < n_chunks; ++i)
2963 {
2964 __m256d u0 = in[4 * i + 0].data;
2965 __m256d u1 = in[4 * i + 1].data;
2966 __m256d u2 = in[4 * i + 2].data;
2967 __m256d u3 = in[4 * i + 3].data;
2968 __m256d t0 = _mm256_permute2f128_pd(u0, u2, 0x20);
2969 __m256d t1 = _mm256_permute2f128_pd(u1, u3, 0x20);
2970 __m256d t2 = _mm256_permute2f128_pd(u0, u2, 0x31);
2971 __m256d t3 = _mm256_permute2f128_pd(u1, u3, 0x31);
2972 __m256d res0 = _mm256_unpacklo_pd(t0, t1);
2973 __m256d res1 = _mm256_unpackhi_pd(t0, t1);
2974 __m256d res2 = _mm256_unpacklo_pd(t2, t3);
2975 __m256d res3 = _mm256_unpackhi_pd(t2, t3);
2976
2977 // Cannot use the same store instructions in both paths of the 'if'
2978 // because the compiler cannot know that there is no aliasing between
2979 // pointers
2980 if (add_into)
2981 {
2982 res0 = _mm256_add_pd(_mm256_loadu_pd(out0 + 4 * i), res0);
2983 _mm256_storeu_pd(out0 + 4 * i, res0);
2984 res1 = _mm256_add_pd(_mm256_loadu_pd(out1 + 4 * i), res1);
2985 _mm256_storeu_pd(out1 + 4 * i, res1);
2986 res2 = _mm256_add_pd(_mm256_loadu_pd(out2 + 4 * i), res2);
2987 _mm256_storeu_pd(out2 + 4 * i, res2);
2988 res3 = _mm256_add_pd(_mm256_loadu_pd(out3 + 4 * i), res3);
2989 _mm256_storeu_pd(out3 + 4 * i, res3);
2990 }
2991 else
2992 {
2993 _mm256_storeu_pd(out0 + 4 * i, res0);
2994 _mm256_storeu_pd(out1 + 4 * i, res1);
2995 _mm256_storeu_pd(out2 + 4 * i, res2);
2996 _mm256_storeu_pd(out3 + 4 * i, res3);
2997 }
2998 }
2999
3000 // remainder loop of work that does not divide by 4
3001 if (add_into)
3002 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
3003 for (unsigned int v = 0; v < 4; ++v)
3004 out[v][i] += in[i][v];
3005 else
3006 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
3007 for (unsigned int v = 0; v < 4; ++v)
3008 out[v][i] = in[i][v];
3009}
3010
3011
3012
3016template <>
3017inline DEAL_II_ALWAYS_INLINE void
3018vectorized_transpose_and_store(const bool add_into,
3019 const unsigned int n_entries,
3021 const unsigned int *offsets,
3022 double *out)
3023{
3025 add_into,
3026 n_entries,
3027 in,
3028 {{out + offsets[0], out + offsets[1], out + offsets[2], out + offsets[3]}});
3029}
3030
3031
3032
3036template <>
3037class VectorizedArray<float, 8>
3038 : public VectorizedArrayBase<VectorizedArray<float, 8>, 8>
3039{
3040public:
3044 using value_type = float;
3045
3050 static constexpr bool is_implemented = true;
3051
3056 VectorizedArray() = default;
3057
3061 VectorizedArray(const float scalar)
3062 {
3063 this->operator=(scalar);
3064 }
3065
3069 template <typename U>
3070 VectorizedArray(const std::initializer_list<U> &list)
3071 : VectorizedArrayBase<VectorizedArray<float, 8>, 8>(list)
3072 {}
3073
3079 operator=(const float x) &
3080 {
3081 data = _mm256_set1_ps(x);
3082 return *this;
3083 }
3084
3091 operator=(const float scalar) && = delete;
3092
3097 float &
3098 operator[](const unsigned int comp)
3099 {
3100 AssertIndexRange(comp, 8);
3101 return *(reinterpret_cast<float *>(&data) + comp);
3102 }
3103
3108 const float &
3109 operator[](const unsigned int comp) const
3110 {
3111 AssertIndexRange(comp, 8);
3112 return *(reinterpret_cast<const float *>(&data) + comp);
3113 }
3114
3120 operator+=(const VectorizedArray &vec)
3121 {
3122 // if the compiler supports vector arithmetic, we can simply use +=
3123 // operator on the given data type. this allows the compiler to combine
3124 // additions with multiplication (fused multiply-add) if those
3125 // instructions are available. Otherwise, we need to use the built-in
3126 // intrinsic command for __m256d
3127# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3128 data += vec.data;
3129# else
3130 data = _mm256_add_ps(data, vec.data);
3131# endif
3132 return *this;
3133 }
3134
3140 operator-=(const VectorizedArray &vec)
3141 {
3142# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3143 data -= vec.data;
3144# else
3145 data = _mm256_sub_ps(data, vec.data);
3146# endif
3147 return *this;
3148 }
3154 operator*=(const VectorizedArray &vec)
3155 {
3156# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3157 data *= vec.data;
3158# else
3159 data = _mm256_mul_ps(data, vec.data);
3160# endif
3161 return *this;
3162 }
3163
3169 operator/=(const VectorizedArray &vec)
3170 {
3171# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3172 data /= vec.data;
3173# else
3174 data = _mm256_div_ps(data, vec.data);
3175# endif
3176 return *this;
3177 }
3178
3185 void
3186 load(const float *ptr)
3187 {
3188 data = _mm256_loadu_ps(ptr);
3189 }
3190
3198 void
3199 store(float *ptr) const
3200 {
3201 _mm256_storeu_ps(ptr, data);
3202 }
3203
3209 void
3210 streaming_store(float *ptr) const
3211 {
3212 Assert(reinterpret_cast<std::size_t>(ptr) % 32 == 0,
3213 ExcMessage("Memory not aligned"));
3214 _mm256_stream_ps(ptr, data);
3215 }
3216
3221 void
3222 gather(const float *base_ptr, const unsigned int *offsets)
3223 {
3224# if defined(__AVX2__) && defined(DEAL_II_USE_VECTORIZATION_GATHER)
3225 // unfortunately, there does not appear to be a 256 bit integer load, so
3226 // do it by some reinterpret casts here. this is allowed because the Intel
3227 // API allows aliasing between different vector types.
3228 const __m256 index_val =
3229 _mm256_loadu_ps(reinterpret_cast<const float *>(offsets));
3230 const __m256i index = *reinterpret_cast<const __m256i *>(&index_val);
3231
3232 // work around a warning with gcc-12 about an uninitialized initial state
3233 // for gather by starting with a zero guess, even though all lanes will be
3234 // overwritten
3235 __m256 zero = _mm256_setzero_ps();
3236 __m256i invalid = _mm256_set1_epi32(numbers::invalid_unsigned_int);
3237 __m256i inverse_mask = _mm256_cmpeq_epi32(index, invalid);
3238 __m256i mask = _mm256_xor_si256(invalid, inverse_mask);
3239
3240 data = _mm256_mask_i32gather_ps(
3241 zero, base_ptr, index, *reinterpret_cast<__m256 *>(&mask), 4);
3242# else
3243 this->operator=(0.f);
3244 for (unsigned int i = 0; i < 8; ++i)
3245 if (offsets[i] != numbers::invalid_unsigned_int)
3246 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
3247# endif
3248 }
3249
3254 void
3255 scatter(const unsigned int *offsets, float *base_ptr) const
3256 {
3257 // no scatter operation in AVX/AVX2
3258 for (unsigned int i = 0; i < 8; ++i)
3259 if (offsets[i] != numbers::invalid_unsigned_int)
3260 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
3261 }
3262
3267 float
3268 sum() const
3269 {
3271 t1.data = _mm_add_ps(this->get_lower(), this->get_upper());
3272 return t1.sum();
3273 }
3274
3279 get_floor() const
3280 {
3281 VectorizedArray res;
3282 res.data = _mm256_floor_ps(data);
3283 return res;
3284 }
3285
3291 __m256 data;
3292
3293private:
3298 __m128
3299 get_lower() const
3300 {
3301 return _mm256_castps256_ps128(data);
3302 }
3303
3308 __m128
3309 get_upper() const
3310 {
3311 return _mm256_extractf128_ps(data, 1);
3312 }
3313
3320 get_sqrt() const
3321 {
3322 VectorizedArray res;
3323 res.data = _mm256_sqrt_ps(data);
3324 return res;
3325 }
3326
3333 get_abs() const
3334 {
3335 // to compute the absolute value, perform bitwise andnot with -0. This
3336 // will leave all value and exponent bits unchanged but force the sign
3337 // value to +.
3338 __m256 mask = _mm256_set1_ps(-0.f);
3339 VectorizedArray res;
3340 res.data = _mm256_andnot_ps(mask, data);
3341 return res;
3342 }
3343
3350 get_max(const VectorizedArray &other) const
3351 {
3352 VectorizedArray res;
3353 res.data = _mm256_max_ps(data, other.data);
3354 return res;
3355 }
3356
3363 get_min(const VectorizedArray &other) const
3364 {
3365 VectorizedArray res;
3366 res.data = _mm256_min_ps(data, other.data);
3367 return res;
3368 }
3369
3370 // Make a few functions friends.
3371 template <typename Number2, std::size_t width2>
3374 template <typename Number2, std::size_t width2>
3377 template <typename Number2, std::size_t width2>
3381 template <typename Number2, std::size_t width2>
3385};
3386
3387
3388
3392template <>
3393inline DEAL_II_ALWAYS_INLINE void
3394vectorized_load_and_transpose(const unsigned int n_entries,
3395 const std::array<const float *, 8> &in,
3397{
3398 const unsigned int n_chunks = n_entries / 4;
3399 for (unsigned int i = 0; i < n_chunks; ++i)
3400 {
3401 // To avoid warnings about uninitialized variables, need to initialize
3402 // one variable with zero before using it.
3403 __m256 t0, t1, t2, t3 = {};
3404 t0 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[0] + 4 * i), 0);
3405 t0 = _mm256_insertf128_ps(t0, _mm_loadu_ps(in[4] + 4 * i), 1);
3406 t1 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[1] + 4 * i), 0);
3407 t1 = _mm256_insertf128_ps(t1, _mm_loadu_ps(in[5] + 4 * i), 1);
3408 t2 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[2] + 4 * i), 0);
3409 t2 = _mm256_insertf128_ps(t2, _mm_loadu_ps(in[6] + 4 * i), 1);
3410 t3 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[3] + 4 * i), 0);
3411 t3 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[7] + 4 * i), 1);
3412
3413 __m256 v0 = _mm256_shuffle_ps(t0, t1, 0x44);
3414 __m256 v1 = _mm256_shuffle_ps(t0, t1, 0xee);
3415 __m256 v2 = _mm256_shuffle_ps(t2, t3, 0x44);
3416 __m256 v3 = _mm256_shuffle_ps(t2, t3, 0xee);
3417 out[4 * i + 0].data = _mm256_shuffle_ps(v0, v2, 0x88);
3418 out[4 * i + 1].data = _mm256_shuffle_ps(v0, v2, 0xdd);
3419 out[4 * i + 2].data = _mm256_shuffle_ps(v1, v3, 0x88);
3420 out[4 * i + 3].data = _mm256_shuffle_ps(v1, v3, 0xdd);
3421 }
3422
3423 // Specialized remainder code
3424 const unsigned int i = 4 * n_chunks;
3425 const unsigned int remainder = n_entries - i;
3426 if (remainder == 3)
3427 {
3428 // Use masked load of 3 floats (this avoids accessing in[x][3], which
3429 // might be an invalid address)
3430 __m128i mask = _mm_setr_epi32(-1, -1, -1, 0);
3431 __m256 t0, t1, t2, t3 = {};
3432 t0 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[0] + i, mask), 0);
3433 t0 = _mm256_insertf128_ps(t0, _mm_maskload_ps(in[4] + i, mask), 1);
3434 t1 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[1] + i, mask), 0);
3435 t1 = _mm256_insertf128_ps(t1, _mm_maskload_ps(in[5] + i, mask), 1);
3436 t2 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[2] + i, mask), 0);
3437 t2 = _mm256_insertf128_ps(t2, _mm_maskload_ps(in[6] + i, mask), 1);
3438 t3 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[3] + i, mask), 0);
3439 t3 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[7] + i, mask), 1);
3440 __m256 v0 = _mm256_shuffle_ps(t0, t1, 0x44);
3441 __m256 v1 = _mm256_shuffle_ps(t0, t1, 0xee);
3442 __m256 v2 = _mm256_shuffle_ps(t2, t3, 0x44);
3443 __m256 v3 = _mm256_shuffle_ps(t2, t3, 0xee);
3444 out[i + 0].data = _mm256_shuffle_ps(v0, v2, 0x88);
3445 out[i + 1].data = _mm256_shuffle_ps(v0, v2, 0xdd);
3446 out[i + 2].data = _mm256_shuffle_ps(v1, v3, 0x88);
3447 }
3448 else if (remainder == 2)
3449 {
3450 // Use 64-bit integer loads
3451 __m128i t0, t1;
3452 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[0] + i));
3453 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[1] + i));
3454 __m256i v0 = _mm256_castsi128_si256(_mm_unpacklo_epi64(t0, t1));
3455 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[4] + i));
3456 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[5] + i));
3457 v0 = _mm256_inserti128_si256(v0, _mm_unpacklo_epi64(t0, t1), 1);
3458 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[2] + i));
3459 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[3] + i));
3460 __m256i v1 = _mm256_castsi128_si256(_mm_unpacklo_epi64(t0, t1));
3461 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[6] + i));
3462 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[7] + i));
3463 v1 = _mm256_inserti128_si256(v1, _mm_unpacklo_epi64(t0, t1), 1);
3464 out[i + 0].data = _mm256_shuffle_ps(_mm256_castsi256_ps(v0),
3465 _mm256_castsi256_ps(v1),
3466 0x88);
3467 out[i + 1].data = _mm256_shuffle_ps(_mm256_castsi256_ps(v0),
3468 _mm256_castsi256_ps(v1),
3469 0xdd);
3470 }
3471 else if (remainder == 1)
3472 for (unsigned int v = 0; v < 8; ++v)
3473 out[i][v] = in[v][i];
3474}
3475
3476
3477
3481template <>
3482inline DEAL_II_ALWAYS_INLINE void
3483vectorized_load_and_transpose(const unsigned int n_entries,
3484 const float *in,
3485 const unsigned int *offsets,
3487{
3489 {{in + offsets[0],
3490 in + offsets[1],
3491 in + offsets[2],
3492 in + offsets[3],
3493 in + offsets[4],
3494 in + offsets[5],
3495 in + offsets[6],
3496 in + offsets[7]}},
3497 out);
3498}
3499
3500
3501
3505template <>
3506inline DEAL_II_ALWAYS_INLINE void
3507vectorized_transpose_and_store(const bool add_into,
3508 const unsigned int n_entries,
3509 const VectorizedArray<float, 8> *in,
3510 const std::array<float *, 8> &out)
3511{
3512 const unsigned int n_chunks = n_entries / 4;
3513 for (unsigned int i = 0; i < n_chunks; ++i)
3514 {
3515 __m256 u0 = in[4 * i + 0].data;
3516 __m256 u1 = in[4 * i + 1].data;
3517 __m256 u2 = in[4 * i + 2].data;
3518 __m256 u3 = in[4 * i + 3].data;
3519 __m256 t0 = _mm256_shuffle_ps(u0, u1, 0x44);
3520 __m256 t1 = _mm256_shuffle_ps(u0, u1, 0xee);
3521 __m256 t2 = _mm256_shuffle_ps(u2, u3, 0x44);
3522 __m256 t3 = _mm256_shuffle_ps(u2, u3, 0xee);
3523 u0 = _mm256_shuffle_ps(t0, t2, 0x88);
3524 u1 = _mm256_shuffle_ps(t0, t2, 0xdd);
3525 u2 = _mm256_shuffle_ps(t1, t3, 0x88);
3526 u3 = _mm256_shuffle_ps(t1, t3, 0xdd);
3527 __m128 res0 = _mm256_extractf128_ps(u0, 0);
3528 __m128 res4 = _mm256_extractf128_ps(u0, 1);
3529 __m128 res1 = _mm256_extractf128_ps(u1, 0);
3530 __m128 res5 = _mm256_extractf128_ps(u1, 1);
3531 __m128 res2 = _mm256_extractf128_ps(u2, 0);
3532 __m128 res6 = _mm256_extractf128_ps(u2, 1);
3533 __m128 res3 = _mm256_extractf128_ps(u3, 0);
3534 __m128 res7 = _mm256_extractf128_ps(u3, 1);
3535
3536 // Cannot use the same store instructions in both paths of the 'if'
3537 // because the compiler cannot know that there is no aliasing between
3538 // pointers
3539 if (add_into)
3540 {
3541 res0 = _mm_add_ps(_mm_loadu_ps(out[0] + 4 * i), res0);
3542 _mm_storeu_ps(out[0] + 4 * i, res0);
3543 res1 = _mm_add_ps(_mm_loadu_ps(out[1] + 4 * i), res1);
3544 _mm_storeu_ps(out[1] + 4 * i, res1);
3545 res2 = _mm_add_ps(_mm_loadu_ps(out[2] + 4 * i), res2);
3546 _mm_storeu_ps(out[2] + 4 * i, res2);
3547 res3 = _mm_add_ps(_mm_loadu_ps(out[3] + 4 * i), res3);
3548 _mm_storeu_ps(out[3] + 4 * i, res3);
3549 res4 = _mm_add_ps(_mm_loadu_ps(out[4] + 4 * i), res4);
3550 _mm_storeu_ps(out[4] + 4 * i, res4);
3551 res5 = _mm_add_ps(_mm_loadu_ps(out[5] + 4 * i), res5);
3552 _mm_storeu_ps(out[5] + 4 * i, res5);
3553 res6 = _mm_add_ps(_mm_loadu_ps(out[6] + 4 * i), res6);
3554 _mm_storeu_ps(out[6] + 4 * i, res6);
3555 res7 = _mm_add_ps(_mm_loadu_ps(out[7] + 4 * i), res7);
3556 _mm_storeu_ps(out[7] + 4 * i, res7);
3557 }
3558 else
3559 {
3560 _mm_storeu_ps(out[0] + 4 * i, res0);
3561 _mm_storeu_ps(out[1] + 4 * i, res1);
3562 _mm_storeu_ps(out[2] + 4 * i, res2);
3563 _mm_storeu_ps(out[3] + 4 * i, res3);
3564 _mm_storeu_ps(out[4] + 4 * i, res4);
3565 _mm_storeu_ps(out[5] + 4 * i, res5);
3566 _mm_storeu_ps(out[6] + 4 * i, res6);
3567 _mm_storeu_ps(out[7] + 4 * i, res7);
3568 }
3569 }
3570
3571 if (add_into)
3572 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
3573 for (unsigned int v = 0; v < 8; ++v)
3574 out[v][i] += in[i][v];
3575 else
3576 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
3577 for (unsigned int v = 0; v < 8; ++v)
3578 out[v][i] = in[i][v];
3579}
3580
3581
3582
3586template <>
3587inline DEAL_II_ALWAYS_INLINE void
3588vectorized_transpose_and_store(const bool add_into,
3589 const unsigned int n_entries,
3590 const VectorizedArray<float, 8> *in,
3591 const unsigned int *offsets,
3592 float *out)
3593{
3595 n_entries,
3596 in,
3597 {{out + offsets[0],
3598 out + offsets[1],
3599 out + offsets[2],
3600 out + offsets[3],
3601 out + offsets[4],
3602 out + offsets[5],
3603 out + offsets[6],
3604 out + offsets[7]}});
3605}
3606
3607# endif
3608
3609// for safety, also check that __AVX512F__ is defined in case the user manually
3610// set some conflicting compile flags which prevent compilation
3611
3612# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 512 && defined(__AVX512F__)
3613
3617template <>
3618class VectorizedArray<double, 8>
3619 : public VectorizedArrayBase<VectorizedArray<double, 8>, 8>
3620{
3621public:
3625 using value_type = double;
3626
3631 static constexpr bool is_implemented = true;
3632
3637 VectorizedArray() = default;
3638
3642 VectorizedArray(const double scalar)
3643 {
3644 this->operator=(scalar);
3645 }
3646
3650 template <typename U>
3651 VectorizedArray(const std::initializer_list<U> &list)
3652 : VectorizedArrayBase<VectorizedArray<double, 8>, 8>(list)
3653 {}
3654
3660 operator=(const double x) &
3661 {
3662 data = _mm512_set1_pd(x);
3663 return *this;
3664 }
3665
3666
3673 operator=(const double scalar) && = delete;
3674
3679 double &
3680 operator[](const unsigned int comp)
3681 {
3682 AssertIndexRange(comp, 8);
3683 return *(reinterpret_cast<double *>(&data) + comp);
3684 }
3685
3690 const double &
3691 operator[](const unsigned int comp) const
3692 {
3693 AssertIndexRange(comp, 8);
3694 return *(reinterpret_cast<const double *>(&data) + comp);
3695 }
3696
3702 operator+=(const VectorizedArray &vec)
3703 {
3704 // if the compiler supports vector arithmetic, we can simply use +=
3705 // operator on the given data type. this allows the compiler to combine
3706 // additions with multiplication (fused multiply-add) if those
3707 // instructions are available. Otherwise, we need to use the built-in
3708 // intrinsic command for __m512d
3709# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3710 data += vec.data;
3711# else
3712 data = _mm512_add_pd(data, vec.data);
3713# endif
3714 return *this;
3715 }
3716
3722 operator-=(const VectorizedArray &vec)
3723 {
3724# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3725 data -= vec.data;
3726# else
3727 data = _mm512_sub_pd(data, vec.data);
3728# endif
3729 return *this;
3730 }
3736 operator*=(const VectorizedArray &vec)
3737 {
3738# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3739 data *= vec.data;
3740# else
3741 data = _mm512_mul_pd(data, vec.data);
3742# endif
3743 return *this;
3744 }
3745
3751 operator/=(const VectorizedArray &vec)
3752 {
3753# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3754 data /= vec.data;
3755# else
3756 data = _mm512_div_pd(data, vec.data);
3757# endif
3758 return *this;
3759 }
3760
3767 void
3768 load(const double *ptr)
3769 {
3770 data = _mm512_loadu_pd(ptr);
3771 }
3772
3774 void
3775 load(const float *ptr)
3776 {
3777 data = _mm512_cvtps_pd(_mm256_loadu_ps(ptr));
3778 }
3779
3787 void
3788 store(double *ptr) const
3789 {
3790 _mm512_storeu_pd(ptr, data);
3791 }
3792
3794 void
3795 store(float *ptr) const
3796 {
3797 _mm256_storeu_ps(ptr, _mm512_cvtpd_ps(data));
3798 }
3799
3805 void
3806 streaming_store(double *ptr) const
3807 {
3808 Assert(reinterpret_cast<std::size_t>(ptr) % 64 == 0,
3809 ExcMessage("Memory not aligned"));
3810 _mm512_stream_pd(ptr, data);
3811 }
3812
3817 void
3818 gather(const double *base_ptr, const unsigned int *offsets)
3819 {
3820# ifdef DEAL_II_USE_VECTORIZATION_GATHER
3821 // unfortunately, there does not appear to be a 256 bit integer load, so
3822 // do it by some reinterpret casts here. this is allowed because the Intel
3823 // API allows aliasing between different vector types.
3824 const __m256 index_val =
3825 _mm256_loadu_ps(reinterpret_cast<const float *>(offsets));
3826 const __m256i index = *reinterpret_cast<const __m256i *>(&index_val);
3827
3828 // work around a warning with gcc-12 about an uninitialized initial state
3829 // for gather by starting with a zero guess, even though all lanes will be
3830 // overwritten
3831 __m512d zero = {};
3832 const __m256i invalid = _mm256_set1_epi32(numbers::invalid_unsigned_int);
3833 const __m256i equal = _mm256_cmpeq_epi32(invalid, index);
3834 __mmask8 mask =
3835 static_cast<__mmask8>(~(_mm256_movemask_ps(_mm256_castsi256_ps(equal))));
3836
3837 data = _mm512_mask_i32gather_pd(zero, mask, index, base_ptr, 8);
3838# else
3839 this->operator=(0.);
3840 for (unsigned int i = 0; i < 8; ++i)
3841 if (offsets[i] != numbers::invalid_unsigned_int)
3842 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
3843# endif
3844 }
3845
3850 void
3851 scatter(const unsigned int *offsets, double *base_ptr) const
3852 {
3853# ifdef DEAL_II_USE_VECTORIZATION_GATHER
3854 for (unsigned int i = 0; i < 8; ++i)
3855 for (unsigned int j = i + 1; j < 8; ++j)
3856 Assert(offsets[i] == numbers::invalid_unsigned_int ||
3857 offsets[i] != offsets[j],
3858 ExcMessage("Result of scatter undefined if two offset elements"
3859 " point to the same position"));
3860
3861 // unfortunately, there does not appear to be a 256 bit integer load, so
3862 // do it by some reinterpret casts here. this is allowed because the Intel
3863 // API allows aliasing between different vector types.
3864 const __m256 index_val =
3865 _mm256_loadu_ps(reinterpret_cast<const float *>(offsets));
3866 const __m256i index = *reinterpret_cast<const __m256i *>(&index_val);
3867 const __m256i invalid = _mm256_set1_epi32(numbers::invalid_unsigned_int);
3868 const __m256i equal = _mm256_cmpeq_epi32(invalid, index);
3869 __mmask8 mask =
3870 static_cast<__mmask8>(~(_mm256_movemask_ps(_mm256_castsi256_ps(equal))));
3871 _mm512_mask_i32scatter_pd(base_ptr, mask, index, data, 8);
3872
3873# else
3874 for (unsigned int i = 0; i < 8; ++i)
3875 if (offsets[i] != numbers::invalid_unsigned_int)
3876 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
3877# endif
3878 }
3879
3884 double
3885 sum() const
3886 {
3888 t1.data = _mm256_add_pd(this->get_lower(), this->get_upper());
3889 return t1.sum();
3890 }
3891
3896 get_floor() const
3897 {
3898 VectorizedArray res;
3899 res.data = _mm512_roundscale_pd(data, _MM_FROUND_TO_NEG_INF);
3900 return res;
3901 }
3902
3908 __m512d data;
3909
3910private:
3915 __m256d
3916 get_lower() const
3917 {
3918 return _mm512_castpd512_pd256(data);
3919 }
3920
3925 __m256d
3926 get_upper() const
3927 {
3928 return _mm512_extractf64x4_pd(data, 1);
3929 }
3930
3937 get_sqrt() const
3938 {
3939 VectorizedArray res;
3940 res.data = _mm512_sqrt_pd(data);
3941 return res;
3942 }
3943
3950 get_abs() const
3951 {
3952 // to compute the absolute value, perform bitwise andnot with -0. This
3953 // will leave all value and exponent bits unchanged but force the sign
3954 // value to +. Since there is no andnot for AVX512, we interpret the data
3955 // as 64 bit integers and do the andnot on those types (note that andnot
3956 // is a bitwise operation so the data type does not matter)
3957 __m512d mask = _mm512_set1_pd(-0.);
3958 VectorizedArray res;
3959 res.data = reinterpret_cast<__m512d>(
3960 _mm512_andnot_epi64(reinterpret_cast<__m512i>(mask),
3961 reinterpret_cast<__m512i>(data)));
3962 return res;
3963 }
3964
3971 get_max(const VectorizedArray &other) const
3972 {
3973 VectorizedArray res;
3974 res.data = _mm512_max_pd(data, other.data);
3975 return res;
3976 }
3977
3984 get_min(const VectorizedArray &other) const
3985 {
3986 VectorizedArray res;
3987 res.data = _mm512_min_pd(data, other.data);
3988 return res;
3989 }
3990
3991 // Make a few functions friends.
3992 template <typename Number2, std::size_t width2>
3995 template <typename Number2, std::size_t width2>
3998 template <typename Number2, std::size_t width2>
4002 template <typename Number2, std::size_t width2>
4006};
4007
4008
4009
4013template <>
4014inline DEAL_II_ALWAYS_INLINE void
4015vectorized_load_and_transpose(const unsigned int n_entries,
4016 const std::array<const double *, 8> &in,
4018{
4019 // do not do full transpose because the code is long and will most
4020 // likely not pay off because many processors have two load units
4021 // (for the top 8 instructions) but only 1 permute unit (for the 8
4022 // shuffle/unpack instructions). rather start the transposition on the
4023 // vectorized array of half the size with 256 bits
4024 const unsigned int n_chunks = n_entries / 4;
4025 for (unsigned int i = 0; i < n_chunks; ++i)
4026 {
4027 __m512d t0, t1, t2, t3 = {};
4028
4029 t0 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[0] + 4 * i), 0);
4030 t0 = _mm512_insertf64x4(t0, _mm256_loadu_pd(in[2] + 4 * i), 1);
4031 t1 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[1] + 4 * i), 0);
4032 t1 = _mm512_insertf64x4(t1, _mm256_loadu_pd(in[3] + 4 * i), 1);
4033 t2 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[4] + 4 * i), 0);
4034 t2 = _mm512_insertf64x4(t2, _mm256_loadu_pd(in[6] + 4 * i), 1);
4035 t3 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[5] + 4 * i), 0);
4036 t3 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[7] + 4 * i), 1);
4037
4038 __m512d v0 = _mm512_shuffle_f64x2(t0, t2, 0x88);
4039 __m512d v1 = _mm512_shuffle_f64x2(t0, t2, 0xdd);
4040 __m512d v2 = _mm512_shuffle_f64x2(t1, t3, 0x88);
4041 __m512d v3 = _mm512_shuffle_f64x2(t1, t3, 0xdd);
4042 out[4 * i + 0].data = _mm512_unpacklo_pd(v0, v2);
4043 out[4 * i + 1].data = _mm512_unpackhi_pd(v0, v2);
4044 out[4 * i + 2].data = _mm512_unpacklo_pd(v1, v3);
4045 out[4 * i + 3].data = _mm512_unpackhi_pd(v1, v3);
4046 }
4047
4048 // Specialized remainder code
4049 const unsigned int i = 4 * n_chunks;
4050 const unsigned int remainder = n_entries - i;
4051 if (remainder == 3)
4052 {
4053 // Use masked loads to access the first 3 doubles along each pointer
4054 // without touching the possibly invalid addressed at in[x][3].
4055 __m256i mask{-1, -1, -1, 0};
4056 __m512d t0, t1, t2, t3 = {};
4057
4058 t0 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[0] + i, mask), 0);
4059 t0 = _mm512_insertf64x4(t0, _mm256_maskload_pd(in[2] + i, mask), 1);
4060 t1 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[1] + i, mask), 0);
4061 t1 = _mm512_insertf64x4(t1, _mm256_maskload_pd(in[3] + i, mask), 1);
4062 t2 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[4] + i, mask), 0);
4063 t2 = _mm512_insertf64x4(t2, _mm256_maskload_pd(in[6] + i, mask), 1);
4064 t3 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[5] + i, mask), 0);
4065 t3 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[7] + i, mask), 1);
4066
4067 __m512d v0 = _mm512_shuffle_f64x2(t0, t2, 0x88);
4068 __m512d v1 = _mm512_shuffle_f64x2(t0, t2, 0xdd);
4069 __m512d v2 = _mm512_shuffle_f64x2(t1, t3, 0x88);
4070 __m512d v3 = _mm512_shuffle_f64x2(t1, t3, 0xdd);
4071 out[i + 0].data = _mm512_unpacklo_pd(v0, v2);
4072 out[i + 1].data = _mm512_unpackhi_pd(v0, v2);
4073 out[i + 2].data = _mm512_unpacklo_pd(v1, v3);
4074 }
4075 else if (remainder == 2)
4076 {
4077 // Use 128-bit loads of 2 doubles
4078 __m256d t0, t1, t2, t3 = {};
4079 t0 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[0] + i), 0);
4080 t0 = _mm256_insertf128_pd(t0, _mm_loadu_pd(in[2] + i), 1);
4081 t1 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[1] + i), 0);
4082 t1 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[3] + i), 1);
4083 t2 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[4] + i), 0);
4084 t2 = _mm256_insertf128_pd(t2, _mm_loadu_pd(in[6] + i), 1);
4085 t3 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[5] + i), 0);
4086 t3 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[7] + i), 1);
4087
4088 __m512d v0, v1 = {};
4089 v0 = _mm512_insertf64x4(v1, t0, 0);
4090 v0 = _mm512_insertf64x4(v0, t2, 1);
4091 v1 = _mm512_insertf64x4(v1, t1, 0);
4092 v1 = _mm512_insertf64x4(v1, t3, 1);
4093 out[i + 0].data = _mm512_unpacklo_pd(v0, v1);
4094 out[i + 1].data = _mm512_unpackhi_pd(v0, v1);
4095 }
4096 else if (remainder == 1)
4097 for (unsigned int v = 0; v < 8; ++v)
4098 out[i][v] = in[v][i];
4099}
4100
4101
4102
4106template <>
4107inline DEAL_II_ALWAYS_INLINE void
4108vectorized_load_and_transpose(const unsigned int n_entries,
4109 const double *in,
4110 const unsigned int *offsets,
4112{
4114 {{in + offsets[0],
4115 in + offsets[1],
4116 in + offsets[2],
4117 in + offsets[3],
4118 in + offsets[4],
4119 in + offsets[5],
4120 in + offsets[6],
4121 in + offsets[7]}},
4122 out);
4123}
4124
4125
4126
4130template <>
4131inline DEAL_II_ALWAYS_INLINE void
4132vectorized_transpose_and_store(const bool add_into,
4133 const unsigned int n_entries,
4135 const std::array<double *, 8> &out)
4136{
4137 // as for the load, we split the store operations into 256 bit units to
4138 // better balance between code size, shuffle instructions, and stores
4139 const unsigned int n_chunks = n_entries / 4;
4140 __m512i mask1 = _mm512_set_epi64(0xd, 0xc, 0x5, 0x4, 0x9, 0x8, 0x1, 0x0);
4141 __m512i mask2 = _mm512_set_epi64(0xf, 0xe, 0x7, 0x6, 0xb, 0xa, 0x3, 0x2);
4142 for (unsigned int i = 0; i < n_chunks; ++i)
4143 {
4144 __m512d t0 = _mm512_unpacklo_pd(in[i * 4].data, in[i * 4 + 1].data);
4145 __m512d t1 = _mm512_unpackhi_pd(in[i * 4].data, in[i * 4 + 1].data);
4146 __m512d t2 = _mm512_unpacklo_pd(in[i * 4 + 2].data, in[i * 4 + 3].data);
4147 __m512d t3 = _mm512_unpackhi_pd(in[i * 4 + 2].data, in[i * 4 + 3].data);
4148 __m512d v0 = _mm512_permutex2var_pd(t0, mask1, t2);
4149 __m512d v1 = _mm512_permutex2var_pd(t0, mask2, t2);
4150 __m512d v2 = _mm512_permutex2var_pd(t1, mask1, t3);
4151 __m512d v3 = _mm512_permutex2var_pd(t1, mask2, t3);
4152 __m256d res0 = _mm512_extractf64x4_pd(v0, 0);
4153 __m256d res4 = _mm512_extractf64x4_pd(v0, 1);
4154 __m256d res1 = _mm512_extractf64x4_pd(v2, 0);
4155 __m256d res5 = _mm512_extractf64x4_pd(v2, 1);
4156 __m256d res2 = _mm512_extractf64x4_pd(v1, 0);
4157 __m256d res6 = _mm512_extractf64x4_pd(v1, 1);
4158 __m256d res3 = _mm512_extractf64x4_pd(v3, 0);
4159 __m256d res7 = _mm512_extractf64x4_pd(v3, 1);
4160
4161 // Cannot use the same store instructions in both paths of the 'if'
4162 // because the compiler cannot know that there is no aliasing
4163 // between pointers
4164 if (add_into)
4165 {
4166 res0 = _mm256_add_pd(_mm256_loadu_pd(out[0] + 4 * i), res0);
4167 _mm256_storeu_pd(out[0] + 4 * i, res0);
4168 res1 = _mm256_add_pd(_mm256_loadu_pd(out[1] + 4 * i), res1);
4169 _mm256_storeu_pd(out[1] + 4 * i, res1);
4170 res2 = _mm256_add_pd(_mm256_loadu_pd(out[2] + 4 * i), res2);
4171 _mm256_storeu_pd(out[2] + 4 * i, res2);
4172 res3 = _mm256_add_pd(_mm256_loadu_pd(out[3] + 4 * i), res3);
4173 _mm256_storeu_pd(out[3] + 4 * i, res3);
4174 res4 = _mm256_add_pd(_mm256_loadu_pd(out[4] + 4 * i), res4);
4175 _mm256_storeu_pd(out[4] + 4 * i, res4);
4176 res5 = _mm256_add_pd(_mm256_loadu_pd(out[5] + 4 * i), res5);
4177 _mm256_storeu_pd(out[5] + 4 * i, res5);
4178 res6 = _mm256_add_pd(_mm256_loadu_pd(out[6] + 4 * i), res6);
4179 _mm256_storeu_pd(out[6] + 4 * i, res6);
4180 res7 = _mm256_add_pd(_mm256_loadu_pd(out[7] + 4 * i), res7);
4181 _mm256_storeu_pd(out[7] + 4 * i, res7);
4182 }
4183 else
4184 {
4185 _mm256_storeu_pd(out[0] + 4 * i, res0);
4186 _mm256_storeu_pd(out[1] + 4 * i, res1);
4187 _mm256_storeu_pd(out[2] + 4 * i, res2);
4188 _mm256_storeu_pd(out[3] + 4 * i, res3);
4189 _mm256_storeu_pd(out[4] + 4 * i, res4);
4190 _mm256_storeu_pd(out[5] + 4 * i, res5);
4191 _mm256_storeu_pd(out[6] + 4 * i, res6);
4192 _mm256_storeu_pd(out[7] + 4 * i, res7);
4193 }
4194 }
4195
4196 if (add_into)
4197 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4198 for (unsigned int v = 0; v < 8; ++v)
4199 out[v][i] += in[i][v];
4200 else
4201 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4202 for (unsigned int v = 0; v < 8; ++v)
4203 out[v][i] = in[i][v];
4204}
4205
4206
4207
4211template <>
4212inline DEAL_II_ALWAYS_INLINE void
4213vectorized_transpose_and_store(const bool add_into,
4214 const unsigned int n_entries,
4216 const unsigned int *offsets,
4217 double *out)
4218{
4220 n_entries,
4221 in,
4222 {{out + offsets[0],
4223 out + offsets[1],
4224 out + offsets[2],
4225 out + offsets[3],
4226 out + offsets[4],
4227 out + offsets[5],
4228 out + offsets[6],
4229 out + offsets[7]}});
4230}
4231
4232
4233
4237template <>
4238class VectorizedArray<float, 16>
4239 : public VectorizedArrayBase<VectorizedArray<float, 16>, 16>
4240{
4241public:
4245 using value_type = float;
4246
4251 static constexpr bool is_implemented = true;
4252
4257 VectorizedArray() = default;
4258
4262 VectorizedArray(const float scalar)
4263 {
4264 this->operator=(scalar);
4265 }
4266
4270 template <typename U>
4271 VectorizedArray(const std::initializer_list<U> &list)
4272 : VectorizedArrayBase<VectorizedArray<float, 16>, 16>(list)
4273 {}
4274
4280 operator=(const float x) &
4281 {
4282 data = _mm512_set1_ps(x);
4283 return *this;
4284 }
4285
4292 operator=(const float scalar) && = delete;
4293
4298 float &
4299 operator[](const unsigned int comp)
4300 {
4301 AssertIndexRange(comp, 16);
4302 return *(reinterpret_cast<float *>(&data) + comp);
4303 }
4304
4309 const float &
4310 operator[](const unsigned int comp) const
4311 {
4312 AssertIndexRange(comp, 16);
4313 return *(reinterpret_cast<const float *>(&data) + comp);
4314 }
4315
4321 operator+=(const VectorizedArray &vec)
4322 {
4323 // if the compiler supports vector arithmetic, we can simply use +=
4324 // operator on the given data type. this allows the compiler to combine
4325 // additions with multiplication (fused multiply-add) if those
4326 // instructions are available. Otherwise, we need to use the built-in
4327 // intrinsic command for __m512d
4328# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4329 data += vec.data;
4330# else
4331 data = _mm512_add_ps(data, vec.data);
4332# endif
4333 return *this;
4334 }
4335
4341 operator-=(const VectorizedArray &vec)
4342 {
4343# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4344 data -= vec.data;
4345# else
4346 data = _mm512_sub_ps(data, vec.data);
4347# endif
4348 return *this;
4349 }
4355 operator*=(const VectorizedArray &vec)
4356 {
4357# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4358 data *= vec.data;
4359# else
4360 data = _mm512_mul_ps(data, vec.data);
4361# endif
4362 return *this;
4363 }
4364
4370 operator/=(const VectorizedArray &vec)
4371 {
4372# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4373 data /= vec.data;
4374# else
4375 data = _mm512_div_ps(data, vec.data);
4376# endif
4377 return *this;
4378 }
4379
4386 void
4387 load(const float *ptr)
4388 {
4389 data = _mm512_loadu_ps(ptr);
4390 }
4391
4399 void
4400 store(float *ptr) const
4401 {
4402 _mm512_storeu_ps(ptr, data);
4403 }
4404
4410 void
4411 streaming_store(float *ptr) const
4412 {
4413 Assert(reinterpret_cast<std::size_t>(ptr) % 64 == 0,
4414 ExcMessage("Memory not aligned"));
4415 _mm512_stream_ps(ptr, data);
4416 }
4417
4422 void
4423 gather(const float *base_ptr, const unsigned int *offsets)
4424 {
4425# ifdef DEAL_II_USE_VECTORIZATION_GATHER
4426 // unfortunately, there does not appear to be a 512 bit integer load, so
4427 // do it by some reinterpret casts here. this is allowed because the Intel
4428 // API allows aliasing between different vector types.
4429 const __m512 index_val =
4430 _mm512_loadu_ps(reinterpret_cast<const float *>(offsets));
4431 const __m512i index = *reinterpret_cast<const __m512i *>(&index_val);
4432
4433 // work around a warning with gcc-12 about an uninitialized initial state
4434 // for gather by starting with a zero guess, even though all lanes will be
4435 // overwritten
4436 __m512 zero = {};
4437 const __m512i invalid = _mm512_set1_epi32(numbers::invalid_unsigned_int);
4438 __mmask16 mask = _mm512_cmpneq_epu32_mask(invalid, index);
4439
4440 data = _mm512_mask_i32gather_ps(zero, mask, index, base_ptr, 4);
4441# else
4442 this->operator=(0.f);
4443 for (unsigned int i = 0; i < 16; ++i)
4444 if (offsets[i] != numbers::invalid_unsigned_int)
4445 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
4446# endif
4447 }
4448
4453 void
4454 scatter(const unsigned int *offsets, float *base_ptr) const
4455 {
4456# ifdef DEAL_II_USE_VECTORIZATION_GATHER
4457 for (unsigned int i = 0; i < 16; ++i)
4458 for (unsigned int j = i + 1; j < 16; ++j)
4459 Assert(offsets[i] == numbers::invalid_unsigned_int ||
4460 offsets[i] != offsets[j],
4461 ExcMessage("Result of scatter undefined if two offset elements"
4462 " point to the same position"));
4463
4464 // unfortunately, there does not appear to be a 512 bit integer load, so
4465 // do it by some reinterpret casts here. this is allowed because the Intel
4466 // API allows aliasing between different vector types.
4467 const __m512 index_val =
4468 _mm512_loadu_ps(reinterpret_cast<const float *>(offsets));
4469 const __m512i index = *reinterpret_cast<const __m512i *>(&index_val);
4470 const __m512i invalid = _mm512_set1_epi32(numbers::invalid_unsigned_int);
4471 __mmask16 mask = _mm512_cmpneq_epu32_mask(invalid, index);
4472 _mm512_mask_i32scatter_ps(base_ptr, mask, index, data, 4);
4473# else
4474 for (unsigned int i = 0; i < 16; ++i)
4475 if (offsets[i] != numbers::invalid_unsigned_int)
4476 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
4477# endif
4478 }
4479
4484 float
4485 sum() const
4486 {
4488 t1.data = _mm256_add_ps(this->get_lower(), this->get_upper());
4489 return t1.sum();
4490 }
4491
4497 get_floor() const
4498 {
4499 VectorizedArray res;
4500 res.data = _mm512_roundscale_ps(data, _MM_FROUND_TO_NEG_INF);
4501 return res;
4502 }
4503
4509 __m512 data;
4510
4511private:
4516 __m256
4517 get_lower() const
4518 {
4519 return _mm512_castps512_ps256(data);
4520 }
4521
4526 __m256
4527 get_upper() const
4528 {
4529 return _mm256_castpd_ps(_mm512_extractf64x4_pd(_mm512_castps_pd(data), 1));
4530 }
4531
4538 get_sqrt() const
4539 {
4540 VectorizedArray res;
4541 res.data = _mm512_sqrt_ps(data);
4542 return res;
4543 }
4544
4551 get_abs() const
4552 {
4553 // to compute the absolute value, perform bitwise andnot with -0. This
4554 // will leave all value and exponent bits unchanged but force the sign
4555 // value to +. Since there is no andnot for AVX512, we interpret the data
4556 // as 32 bit integers and do the andnot on those types (note that andnot
4557 // is a bitwise operation so the data type does not matter)
4558 __m512 mask = _mm512_set1_ps(-0.f);
4559 VectorizedArray res;
4560 res.data = reinterpret_cast<__m512>(
4561 _mm512_andnot_epi32(reinterpret_cast<__m512i>(mask),
4562 reinterpret_cast<__m512i>(data)));
4563 return res;
4564 }
4565
4572 get_max(const VectorizedArray &other) const
4573 {
4574 VectorizedArray res;
4575 res.data = _mm512_max_ps(data, other.data);
4576 return res;
4577 }
4578
4585 get_min(const VectorizedArray &other) const
4586 {
4587 VectorizedArray res;
4588 res.data = _mm512_min_ps(data, other.data);
4589 return res;
4590 }
4591
4592 // Make a few functions friends.
4593 template <typename Number2, std::size_t width2>
4596 template <typename Number2, std::size_t width2>
4599 template <typename Number2, std::size_t width2>
4603 template <typename Number2, std::size_t width2>
4607};
4608
4609
4610
4614template <>
4615inline DEAL_II_ALWAYS_INLINE void
4616vectorized_load_and_transpose(const unsigned int n_entries,
4617 const std::array<const float *, 16> &in,
4619{
4620 // Similar to the double case, we perform the work on smaller entities. In
4621 // this case, we start from 128 bit arrays and insert them into a full 512
4622 // bit index. This reduces the code size and register pressure because we do
4623 // shuffles on 4 numbers rather than 16.
4624 const unsigned int n_chunks = n_entries / 4;
4625
4626 for (unsigned int i = 0; i < n_chunks; ++i)
4627 {
4628 __m512 t0, t1, t2, t3 = {};
4629
4630 t0 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[0] + 4 * i), 0);
4631 t0 = _mm512_insertf32x4(t0, _mm_loadu_ps(in[4] + 4 * i), 1);
4632 t0 = _mm512_insertf32x4(t0, _mm_loadu_ps(in[8] + 4 * i), 2);
4633 t0 = _mm512_insertf32x4(t0, _mm_loadu_ps(in[12] + 4 * i), 3);
4634 t1 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[1] + 4 * i), 0);
4635 t1 = _mm512_insertf32x4(t1, _mm_loadu_ps(in[5] + 4 * i), 1);
4636 t1 = _mm512_insertf32x4(t1, _mm_loadu_ps(in[9] + 4 * i), 2);
4637 t1 = _mm512_insertf32x4(t1, _mm_loadu_ps(in[13] + 4 * i), 3);
4638 t2 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[2] + 4 * i), 0);
4639 t2 = _mm512_insertf32x4(t2, _mm_loadu_ps(in[6] + 4 * i), 1);
4640 t2 = _mm512_insertf32x4(t2, _mm_loadu_ps(in[10] + 4 * i), 2);
4641 t2 = _mm512_insertf32x4(t2, _mm_loadu_ps(in[14] + 4 * i), 3);
4642 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[3] + 4 * i), 0);
4643 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[7] + 4 * i), 1);
4644 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[11] + 4 * i), 2);
4645 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[15] + 4 * i), 3);
4646
4647 __m512 v0 = _mm512_shuffle_ps(t0, t1, 0x44);
4648 __m512 v1 = _mm512_shuffle_ps(t0, t1, 0xee);
4649 __m512 v2 = _mm512_shuffle_ps(t2, t3, 0x44);
4650 __m512 v3 = _mm512_shuffle_ps(t2, t3, 0xee);
4651
4652 out[4 * i + 0].data = _mm512_shuffle_ps(v0, v2, 0x88);
4653 out[4 * i + 1].data = _mm512_shuffle_ps(v0, v2, 0xdd);
4654 out[4 * i + 2].data = _mm512_shuffle_ps(v1, v3, 0x88);
4655 out[4 * i + 3].data = _mm512_shuffle_ps(v1, v3, 0xdd);
4656 }
4657
4658 // Specialized remainder code
4659 const unsigned int i = 4 * n_chunks;
4660 const unsigned int remainder = n_entries - i;
4661 if (remainder == 3)
4662 {
4663 __m512 t0, t1, t2, t3 = {};
4664
4665 // Use masked load of 3 floats (this avoids accessing in[x][3], which
4666 // might be an invalid address)
4667 __m128i mask = _mm_setr_epi32(-1, -1, -1, 0);
4668 t0 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[0] + i, mask), 0);
4669 t0 = _mm512_insertf32x4(t0, _mm_maskload_ps(in[4] + i, mask), 1);
4670 t0 = _mm512_insertf32x4(t0, _mm_maskload_ps(in[8] + i, mask), 2);
4671 t0 = _mm512_insertf32x4(t0, _mm_maskload_ps(in[12] + i, mask), 3);
4672 t1 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[1] + i, mask), 0);
4673 t1 = _mm512_insertf32x4(t1, _mm_maskload_ps(in[5] + i, mask), 1);
4674 t1 = _mm512_insertf32x4(t1, _mm_maskload_ps(in[9] + i, mask), 2);
4675 t1 = _mm512_insertf32x4(t1, _mm_maskload_ps(in[13] + i, mask), 3);
4676 t2 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[2] + i, mask), 0);
4677 t2 = _mm512_insertf32x4(t2, _mm_maskload_ps(in[6] + i, mask), 1);
4678 t2 = _mm512_insertf32x4(t2, _mm_maskload_ps(in[10] + i, mask), 2);
4679 t2 = _mm512_insertf32x4(t2, _mm_maskload_ps(in[14] + i, mask), 3);
4680 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[3] + i, mask), 0);
4681 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[7] + i, mask), 1);
4682 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[11] + i, mask), 2);
4683 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[15] + i, mask), 3);
4684 __m512 v0 = _mm512_shuffle_ps(t0, t1, 0x44);
4685 __m512 v1 = _mm512_shuffle_ps(t0, t1, 0xee);
4686 __m512 v2 = _mm512_shuffle_ps(t2, t3, 0x44);
4687 __m512 v3 = _mm512_shuffle_ps(t2, t3, 0xee);
4688 out[i + 0].data = _mm512_shuffle_ps(v0, v2, 0x88);
4689 out[i + 1].data = _mm512_shuffle_ps(v0, v2, 0xdd);
4690 out[i + 2].data = _mm512_shuffle_ps(v1, v3, 0x88);
4691 }
4692 else if (remainder == 2)
4693 {
4694 __m512 v0, v1 = {};
4695
4696 // Use 64-bit integer loads
4697 __m128i t0, t1;
4698 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[0] + i));
4699 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[1] + i));
4700 v0 =
4701 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 0);
4702 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[4] + i));
4703 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[5] + i));
4704 v0 =
4705 _mm512_insertf32x4(v0, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 1);
4706 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[8] + i));
4707 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[9] + i));
4708 v0 =
4709 _mm512_insertf32x4(v0, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 2);
4710 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[12] + i));
4711 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[13] + i));
4712 v0 =
4713 _mm512_insertf32x4(v0, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 3);
4714 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[2] + i));
4715 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[3] + i));
4716 v1 =
4717 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 0);
4718 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[6] + i));
4719 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[7] + i));
4720 v1 =
4721 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 1);
4722 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[10] + i));
4723 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[11] + i));
4724 v1 =
4725 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 2);
4726 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[14] + i));
4727 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[15] + i));
4728 v1 =
4729 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 3);
4730 out[i + 0].data = _mm512_shuffle_ps(v0, v1, 0x88);
4731 out[i + 1].data = _mm512_shuffle_ps(v0, v1, 0xdd);
4732 }
4733 else if (remainder == 1)
4734 for (unsigned int v = 0; v < 16; ++v)
4735 out[i][v] = in[v][i];
4736}
4737
4738
4739
4743template <>
4744inline DEAL_II_ALWAYS_INLINE void
4745vectorized_load_and_transpose(const unsigned int n_entries,
4746 const float *in,
4747 const unsigned int *offsets,
4749{
4751 {{in + offsets[0],
4752 in + offsets[1],
4753 in + offsets[2],
4754 in + offsets[3],
4755 in + offsets[4],
4756 in + offsets[5],
4757 in + offsets[6],
4758 in + offsets[7],
4759 in + offsets[8],
4760 in + offsets[9],
4761 in + offsets[10],
4762 in + offsets[11],
4763 in + offsets[12],
4764 in + offsets[13],
4765 in + offsets[14],
4766 in + offsets[15]}},
4767 out);
4768}
4769
4770
4771
4775template <>
4776inline DEAL_II_ALWAYS_INLINE void
4777vectorized_transpose_and_store(const bool add_into,
4778 const unsigned int n_entries,
4780 const std::array<float *, 16> &out)
4781{
4782 const unsigned int n_chunks = n_entries / 4;
4783 for (unsigned int i = 0; i < n_chunks; ++i)
4784 {
4785 __m512 t0 = _mm512_shuffle_ps(in[4 * i].data, in[1 + 4 * i].data, 0x44);
4786 __m512 t1 = _mm512_shuffle_ps(in[4 * i].data, in[1 + 4 * i].data, 0xee);
4787 __m512 t2 =
4788 _mm512_shuffle_ps(in[2 + 4 * i].data, in[3 + 4 * i].data, 0x44);
4789 __m512 t3 =
4790 _mm512_shuffle_ps(in[2 + 4 * i].data, in[3 + 4 * i].data, 0xee);
4791 __m512 u0 = _mm512_shuffle_ps(t0, t2, 0x88);
4792 __m512 u1 = _mm512_shuffle_ps(t0, t2, 0xdd);
4793 __m512 u2 = _mm512_shuffle_ps(t1, t3, 0x88);
4794 __m512 u3 = _mm512_shuffle_ps(t1, t3, 0xdd);
4795
4796 __m128 res0 = _mm512_extractf32x4_ps(u0, 0);
4797 __m128 res4 = _mm512_extractf32x4_ps(u0, 1);
4798 __m128 res8 = _mm512_extractf32x4_ps(u0, 2);
4799 __m128 res12 = _mm512_extractf32x4_ps(u0, 3);
4800 __m128 res1 = _mm512_extractf32x4_ps(u1, 0);
4801 __m128 res5 = _mm512_extractf32x4_ps(u1, 1);
4802 __m128 res9 = _mm512_extractf32x4_ps(u1, 2);
4803 __m128 res13 = _mm512_extractf32x4_ps(u1, 3);
4804 __m128 res2 = _mm512_extractf32x4_ps(u2, 0);
4805 __m128 res6 = _mm512_extractf32x4_ps(u2, 1);
4806 __m128 res10 = _mm512_extractf32x4_ps(u2, 2);
4807 __m128 res14 = _mm512_extractf32x4_ps(u2, 3);
4808 __m128 res3 = _mm512_extractf32x4_ps(u3, 0);
4809 __m128 res7 = _mm512_extractf32x4_ps(u3, 1);
4810 __m128 res11 = _mm512_extractf32x4_ps(u3, 2);
4811 __m128 res15 = _mm512_extractf32x4_ps(u3, 3);
4812
4813 // Cannot use the same store instructions in both paths of the 'if'
4814 // because the compiler cannot know that there is no aliasing between
4815 // pointers
4816 if (add_into)
4817 {
4818 res0 = _mm_add_ps(_mm_loadu_ps(out[0] + 4 * i), res0);
4819 _mm_storeu_ps(out[0] + 4 * i, res0);
4820 res1 = _mm_add_ps(_mm_loadu_ps(out[1] + 4 * i), res1);
4821 _mm_storeu_ps(out[1] + 4 * i, res1);
4822 res2 = _mm_add_ps(_mm_loadu_ps(out[2] + 4 * i), res2);
4823 _mm_storeu_ps(out[2] + 4 * i, res2);
4824 res3 = _mm_add_ps(_mm_loadu_ps(out[3] + 4 * i), res3);
4825 _mm_storeu_ps(out[3] + 4 * i, res3);
4826 res4 = _mm_add_ps(_mm_loadu_ps(out[4] + 4 * i), res4);
4827 _mm_storeu_ps(out[4] + 4 * i, res4);
4828 res5 = _mm_add_ps(_mm_loadu_ps(out[5] + 4 * i), res5);
4829 _mm_storeu_ps(out[5] + 4 * i, res5);
4830 res6 = _mm_add_ps(_mm_loadu_ps(out[6] + 4 * i), res6);
4831 _mm_storeu_ps(out[6] + 4 * i, res6);
4832 res7 = _mm_add_ps(_mm_loadu_ps(out[7] + 4 * i), res7);
4833 _mm_storeu_ps(out[7] + 4 * i, res7);
4834 res8 = _mm_add_ps(_mm_loadu_ps(out[8] + 4 * i), res8);
4835 _mm_storeu_ps(out[8] + 4 * i, res8);
4836 res9 = _mm_add_ps(_mm_loadu_ps(out[9] + 4 * i), res9);
4837 _mm_storeu_ps(out[9] + 4 * i, res9);
4838 res10 = _mm_add_ps(_mm_loadu_ps(out[10] + 4 * i), res10);
4839 _mm_storeu_ps(out[10] + 4 * i, res10);
4840 res11 = _mm_add_ps(_mm_loadu_ps(out[11] + 4 * i), res11);
4841 _mm_storeu_ps(out[11] + 4 * i, res11);
4842 res12 = _mm_add_ps(_mm_loadu_ps(out[12] + 4 * i), res12);
4843 _mm_storeu_ps(out[12] + 4 * i, res12);
4844 res13 = _mm_add_ps(_mm_loadu_ps(out[13] + 4 * i), res13);
4845 _mm_storeu_ps(out[13] + 4 * i, res13);
4846 res14 = _mm_add_ps(_mm_loadu_ps(out[14] + 4 * i), res14);
4847 _mm_storeu_ps(out[14] + 4 * i, res14);
4848 res15 = _mm_add_ps(_mm_loadu_ps(out[15] + 4 * i), res15);
4849 _mm_storeu_ps(out[15] + 4 * i, res15);
4850 }
4851 else
4852 {
4853 _mm_storeu_ps(out[0] + 4 * i, res0);
4854 _mm_storeu_ps(out[1] + 4 * i, res1);
4855 _mm_storeu_ps(out[2] + 4 * i, res2);
4856 _mm_storeu_ps(out[3] + 4 * i, res3);
4857 _mm_storeu_ps(out[4] + 4 * i, res4);
4858 _mm_storeu_ps(out[5] + 4 * i, res5);
4859 _mm_storeu_ps(out[6] + 4 * i, res6);
4860 _mm_storeu_ps(out[7] + 4 * i, res7);
4861 _mm_storeu_ps(out[8] + 4 * i, res8);
4862 _mm_storeu_ps(out[9] + 4 * i, res9);
4863 _mm_storeu_ps(out[10] + 4 * i, res10);
4864 _mm_storeu_ps(out[11] + 4 * i, res11);
4865 _mm_storeu_ps(out[12] + 4 * i, res12);
4866 _mm_storeu_ps(out[13] + 4 * i, res13);
4867 _mm_storeu_ps(out[14] + 4 * i, res14);
4868 _mm_storeu_ps(out[15] + 4 * i, res15);
4869 }
4870 }
4871
4872 if (add_into)
4873 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4874 for (unsigned int v = 0; v < 16; ++v)
4875 out[v][i] += in[i][v];
4876 else
4877 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4878 for (unsigned int v = 0; v < 16; ++v)
4879 out[v][i] = in[i][v];
4880}
4881
4882
4883
4887template <>
4888inline DEAL_II_ALWAYS_INLINE void
4889vectorized_transpose_and_store(const bool add_into,
4890 const unsigned int n_entries,
4892 const unsigned int *offsets,
4893 float *out)
4894{
4896 n_entries,
4897 in,
4898 {{out + offsets[0],
4899 out + offsets[1],
4900 out + offsets[2],
4901 out + offsets[3],
4902 out + offsets[4],
4903 out + offsets[5],
4904 out + offsets[6],
4905 out + offsets[7],
4906 out + offsets[8],
4907 out + offsets[9],
4908 out + offsets[10],
4909 out + offsets[11],
4910 out + offsets[12],
4911 out + offsets[13],
4912 out + offsets[14],
4913 out + offsets[15]}});
4914}
4915
4916# endif
4917
4918# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__ALTIVEC__) && \
4919 defined(__VSX__)
4920
4921template <>
4922class VectorizedArray<double, 2>
4923 : public VectorizedArrayBase<VectorizedArray<double, 2>, 2>
4924{
4925public:
4929 using value_type = double;
4930
4935 static constexpr bool is_implemented = true;
4936
4941 VectorizedArray() = default;
4942
4946 VectorizedArray(const double scalar)
4947 {
4948 this->operator=(scalar);
4949 }
4950
4954 template <typename U>
4955 VectorizedArray(const std::initializer_list<U> &list)
4956 : VectorizedArrayBase<VectorizedArray<double, 2>, 2>(list)
4957 {}
4958
4964 operator=(const double x) &
4965 {
4966 data = vec_splats(x);
4967
4968 // Some compilers believe that vec_splats sets 'x', but that's not true.
4969 // They then warn about setting a variable and not using it. Suppress the
4970 // warning by "using" the variable:
4971 (void)x;
4972 return *this;
4973 }
4974
4981 operator=(const double scalar) && = delete;
4982
4987 double &
4988 operator[](const unsigned int comp)
4989 {
4990 AssertIndexRange(comp, 2);
4991 return *(reinterpret_cast<double *>(&data) + comp);
4992 }
4993
4998 const double &
4999 operator[](const unsigned int comp) const
5000 {
5001 AssertIndexRange(comp, 2);
5002 return *(reinterpret_cast<const double *>(&data) + comp);
5003 }
5004
5010 operator+=(const VectorizedArray &vec)
5011 {
5012 data = vec_add(data, vec.data);
5013 return *this;
5014 }
5015
5021 operator-=(const VectorizedArray &vec)
5022 {
5023 data = vec_sub(data, vec.data);
5024 return *this;
5025 }
5026
5032 operator*=(const VectorizedArray &vec)
5033 {
5034 data = vec_mul(data, vec.data);
5035 return *this;
5036 }
5037
5043 operator/=(const VectorizedArray &vec)
5044 {
5045 data = vec_div(data, vec.data);
5046 return *this;
5047 }
5048
5054 void
5055 load(const double *ptr)
5056 {
5057 data = vec_vsx_ld(0, ptr);
5058 }
5059
5065 void
5066 store(double *ptr) const
5067 {
5068 vec_vsx_st(data, 0, ptr);
5069 }
5070
5075 void
5076 streaming_store(double *ptr) const
5077 {
5078 store(ptr);
5079 }
5080
5085 void
5086 gather(const double *base_ptr, const unsigned int *offsets)
5087 {
5088 this->operator=(0.);
5089 for (unsigned int i = 0; i < 2; ++i)
5090 if (offsets[i] != numbers::invalid_unsigned_int)
5091 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
5092 }
5093
5098 void
5099 scatter(const unsigned int *offsets, double *base_ptr) const
5100 {
5101 for (unsigned int i = 0; i < 2; ++i)
5102 if (offsets[i] != numbers::invalid_unsigned_int)
5103 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
5104 }
5105
5111 __vector double data;
5112
5113private:
5120 get_sqrt() const
5121 {
5122 VectorizedArray res;
5123 res.data = vec_sqrt(data);
5124 return res;
5125 }
5126
5133 get_abs() const
5134 {
5135 VectorizedArray res;
5136 res.data = vec_abs(data);
5137 return res;
5138 }
5139
5146 get_max(const VectorizedArray &other) const
5147 {
5148 VectorizedArray res;
5149 res.data = vec_max(data, other.data);
5150 return res;
5151 }
5152
5159 get_min(const VectorizedArray &other) const
5160 {
5161 VectorizedArray res;
5162 res.data = vec_min(data, other.data);
5163 return res;
5164 }
5165
5166 // Make a few functions friends.
5167 template <typename Number2, std::size_t width2>
5170 template <typename Number2, std::size_t width2>
5173 template <typename Number2, std::size_t width2>
5177 template <typename Number2, std::size_t width2>
5181};
5182
5183
5184
5185template <>
5186class VectorizedArray<float, 4>
5187 : public VectorizedArrayBase<VectorizedArray<float, 4>, 4>
5188{
5189public:
5193 using value_type = float;
5194
5199 static constexpr bool is_implemented = true;
5200
5205 VectorizedArray() = default;
5206
5210 VectorizedArray(const float scalar)
5211 {
5212 this->operator=(scalar);
5213 }
5214
5218 template <typename U>
5219 VectorizedArray(const std::initializer_list<U> &list)
5220 : VectorizedArrayBase<VectorizedArray<float, 4>, 4>(list)
5221 {}
5222
5228 operator=(const float x) &
5229 {
5230 data = vec_splats(x);
5231
5232 // Some compilers believe that vec_splats sets 'x', but that's not true.
5233 // They then warn about setting a variable and not using it. Suppress the
5234 // warning by "using" the variable:
5235 (void)x;
5236 return *this;
5237 }
5238
5245 operator=(const float scalar) && = delete;
5246
5251 float &
5252 operator[](const unsigned int comp)
5253 {
5254 AssertIndexRange(comp, 4);
5255 return *(reinterpret_cast<float *>(&data) + comp);
5256 }
5257
5262 const float &
5263 operator[](const unsigned int comp) const
5264 {
5265 AssertIndexRange(comp, 4);
5266 return *(reinterpret_cast<const float *>(&data) + comp);
5267 }
5268
5274 operator+=(const VectorizedArray &vec)
5275 {
5276 data = vec_add(data, vec.data);
5277 return *this;
5278 }
5279
5285 operator-=(const VectorizedArray &vec)
5286 {
5287 data = vec_sub(data, vec.data);
5288 return *this;
5289 }
5290
5296 operator*=(const VectorizedArray &vec)
5297 {
5298 data = vec_mul(data, vec.data);
5299 return *this;
5300 }
5301
5307 operator/=(const VectorizedArray &vec)
5308 {
5309 data = vec_div(data, vec.data);
5310 return *this;
5311 }
5312
5318 void
5319 load(const float *ptr)
5320 {
5321 data = vec_vsx_ld(0, ptr);
5322 }
5323
5329 void
5330 store(float *ptr) const
5331 {
5332 vec_vsx_st(data, 0, ptr);
5333 }
5334
5339 void
5340 streaming_store(float *ptr) const
5341 {
5342 store(ptr);
5343 }
5344
5349 void
5350 gather(const float *base_ptr, const unsigned int *offsets)
5351 {
5352 this->operator=(0.f);
5353 for (unsigned int i = 0; i < 4; ++i)
5354 if (offsets[i] != numbers::invalid_unsigned_int)
5355 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
5356 }
5357
5362 void
5363 scatter(const unsigned int *offsets, float *base_ptr) const
5364 {
5365 for (unsigned int i = 0; i < 4; ++i)
5366 if (offsets[i] != numbers::invalid_unsigned_int)
5367 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
5368 }
5369
5375 __vector float data;
5376
5377private:
5384 get_sqrt() const
5385 {
5386 VectorizedArray res;
5387 res.data = vec_sqrt(data);
5388 return res;
5389 }
5390
5397 get_abs() const
5398 {
5399 VectorizedArray res;
5400 res.data = vec_abs(data);
5401 return res;
5402 }
5403
5410 get_max(const VectorizedArray &other) const
5411 {
5412 VectorizedArray res;
5413 res.data = vec_max(data, other.data);
5414 return res;
5415 }
5416
5423 get_min(const VectorizedArray &other) const
5424 {
5425 VectorizedArray res;
5426 res.data = vec_min(data, other.data);
5427 return res;
5428 }
5429
5430 // Make a few functions friends.
5431 template <typename Number2, std::size_t width2>
5434 template <typename Number2, std::size_t width2>
5437 template <typename Number2, std::size_t width2>
5441 template <typename Number2, std::size_t width2>
5445};
5446
5447# endif // if DEAL_II_VECTORIZATION_LEVEL >=1 && defined(__ALTIVEC__) &&
5448 // defined(__VSX__)
5449
5450
5451#endif // DOXYGEN
5452
5453
5454
5459
5465template <typename Number, std::size_t width>
5466inline DEAL_II_ALWAYS_INLINE bool
5469{
5470 for (unsigned int i = 0; i < VectorizedArray<Number, width>::size(); ++i)
5471 if (lhs[i] != rhs[i])
5472 return false;
5473
5474 return true;
5475}
5476
5477
5483template <typename Number, std::size_t width>
5487{
5489 return tmp += v;
5490}
5491
5497template <typename Number, std::size_t width>
5501{
5503 return tmp -= v;
5504}
5505
5511template <typename Number, std::size_t width>
5515{
5517 return tmp *= v;
5518}
5519
5525template <typename Number, std::size_t width>
5529{
5531 return tmp /= v;
5532}
5533
5540template <typename Number, std::size_t width>
5543{
5545 return tmp += v;
5546}
5547
5556template <std::size_t width>
5559{
5561 return tmp += v;
5562}
5563
5570template <typename Number, std::size_t width>
5573{
5574 return u + v;
5575}
5576
5585template <std::size_t width>
5588{
5589 return u + v;
5590}
5591
5598template <typename Number, std::size_t width>
5601{
5603 return tmp -= v;
5604}
5605
5614template <std::size_t width>
5617{
5618 VectorizedArray<float, width> tmp = static_cast<float>(u);
5619 return tmp -= v;
5620}
5621
5628template <typename Number, std::size_t width>
5631{
5633 return v - tmp;
5634}
5635
5644template <std::size_t width>
5647{
5648 VectorizedArray<float, width> tmp = static_cast<float>(u);
5649 return v - tmp;
5650}
5651
5658template <typename Number, std::size_t width>
5661{
5663 return tmp *= v;
5664}
5665
5674template <std::size_t width>
5677{
5678 VectorizedArray<float, width> tmp = static_cast<float>(u);
5679 return tmp *= v;
5680}
5681
5688template <typename Number, std::size_t width>
5691{
5692 return u * v;
5693}
5694
5703template <std::size_t width>
5706{
5707 return u * v;
5708}
5709
5716template <typename Number, std::size_t width>
5719{
5721 return tmp /= v;
5722}
5723
5732template <std::size_t width>
5735{
5736 VectorizedArray<float, width> tmp = static_cast<float>(u);
5737 return tmp /= v;
5738}
5739
5746template <typename Number, std::size_t width>
5749{
5751 return v / tmp;
5752}
5753
5762template <std::size_t width>
5765{
5766 VectorizedArray<float, width> tmp = static_cast<float>(u);
5767 return v / tmp;
5768}
5769
5775template <typename Number, std::size_t width>
5778{
5779 return u;
5780}
5781
5787template <typename Number, std::size_t width>
5790{
5791 // to get a negative sign, subtract the input from zero (could also
5792 // multiply by -1, but this one is slightly simpler)
5793 return VectorizedArray<Number, width>() - u;
5794}
5795
5801template <typename Number, std::size_t width>
5802inline std::ostream &
5803operator<<(std::ostream &out, const VectorizedArray<Number, width> &p)
5804{
5805 constexpr unsigned int n = VectorizedArray<Number, width>::size();
5806 for (unsigned int i = 0; i < n - 1; ++i)
5807 out << p[i] << ' ';
5808 out << p[n - 1];
5809
5810 return out;
5811}
5812
5814
5819
5827enum class SIMDComparison : int
5828{
5829#if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && defined(__AVX__)
5830 equal = _CMP_EQ_OQ,
5831 not_equal = _CMP_NEQ_OQ,
5832 less_than = _CMP_LT_OQ,
5833 less_than_or_equal = _CMP_LE_OQ,
5834 greater_than = _CMP_GT_OQ,
5835 greater_than_or_equal = _CMP_GE_OQ
5836#else
5843#endif
5844};
5845
5846
5910template <SIMDComparison predicate, typename Number>
5911DEAL_II_ALWAYS_INLINE inline Number
5912compare_and_apply_mask(const Number &left,
5913 const Number &right,
5914 const Number &true_value,
5915 const Number &false_value)
5916{
5917 bool mask;
5918 switch (predicate)
5919 {
5921 mask = (left == right);
5922 break;
5924 mask = (left != right);
5925 break;
5927 mask = (left < right);
5928 break;
5930 mask = (left <= right);
5931 break;
5933 mask = (left > right);
5934 break;
5936 mask = (left >= right);
5937 break;
5938 }
5939
5940 return mask ? true_value : false_value;
5941}
5942
5943
5948template <SIMDComparison predicate, typename Number>
5951 const VectorizedArray<Number, 1> &right,
5952 const VectorizedArray<Number, 1> &true_value,
5953 const VectorizedArray<Number, 1> &false_value)
5954{
5957 right.data,
5958 true_value.data,
5959 false_value.data);
5960 return result;
5961}
5962
5964
5965#ifndef DOXYGEN
5966# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 512 && defined(__AVX512F__)
5967
5968template <SIMDComparison predicate>
5971 const VectorizedArray<float, 16> &right,
5972 const VectorizedArray<float, 16> &true_values,
5973 const VectorizedArray<float, 16> &false_values)
5974{
5975 const __mmask16 mask =
5976 _mm512_cmp_ps_mask(left.data, right.data, static_cast<int>(predicate));
5978 result.data = _mm512_mask_mov_ps(false_values.data, mask, true_values.data);
5979 return result;
5980}
5981
5982
5983
5984template <SIMDComparison predicate>
5987 const VectorizedArray<double, 8> &right,
5988 const VectorizedArray<double, 8> &true_values,
5989 const VectorizedArray<double, 8> &false_values)
5990{
5991 const __mmask16 mask =
5992 _mm512_cmp_pd_mask(left.data, right.data, static_cast<int>(predicate));
5994 result.data = _mm512_mask_mov_pd(false_values.data, mask, true_values.data);
5995 return result;
5996}
5997
5998# endif
5999
6000# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && defined(__AVX__)
6001
6002template <SIMDComparison predicate>
6005 const VectorizedArray<float, 8> &right,
6006 const VectorizedArray<float, 8> &true_values,
6007 const VectorizedArray<float, 8> &false_values)
6008{
6009 const auto mask =
6010 _mm256_cmp_ps(left.data, right.data, static_cast<int>(predicate));
6011
6013 result.data = _mm256_blendv_ps(false_values.data, true_values.data, mask);
6014 return result;
6015}
6016
6017
6018template <SIMDComparison predicate>
6021 const VectorizedArray<double, 4> &right,
6022 const VectorizedArray<double, 4> &true_values,
6023 const VectorizedArray<double, 4> &false_values)
6024{
6025 const auto mask =
6026 _mm256_cmp_pd(left.data, right.data, static_cast<int>(predicate));
6027
6029 result.data = _mm256_blendv_pd(false_values.data, true_values.data, mask);
6030 return result;
6031}
6032
6033# endif
6034
6035# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__SSE2__)
6036
6037template <SIMDComparison predicate>
6040 const VectorizedArray<float, 4> &right,
6041 const VectorizedArray<float, 4> &true_values,
6042 const VectorizedArray<float, 4> &false_values)
6043{
6044 __m128 mask;
6045 switch (predicate)
6046 {
6048 mask = _mm_cmpeq_ps(left.data, right.data);
6049 break;
6051 mask = _mm_cmpneq_ps(left.data, right.data);
6052 break;
6054 mask = _mm_cmplt_ps(left.data, right.data);
6055 break;
6057 mask = _mm_cmple_ps(left.data, right.data);
6058 break;
6060 mask = _mm_cmpgt_ps(left.data, right.data);
6061 break;
6063 mask = _mm_cmpge_ps(left.data, right.data);
6064 break;
6065 }
6066
6068 result.data = _mm_or_ps(_mm_and_ps(mask, true_values.data),
6069 _mm_andnot_ps(mask, false_values.data));
6070
6071 return result;
6072}
6073
6074
6075template <SIMDComparison predicate>
6078 const VectorizedArray<double, 2> &right,
6079 const VectorizedArray<double, 2> &true_values,
6080 const VectorizedArray<double, 2> &false_values)
6081{
6082 __m128d mask;
6083 switch (predicate)
6084 {
6086 mask = _mm_cmpeq_pd(left.data, right.data);
6087 break;
6089 mask = _mm_cmpneq_pd(left.data, right.data);
6090 break;
6092 mask = _mm_cmplt_pd(left.data, right.data);
6093 break;
6095 mask = _mm_cmple_pd(left.data, right.data);
6096 break;
6098 mask = _mm_cmpgt_pd(left.data, right.data);
6099 break;
6101 mask = _mm_cmpge_pd(left.data, right.data);
6102 break;
6103 }
6104
6106 result.data = _mm_or_pd(_mm_and_pd(mask, true_values.data),
6107 _mm_andnot_pd(mask, false_values.data));
6108
6109 return result;
6110}
6111
6112# endif
6113
6114# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__ARM_NEON)
6115
6116template <SIMDComparison predicate>
6119 const VectorizedArray<float, 4> &right,
6120 const VectorizedArray<float, 4> &true_values,
6121 const VectorizedArray<float, 4> &false_values)
6122{
6123 uint32x4_t mask;
6124 switch (predicate)
6125 {
6127 mask = vceqq_f32(left.data, right.data);
6128 break;
6130 mask = vmvnq_u32(vceqq_f32(left.data, right.data));
6131 break;
6133 mask = vcltq_f32(left.data, right.data);
6134 break;
6136 mask = vcleq_f32(left.data, right.data);
6137 break;
6139 mask = vcgtq_f32(left.data, right.data);
6140 break;
6142 mask = vcgeq_f32(left.data, right.data);
6143 break;
6144 }
6145
6147 result.data = vreinterpretq_f32_u32(vorrq_u32(
6148 vandq_u32(mask, vreinterpretq_u32_f32(true_values.data)),
6149 vandq_u32(vmvnq_u32(mask), vreinterpretq_u32_f32(false_values.data))));
6150
6151 return result;
6152}
6153
6154
6155template <SIMDComparison predicate>
6158 const VectorizedArray<double, 2> &right,
6159 const VectorizedArray<double, 2> &true_values,
6160 const VectorizedArray<double, 2> &false_values)
6161{
6162 uint64x2_t mask;
6163 switch (predicate)
6164 {
6166 mask = vceqq_f64(left.data, right.data);
6167 break;
6169 mask = vreinterpretq_u64_u32(
6170 vmvnq_u32(vreinterpretq_u32_u64(vceqq_f64(left.data, right.data))));
6171 break;
6173 mask = vcltq_f64(left.data, right.data);
6174 break;
6176 mask = vcleq_f64(left.data, right.data);
6177 break;
6179 mask = vcgtq_f64(left.data, right.data);
6180 break;
6182 mask = vcgeq_f64(left.data, right.data);
6183 break;
6184 }
6185
6187 result.data = vreinterpretq_f64_u64(vorrq_u64(
6188 vandq_u64(mask, vreinterpretq_u64_f64(true_values.data)),
6189 vandq_u64(vreinterpretq_u64_u32(vmvnq_u32(vreinterpretq_u32_u64(mask))),
6190 vreinterpretq_u64_f64(false_values.data))));
6191
6192 return result;
6193}
6194
6195# endif
6196#endif // DOXYGEN
6197
6198
6199namespace internal
6200{
6201 template <typename T>
6203 {
6207 using value_type = T;
6208
6212 static constexpr std::size_t
6214 {
6215 return 1;
6216 }
6217
6222
6229 static constexpr std::size_t
6231 {
6233 }
6234
6238 static value_type &
6239 get(value_type &value, unsigned int c)
6240 {
6241 AssertIndexRange(c, 1);
6242 (void)c;
6243
6244 return value;
6245 }
6246
6250 static const value_type &
6251 get(const value_type &value, unsigned int c)
6252 {
6253 AssertIndexRange(c, 1);
6254 (void)c;
6255
6256 return value;
6257 }
6258
6262 static value_type &
6264 {
6266
6267 return values[c];
6268 }
6269
6274 static const value_type &
6275 get_from_vectorized(const vectorized_value_type &values, unsigned int c)
6276 {
6278
6279 return values[c];
6280 }
6281 };
6282
6283 template <typename T, std::size_t width_>
6285 {
6289 using value_type = T;
6290
6294 static constexpr std::size_t
6296 {
6297 return width_;
6298 }
6299
6304
6312 static constexpr std::size_t
6314 {
6315 return 1;
6316 }
6317
6321 static value_type &
6322 get(vectorized_value_type &values, unsigned int c)
6323 {
6324 AssertIndexRange(c, width_);
6325
6326 return values[c];
6327 }
6328
6332 static const value_type &
6333 get(const vectorized_value_type &values, unsigned int c)
6334 {
6335 AssertIndexRange(c, width_);
6336
6337 return values[c];
6338 }
6339
6343 static vectorized_value_type &
6345 {
6346 (void)c;
6348
6349 return values;
6350 }
6351
6356 static const vectorized_value_type &
6357 get_from_vectorized(const vectorized_value_type &values, unsigned int c)
6358 {
6359 (void)c;
6361
6362 return values;
6363 }
6364 };
6365} // namespace internal
6366
6367
6369
6376namespace std
6377{
6385 template <typename Number, std::size_t width>
6386 inline ::VectorizedArray<Number, width>
6387 sin(const ::VectorizedArray<Number, width> &x)
6388 {
6390 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6391 ++i)
6392 out[i] = std::sin(x[i]);
6393 return out;
6394 }
6395
6396
6397
6405 template <typename Number, std::size_t width>
6406 inline ::VectorizedArray<Number, width>
6407 cos(const ::VectorizedArray<Number, width> &x)
6408 {
6410 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6411 ++i)
6412 out[i] = std::cos(x[i]);
6413 return out;
6414 }
6415
6416
6417
6425 template <typename Number, std::size_t width>
6426 inline ::VectorizedArray<Number, width>
6427 tan(const ::VectorizedArray<Number, width> &x)
6428 {
6430 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6431 ++i)
6432 out[i] = std::tan(x[i]);
6433 return out;
6434 }
6435
6436
6437
6445 template <typename Number, std::size_t width>
6446 inline ::VectorizedArray<Number, width>
6447 acos(const ::VectorizedArray<Number, width> &x)
6448 {
6450 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6451 ++i)
6452 out[i] = std::acos(x[i]);
6453 return out;
6454 }
6455
6456
6457
6465 template <typename Number, std::size_t width>
6466 inline ::VectorizedArray<Number, width>
6467 asin(const ::VectorizedArray<Number, width> &x)
6468 {
6470 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6471 ++i)
6472 out[i] = std::asin(x[i]);
6473 return out;
6474 }
6475
6476
6477
6485 template <typename Number, std::size_t width>
6486 inline ::VectorizedArray<Number, width>
6487 atan(const ::VectorizedArray<Number, width> &x)
6488 {
6490 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6491 ++i)
6492 out[i] = std::atan(x[i]);
6493 return out;
6494 }
6495
6496
6497
6505 template <typename Number, std::size_t width>
6506 inline ::VectorizedArray<Number, width>
6507 cosh(const ::VectorizedArray<Number, width> &x)
6508 {
6510 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6511 ++i)
6512 out[i] = std::cosh(x[i]);
6513 return out;
6514 }
6515
6516
6517
6525 template <typename Number, std::size_t width>
6526 inline ::VectorizedArray<Number, width>
6527 sinh(const ::VectorizedArray<Number, width> &x)
6528 {
6530 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6531 ++i)
6532 out[i] = std::sinh(x[i]);
6533 return out;
6534 }
6535
6536
6537
6545 template <typename Number, std::size_t width>
6546 inline ::VectorizedArray<Number, width>
6547 tanh(const ::VectorizedArray<Number, width> &x)
6548 {
6550 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6551 ++i)
6552 out[i] = std::tanh(x[i]);
6553 return out;
6554 }
6555
6556
6557
6565 template <typename Number, std::size_t width>
6566 inline ::VectorizedArray<Number, width>
6567 acosh(const ::VectorizedArray<Number, width> &x)
6568 {
6570 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6571 ++i)
6572 out[i] = std::acosh(x[i]);
6573 return out;
6574 }
6575
6576
6577
6585 template <typename Number, std::size_t width>
6586 inline ::VectorizedArray<Number, width>
6587 asinh(const ::VectorizedArray<Number, width> &x)
6588 {
6590 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6591 ++i)
6592 out[i] = std::asinh(x[i]);
6593 return out;
6594 }
6595
6596
6597
6605 template <typename Number, std::size_t width>
6606 inline ::VectorizedArray<Number, width>
6607 atanh(const ::VectorizedArray<Number, width> &x)
6608 {
6610 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6611 ++i)
6612 out[i] = std::atanh(x[i]);
6613 return out;
6614 }
6615
6616
6617
6625 template <typename Number, std::size_t width>
6626 inline ::VectorizedArray<Number, width>
6627 exp(const ::VectorizedArray<Number, width> &x)
6628 {
6630 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6631 ++i)
6632 out[i] = std::exp(x[i]);
6633 return out;
6634 }
6635
6636
6637
6645 template <typename Number, std::size_t width>
6646 inline ::VectorizedArray<Number, width>
6647 log(const ::VectorizedArray<Number, width> &x)
6648 {
6650 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6651 ++i)
6652 out[i] = std::log(x[i]);
6653 return out;
6654 }
6655
6656
6657
6665 template <typename Number, std::size_t width>
6666 inline ::VectorizedArray<Number, width>
6667 sqrt(const ::VectorizedArray<Number, width> &x)
6668 {
6669 return x.get_sqrt();
6670 }
6671
6672
6673
6681 template <typename Number, std::size_t width>
6682 inline ::VectorizedArray<Number, width>
6683 pow(const ::VectorizedArray<Number, width> &x, const Number p)
6684 {
6686 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6687 ++i)
6688 out[i] = std::pow(x[i], p);
6689 return out;
6690 }
6691
6692
6693
6702 template <typename Number, std::size_t width>
6703 inline ::VectorizedArray<Number, width>
6704 pow(const ::VectorizedArray<Number, width> &x,
6705 const ::VectorizedArray<Number, width> &p)
6706 {
6708 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6709 ++i)
6710 out[i] = std::pow(x[i], p[i]);
6711 return out;
6712 }
6713
6714
6715
6723 template <typename Number, std::size_t width>
6724 inline ::VectorizedArray<Number, width>
6725 abs(const ::VectorizedArray<Number, width> &x)
6726 {
6727 return x.get_abs();
6728 }
6729
6730
6731
6739 template <typename Number, std::size_t width>
6740 inline ::VectorizedArray<Number, width>
6741 max(const ::VectorizedArray<Number, width> &x,
6742 const ::VectorizedArray<Number, width> &y)
6743 {
6744 return x.get_max(y);
6745 }
6746
6747
6748
6756 template <typename Number, std::size_t width>
6757 inline ::VectorizedArray<Number, width>
6758 min(const ::VectorizedArray<Number, width> &x,
6759 const ::VectorizedArray<Number, width> &y)
6760 {
6761 return x.get_min(y);
6762 }
6763
6764
6765
6769 template <class T>
6771 {
6772#ifdef DEAL_II_HAVE_CXX20
6773 using iterator_category = contiguous_iterator_tag;
6774#else
6775 using iterator_category = random_access_iterator_tag;
6776#endif
6777 using value_type = typename T::value_type;
6778 using difference_type = std::ptrdiff_t;
6779 };
6780
6781} // namespace std
6782
6783#endif
constexpr VectorizedArrayBase()=default
constexpr VectorizedArrayIterator< const VectorizedArrayType > begin() const
constexpr VectorizedArrayBase(const std::initializer_list< U > &list)
constexpr VectorizedArrayIterator< const VectorizedArrayType > end() const
constexpr VectorizedArrayIterator< VectorizedArrayType > begin()
constexpr VectorizedArrayIterator< VectorizedArrayType > end()
static constexpr std::size_t size()
auto dot_product(const VectorizedArrayType &v) const
constexpr VectorizedArrayIterator< T > & operator++()
constexpr VectorizedArrayIterator(T &data, const std::size_t lane)
constexpr VectorizedArrayIterator< T > operator+(const std::size_t &offset) const
constexpr VectorizedArrayIterator< T > & operator+=(const std::size_t offset)
constexpr bool operator==(const VectorizedArrayIterator< T > &other) const
constexpr std::enable_if_t<!std::is_same_v< U, const U >, typename T::value_type > & operator*()
constexpr const T::value_type & operator*() const
constexpr VectorizedArrayIterator< T > & operator--()
constexpr std::ptrdiff_t operator-(const VectorizedArrayIterator< T > &other) const
constexpr bool operator!=(const VectorizedArrayIterator< T > &other) const
VectorizedArray< Number, width > operator-(const VectorizedArray< Number, width > &u)
VectorizedArray & operator=(const Number scalar) &
VectorizedArray< float, width > operator+(const VectorizedArray< float, width > &v, const double u)
VectorizedArray & operator/=(const VectorizedArray &vec)
void gather(const Number *base_ptr, const unsigned int *offsets)
void vectorized_load_and_transpose(const unsigned int n_entries, const Number *in, const unsigned int *offsets, VectorizedArray< Number, width > *out)
VectorizedArray< Number, width > operator+(const VectorizedArray< Number, width > &v, const Number &u)
VectorizedArrayType make_vectorized_array(const typename VectorizedArrayType::value_type &u)
VectorizedArray< Number, width > sinh(const::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > operator/(const VectorizedArray< Number, width > &v, const Number &u)
VectorizedArray< Number, width > acos(const::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > cos(const::VectorizedArray< Number, width > &x)
VectorizedArray get_abs() const
VectorizedArray< float, width > operator/(const VectorizedArray< float, width > &v, const double u)
VectorizedArray< Number, width > exp(const::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > operator*(const VectorizedArray< Number, width > &v, const Number &u)
VectorizedArray< Number, width > operator-(const VectorizedArray< Number, width > &v, const Number &u)
Number & operator[](const unsigned int comp)
VectorizedArray get_floor() const
VectorizedArray< Number, width > asinh(const::VectorizedArray< Number, width > &x)
VectorizedArray< float, width > operator-(const double u, const VectorizedArray< float, width > &v)
VectorizedArray< Number, width > operator+(const Number &u, const VectorizedArray< Number, width > &v)
VectorizedArray< Number, width > operator+(const VectorizedArray< Number, width > &u)
VectorizedArray< Number, width > atanh(const::VectorizedArray< Number, width > &x)
Number sum() const
VectorizedArray()=default
bool operator==(const VectorizedArray< Number, width > &lhs, const VectorizedArray< Number, width > &rhs)
VectorizedArray(const Number scalar)
VectorizedArray< Number, width > operator-(const VectorizedArray< Number, width > &u, const VectorizedArray< Number, width > &v)
VectorizedArray< float, width > operator*(const VectorizedArray< float, width > &v, const double u)
VectorizedArray & operator*=(const VectorizedArray &vec)
VectorizedArray get_max(const VectorizedArray &other) const
const Number & operator[](const unsigned int comp) const
VectorizedArray< Number, width > pow(const::VectorizedArray< Number, width > &x, const::VectorizedArray< Number, width > &p)
VectorizedArray< Number, width > log(const::VectorizedArray< Number, width > &x)
VectorizedArray get_min(const VectorizedArray &other) const
VectorizedArray< Number, width > cosh(const::VectorizedArray< Number, width > &x)
void store(OtherNumber *ptr) const
VectorizedArray< float, width > operator-(const VectorizedArray< float, width > &v, const double u)
VectorizedArray< Number, width > min(const::VectorizedArray< Number, width > &x, const::VectorizedArray< Number, width > &y)
void load(const OtherNumber *ptr)
void scatter(const unsigned int *offsets, Number *base_ptr) const
VectorizedArray< Number, width > operator-(const Number &u, const VectorizedArray< Number, width > &v)
VectorizedArray< Number, width > sin(const::VectorizedArray< Number, width > &x)
VectorizedArray & operator=(const Number scalar) &&=delete
VectorizedArray< Number, width > pow(const::VectorizedArray< Number, width > &x, const Number p)
VectorizedArray< Number, width > atan(const::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > operator*(const VectorizedArray< Number, width > &u, const VectorizedArray< Number, width > &v)
VectorizedArray & operator-=(const VectorizedArray &vec)
VectorizedArray< float, width > operator+(const double u, const VectorizedArray< float, width > &v)
VectorizedArray< Number, width > operator*(const Number &u, const VectorizedArray< Number, width > &v)
VectorizedArray get_sqrt() const
VectorizedArray< Number, width > operator/(const Number &u, const VectorizedArray< Number, width > &v)
VectorizedArray< Number, width > abs(const::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > tan(const::VectorizedArray< Number, width > &x)
VectorizedArray & operator+=(const VectorizedArray &vec)
VectorizedArray< Number, width > acosh(const::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > sqrt(const::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > make_vectorized_array(const Number &u)
VectorizedArray< Number, width > operator/(const VectorizedArray< Number, width > &u, const VectorizedArray< Number, width > &v)
VectorizedArray< Number, width > operator+(const VectorizedArray< Number, width > &u, const VectorizedArray< Number, width > &v)
VectorizedArray< Number, width > max(const::VectorizedArray< Number, width > &x, const::VectorizedArray< Number, width > &y)
VectorizedArray< Number, width > asin(const::VectorizedArray< Number, width > &x)
void streaming_store(Number *ptr) const
VectorizedArray(const std::initializer_list< U > &list)
void vectorized_transpose_and_store(const bool add_into, const unsigned int n_entries, const VectorizedArray< Number, width > *in, const unsigned int *offsets, Number *out)
VectorizedArray< float, width > operator/(const double u, const VectorizedArray< float, width > &v)
VectorizedArray< Number, width > tanh(const::VectorizedArray< Number, width > &x)
VectorizedArray< float, width > operator*(const double u, const VectorizedArray< float, width > &v)
#define DEAL_II_ALWAYS_INLINE
Definition config.h:165
#define DEAL_II_OPENMP_SIMD_PRAGMA
Definition config.h:213
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define Assert(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcMessage(std::string arg1)
const bool IsBlockVector< VectorType >::value
constexpr types::blas_int zero
constexpr unsigned int invalid_unsigned_int
Definition types.h:236
STL namespace.
inline ::VectorizedArray< Number, width > acosh(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > log(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > exp(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &)
inline ::VectorizedArray< Number, width > asinh(const ::VectorizedArray< Number, width > &x)
inline ::VectorizedArray< Number, width > tanh(const ::VectorizedArray< Number, width > &x)
inline ::VectorizedArray< Number, width > sinh(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
inline ::VectorizedArray< Number, width > cosh(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
inline ::VectorizedArray< Number, width > atanh(const ::VectorizedArray< Number, width > &x)
inline ::VectorizedArray< Number, width > atan(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
inline ::VectorizedArray< Number, width > acos(const ::VectorizedArray< Number, width > &x)
inline ::VectorizedArray< Number, width > asin(const ::VectorizedArray< Number, width > &x)
VectorizedArray< typename EnableIfScalar< Number >::type, width > type
static value_type & get(vectorized_value_type &values, unsigned int c)
static vectorized_value_type & get_from_vectorized(vectorized_value_type &values, unsigned int c)
static const value_type & get(const vectorized_value_type &values, unsigned int c)
static const vectorized_value_type & get_from_vectorized(const vectorized_value_type &values, unsigned int c)
static constexpr std::size_t width()
static constexpr std::size_t stride()
static const value_type & get(const value_type &value, unsigned int c)
VectorizedArray< T > vectorized_value_type
static const value_type & get_from_vectorized(const vectorized_value_type &values, unsigned int c)
static value_type & get_from_vectorized(vectorized_value_type &values, unsigned int c)
static value_type & get(value_type &value, unsigned int c)
static constexpr unsigned int max_width
Definition numbers.h:65
void vectorized_load_and_transpose(const unsigned int n_entries, const Number *in, const unsigned int *offsets, VectorizedArray< Number, width > *out)
SIMDComparison
std::ostream & operator<<(std::ostream &out, const VectorizedArray< Number, width > &p)
Number compare_and_apply_mask(const Number &left, const Number &right, const Number &true_value, const Number &false_value)
void gather(VectorizedArray< Number, width > &out, const std::array< const Number *, width > &ptrs, const unsigned int offset)
void vectorized_transpose_and_store(const bool add_into, const unsigned int n_entries, const VectorizedArray< Number, width > *in, const unsigned int *offsets, Number *out)