deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+00:00
\(\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
493 operator=(const Number scalar) &
494 {
495 data = scalar;
496 return *this;
497 }
498
505 operator=(const Number scalar) && = delete;
506
511 DEAL_II_ALWAYS_INLINE inline Number &
512 operator[](const unsigned int comp)
513 {
514 (void)comp;
515 AssertIndexRange(comp, 1);
516 return data;
517 }
518
523 DEAL_II_ALWAYS_INLINE inline const Number &
524 operator[](const unsigned int comp) const
525 {
526 (void)comp;
527 AssertIndexRange(comp, 1);
528 return data;
529 }
530
536 {
537 data += vec.data;
538 return *this;
539 }
540
546 {
547 data -= vec.data;
548 return *this;
549 }
550
556 {
557 data *= vec.data;
558 return *this;
559 }
560
566 {
567 data /= vec.data;
568 return *this;
569 }
570
577 template <typename OtherNumber>
578 DEAL_II_ALWAYS_INLINE inline void
579 load(const OtherNumber *ptr)
580 {
581 data = *ptr;
582 }
583
590 template <typename OtherNumber>
591 DEAL_II_ALWAYS_INLINE inline void
592 store(OtherNumber *ptr) const
593 {
594 *ptr = data;
595 }
596
643 DEAL_II_ALWAYS_INLINE inline void
644 streaming_store(Number *ptr) const
645 {
646 *ptr = data;
647 }
648
666 DEAL_II_ALWAYS_INLINE inline void
667 gather(const Number *base_ptr, const unsigned int *offsets)
668 {
669 this->operator=(Number(0));
670 if (offsets[0] != numbers::invalid_unsigned_int)
671 data = base_ptr[offsets[0]];
672 }
673
689 DEAL_II_ALWAYS_INLINE inline void
690 scatter(const unsigned int *offsets, Number *base_ptr) const
691 {
692 if (offsets[0] != numbers::invalid_unsigned_int)
693 base_ptr[offsets[0]] = data;
694 }
695
700 DEAL_II_ALWAYS_INLINE inline Number
701 sum() const
702 {
703 return data;
704 }
705
710 get_floor() const
711 {
712 VectorizedArray res;
713 res.data = std::floor(data);
714 return res;
715 }
716
722 Number data;
723
724private:
730 get_sqrt() const
731 {
732 VectorizedArray res;
733 res.data = std::sqrt(data);
734 return res;
735 }
736
742 get_abs() const
743 {
744 VectorizedArray res;
745 res.data = std::fabs(data);
746 return res;
747 }
748
754 get_max(const VectorizedArray &other) const
755 {
756 VectorizedArray res;
757 res.data = std::max(data, other.data);
758 return res;
759 }
760
766 get_min(const VectorizedArray &other) const
767 {
768 VectorizedArray res;
769 res.data = std::min(data, other.data);
770 return res;
771 }
772
773 // Make a few functions friends.
774 template <typename Number2, std::size_t width2>
777 template <typename Number2, std::size_t width2>
780 template <typename Number2, std::size_t width2>
784 template <typename Number2, std::size_t width2>
788};
789
790
791
803template <typename Number,
804 std::size_t width =
807make_vectorized_array(const Number &u)
808{
810 return result;
811}
812
813
814
821template <typename VectorizedArrayType>
822DEAL_II_ALWAYS_INLINE inline VectorizedArrayType
823make_vectorized_array(const typename VectorizedArrayType::value_type &u)
824{
825 static_assert(
826 std::is_same_v<VectorizedArrayType,
827 VectorizedArray<typename VectorizedArrayType::value_type,
828 VectorizedArrayType::size()>>,
829 "VectorizedArrayType is not a VectorizedArray.");
830
831 VectorizedArrayType result = u;
832 return result;
833}
834
835
836
848template <typename Number, std::size_t width>
849DEAL_II_ALWAYS_INLINE inline void
851 const std::array<const Number *, width> &ptrs,
852 const unsigned int offset)
853{
854 for (unsigned int v = 0; v < width; ++v)
855 out.data[v] = ptrs[v][offset];
856}
857
858
859
885template <typename Number, std::size_t width>
886DEAL_II_ALWAYS_INLINE inline void
887vectorized_load_and_transpose(const unsigned int n_entries,
888 const Number *in,
889 const unsigned int *offsets,
891{
892 for (unsigned int i = 0; i < n_entries; ++i)
893 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
894 out[i][v] = in[offsets[v] + i];
895}
896
897
909template <typename Number, std::size_t width>
910DEAL_II_ALWAYS_INLINE inline void
911vectorized_load_and_transpose(const unsigned int n_entries,
912 const std::array<const Number *, width> &in,
914{
915 for (unsigned int i = 0; i < n_entries; ++i)
916 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
917 out[i][v] = in[v][i];
918}
919
920
921
960template <typename Number, std::size_t width>
961DEAL_II_ALWAYS_INLINE inline void
963 const unsigned int n_entries,
965 const unsigned int *offsets,
966 Number *out)
967{
968 if (add_into)
969 for (unsigned int i = 0; i < n_entries; ++i)
970 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
971 out[offsets[v] + i] += in[i][v];
972 else
973 for (unsigned int i = 0; i < n_entries; ++i)
974 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
975 out[offsets[v] + i] = in[i][v];
976}
977
978
990template <typename Number, std::size_t width>
991DEAL_II_ALWAYS_INLINE inline void
993 const unsigned int n_entries,
995 const std::array<Number *, width> &out)
996{
997 if (add_into)
998 for (unsigned int i = 0; i < n_entries; ++i)
999 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
1000 out[v][i] += in[i][v];
1001 else
1002 for (unsigned int i = 0; i < n_entries; ++i)
1003 for (unsigned int v = 0; v < VectorizedArray<Number, width>::size(); ++v)
1004 out[v][i] = in[i][v];
1005}
1006
1007
1010#ifndef DOXYGEN
1011
1012# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__ARM_NEON)
1013
1017template <>
1018class VectorizedArray<double, 2>
1019 : public VectorizedArrayBase<VectorizedArray<double, 2>, 2>
1020{
1021public:
1025 using value_type = double;
1026
1031 static constexpr bool is_implemented = true;
1032
1037 VectorizedArray() = default;
1038
1042 VectorizedArray(const double scalar)
1043 {
1044 this->operator=(scalar);
1045 }
1046
1050 template <typename U>
1051 VectorizedArray(const std::initializer_list<U> &list)
1052 : VectorizedArrayBase<VectorizedArray<double, 2>, 2>(list)
1053 {}
1054
1059 operator=(const double x) &
1060 {
1061 data = vdupq_n_f64(x);
1062 return *this;
1063 }
1064
1071 operator=(const double scalar) && = delete;
1072
1076 double &
1077 operator[](const unsigned int comp)
1078 {
1079 return *(reinterpret_cast<double *>(&data) + comp);
1080 }
1081
1085 const double &
1086 operator[](const unsigned int comp) const
1087 {
1088 return *(reinterpret_cast<const double *>(&data) + comp);
1089 }
1090
1095 operator+=(const VectorizedArray &vec)
1096 {
1097 data = vaddq_f64(data, vec.data);
1098 return *this;
1099 }
1100
1105 operator-=(const VectorizedArray &vec)
1106 {
1107 data = vsubq_f64(data, vec.data);
1108 return *this;
1109 }
1110
1115 operator*=(const VectorizedArray &vec)
1116 {
1117 data = vmulq_f64(data, vec.data);
1118 return *this;
1119 }
1120
1125 operator/=(const VectorizedArray &vec)
1126 {
1127 data = vdivq_f64(data, vec.data);
1128 return *this;
1129 }
1130
1136 void
1137 load(const double *ptr)
1138 {
1139 data = vld1q_f64(ptr);
1140 }
1141
1142 DEAL_II_ALWAYS_INLINE inline void
1143 load(const float *ptr)
1144 {
1146 for (unsigned int i = 0; i < 2; ++i)
1147 data[i] = ptr[i];
1148 }
1149
1156 void
1157 store(double *ptr) const
1158 {
1159 vst1q_f64(ptr, data);
1160 }
1161
1162 DEAL_II_ALWAYS_INLINE inline void
1163 store(float *ptr) const
1164 {
1166 for (unsigned int i = 0; i < 2; ++i)
1167 ptr[i] = data[i];
1168 }
1169
1174 DEAL_II_ALWAYS_INLINE inline void
1175 streaming_store(double *ptr) const
1176 {
1177 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
1178 ExcMessage("Memory not aligned"));
1179 vst1q_f64(ptr, data);
1180 }
1181
1185 void
1186 gather(const double *base_ptr, const unsigned int *offsets)
1187 {
1188 this->operator=(0.);
1189 for (unsigned int i = 0; i < 2; ++i)
1190 if (offsets[i] != numbers::invalid_unsigned_int)
1191 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
1192 }
1193
1197 void
1198 scatter(const unsigned int *offsets, double *base_ptr) const
1199 {
1200 for (unsigned int i = 0; i < 2; ++i)
1201 if (offsets[i] != numbers::invalid_unsigned_int)
1202 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
1203 }
1204
1209 double
1210 sum() const
1211 {
1212 return vaddvq_f64(data);
1213 }
1214
1219 get_floor() const
1220 {
1221 VectorizedArray res;
1222 res.data = vrndmq_f64(data);
1223 return res;
1224 }
1225
1231 mutable float64x2_t data;
1232
1233private:
1239 get_sqrt() const
1240 {
1241 VectorizedArray res;
1242 res.data = vsqrtq_f64(data);
1243 return res;
1244 }
1245
1251 get_abs() const
1252 {
1253 VectorizedArray res;
1254 res.data = vabsq_f64(data);
1255 return res;
1256 }
1257
1263 get_max(const VectorizedArray &other) const
1264 {
1265 VectorizedArray res;
1266 res.data = vmaxq_f64(data, other.data);
1267 return res;
1268 }
1269
1275 get_min(const VectorizedArray &other) const
1276 {
1277 VectorizedArray res;
1278 res.data = vminq_f64(data, other.data);
1279 return res;
1280 }
1281
1282 // Make a few functions friends.
1283 template <typename Number2, std::size_t width2>
1286 template <typename Number2, std::size_t width2>
1289 template <typename Number2, std::size_t width2>
1293 template <typename Number2, std::size_t width2>
1297};
1298
1302template <>
1303class VectorizedArray<float, 4>
1304 : public VectorizedArrayBase<VectorizedArray<float, 4>, 4>
1305{
1306public:
1310 using value_type = float;
1311
1316 static constexpr bool is_implemented = true;
1317
1322 VectorizedArray() = default;
1323
1327 VectorizedArray(const float scalar)
1328 {
1329 this->operator=(scalar);
1330 }
1331
1335 template <typename U>
1336 VectorizedArray(const std::initializer_list<U> &list)
1337 : VectorizedArrayBase<VectorizedArray<float, 4>, 4>(list)
1338 {}
1339
1344 operator=(const float x) &
1345 {
1346 data = vdupq_n_f32(x);
1347 return *this;
1348 }
1349
1356 operator=(const float scalar) && = delete;
1357
1361 value_type &
1362 operator[](const unsigned int comp)
1363 {
1364 return *(reinterpret_cast<float *>(&data) + comp);
1365 }
1366
1370 const value_type &
1371 operator[](const unsigned int comp) const
1372 {
1373 return *(reinterpret_cast<const float *>(&data) + comp);
1374 }
1375
1380 operator+=(const VectorizedArray &vec)
1381 {
1382 data = vaddq_f32(data, vec.data);
1383 return *this;
1384 }
1385
1390 operator-=(const VectorizedArray &vec)
1391 {
1392 data = vsubq_f32(data, vec.data);
1393 return *this;
1394 }
1395
1400 operator*=(const VectorizedArray &vec)
1401 {
1402 data = vmulq_f32(data, vec.data);
1403 return *this;
1404 }
1405
1410 operator/=(const VectorizedArray &vec)
1411 {
1412 data = vdivq_f32(data, vec.data);
1413 return *this;
1414 }
1415
1421 void
1422 load(const float *ptr)
1423 {
1424 data = vld1q_f32(ptr);
1425 }
1426
1433 void
1434 store(float *ptr) const
1435 {
1436 vst1q_f32(ptr, data);
1437 }
1438
1443 DEAL_II_ALWAYS_INLINE inline void
1444 streaming_store(float *ptr) const
1445 {
1446 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
1447 ExcMessage("Memory not aligned"));
1448 vst1q_f32(ptr, data);
1449 }
1450
1454 void
1455 gather(const float *base_ptr, const unsigned int *offsets)
1456 {
1457 this->operator=(0.f);
1458 for (unsigned int i = 0; i < 4; ++i)
1459 if (offsets[i] != numbers::invalid_unsigned_int)
1460 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
1461 }
1462
1466 void
1467 scatter(const unsigned int *offsets, float *base_ptr) const
1468 {
1469 for (unsigned int i = 0; i < 4; ++i)
1470 if (offsets[i] != numbers::invalid_unsigned_int)
1471 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
1472 }
1473
1478 float
1479 sum() const
1480 {
1481 return vaddvq_f32(data);
1482 }
1483
1484
1489 get_floor() const
1490 {
1491 VectorizedArray res;
1492 res.data = vrndmq_f32(data);
1493 return res;
1494 }
1495
1501 mutable float32x4_t data;
1502
1503private:
1509 get_sqrt() const
1510 {
1511 VectorizedArray res;
1512 res.data = vsqrtq_f32(data);
1513 return res;
1514 }
1515
1521 get_abs() const
1522 {
1523 VectorizedArray res;
1524 res.data = vabsq_f32(data);
1525 return res;
1526 }
1527
1533 get_max(const VectorizedArray &other) const
1534 {
1535 VectorizedArray res;
1536 res.data = vmaxq_f32(data, other.data);
1537 return res;
1538 }
1539
1545 get_min(const VectorizedArray &other) const
1546 {
1547 VectorizedArray res;
1548 res.data = vminq_f32(data, other.data);
1549 return res;
1550 }
1551
1552 // Make a few functions friends.
1553 template <typename Number2, std::size_t width2>
1556 template <typename Number2, std::size_t width2>
1559 template <typename Number2, std::size_t width2>
1563 template <typename Number2, std::size_t width2>
1567};
1568
1569
1570# endif
1571
1572# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__SSE2__)
1573
1577template <>
1578class VectorizedArray<double, 2>
1579 : public VectorizedArrayBase<VectorizedArray<double, 2>, 2>
1580{
1581public:
1585 using value_type = double;
1586
1591 static constexpr bool is_implemented = true;
1592
1597 VectorizedArray() = default;
1598
1602 VectorizedArray(const double scalar)
1603 {
1604 this->operator=(scalar);
1605 }
1606
1610 template <typename U>
1611 VectorizedArray(const std::initializer_list<U> &list)
1612 : VectorizedArrayBase<VectorizedArray<double, 2>, 2>(list)
1613 {}
1614
1619 operator=(const double x) &
1620 {
1621 data = _mm_set1_pd(x);
1622 return *this;
1623 }
1624
1631 operator=(const double scalar) && = delete;
1632
1636 DEAL_II_ALWAYS_INLINE inline double &
1637 operator[](const unsigned int comp)
1638 {
1639 AssertIndexRange(comp, 2);
1640 return *(reinterpret_cast<double *>(&data) + comp);
1641 }
1642
1646 DEAL_II_ALWAYS_INLINE inline const double &
1647 operator[](const unsigned int comp) const
1648 {
1649 AssertIndexRange(comp, 2);
1650 return *(reinterpret_cast<const double *>(&data) + comp);
1651 }
1652
1657 operator+=(const VectorizedArray &vec)
1658 {
1659# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1660 data += vec.data;
1661# else
1662 data = _mm_add_pd(data, vec.data);
1663# endif
1664 return *this;
1665 }
1666
1671 operator-=(const VectorizedArray &vec)
1672 {
1673# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1674 data -= vec.data;
1675# else
1676 data = _mm_sub_pd(data, vec.data);
1677# endif
1678 return *this;
1679 }
1680
1685 operator*=(const VectorizedArray &vec)
1686 {
1687# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1688 data *= vec.data;
1689# else
1690 data = _mm_mul_pd(data, vec.data);
1691# endif
1692 return *this;
1693 }
1694
1699 operator/=(const VectorizedArray &vec)
1700 {
1701# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
1702 data /= vec.data;
1703# else
1704 data = _mm_div_pd(data, vec.data);
1705# endif
1706 return *this;
1707 }
1708
1714 DEAL_II_ALWAYS_INLINE inline void
1715 load(const double *ptr)
1716 {
1717 data = _mm_loadu_pd(ptr);
1718 }
1719
1720 DEAL_II_ALWAYS_INLINE inline void
1721 load(const float *ptr)
1722 {
1724 for (unsigned int i = 0; i < 2; ++i)
1725 data[i] = ptr[i];
1726 }
1727
1734 DEAL_II_ALWAYS_INLINE inline void
1735 store(double *ptr) const
1736 {
1737 _mm_storeu_pd(ptr, data);
1738 }
1739
1740 DEAL_II_ALWAYS_INLINE inline void
1741 store(float *ptr) const
1742 {
1744 for (unsigned int i = 0; i < 2; ++i)
1745 ptr[i] = data[i];
1746 }
1747
1752 DEAL_II_ALWAYS_INLINE inline void
1753 streaming_store(double *ptr) const
1754 {
1755 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
1756 ExcMessage("Memory not aligned"));
1757 _mm_stream_pd(ptr, data);
1758 }
1759
1763 DEAL_II_ALWAYS_INLINE inline void
1764 gather(const double *base_ptr, const unsigned int *offsets)
1765 {
1766 this->operator=(0.);
1767 for (unsigned int i = 0; i < 2; ++i)
1768 if (offsets[i] != numbers::invalid_unsigned_int)
1769 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
1770 }
1771
1775 DEAL_II_ALWAYS_INLINE inline void
1776 scatter(const unsigned int *offsets, double *base_ptr) const
1777 {
1778 for (unsigned int i = 0; i < 2; ++i)
1779 if (offsets[i] != numbers::invalid_unsigned_int)
1780 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
1781 }
1782
1787 double
1788 sum() const
1789 {
1790 __m128d t1 = _mm_unpackhi_pd(data, data);
1791 __m128d t2 = _mm_add_pd(data, t1);
1792 return _mm_cvtsd_f64(t2);
1793 }
1794
1799 get_floor() const
1800 {
1801 VectorizedArray res;
1802 for (std::size_t i = 0; i < 2; ++i)
1803 res.data[i] = std::floor(data[i]);
1804 return res;
1805 }
1806
1812 __m128d data;
1813
1814private:
1820 get_sqrt() const
1821 {
1822 VectorizedArray res;
1823 res.data = _mm_sqrt_pd(data);
1824 return res;
1825 }
1826
1832 get_abs() const
1833 {
1834 // to compute the absolute value, perform
1835 // bitwise andnot with -0. This will leave all
1836 // value and exponent bits unchanged but force
1837 // the sign value to +.
1838 __m128d mask = _mm_set1_pd(-0.);
1839 VectorizedArray res;
1840 res.data = _mm_andnot_pd(mask, data);
1841 return res;
1842 }
1843
1849 get_max(const VectorizedArray &other) const
1850 {
1851 VectorizedArray res;
1852 res.data = _mm_max_pd(data, other.data);
1853 return res;
1854 }
1855
1861 get_min(const VectorizedArray &other) const
1862 {
1863 VectorizedArray res;
1864 res.data = _mm_min_pd(data, other.data);
1865 return res;
1866 }
1867
1868 // Make a few functions friends.
1869 template <typename Number2, std::size_t width2>
1872 template <typename Number2, std::size_t width2>
1875 template <typename Number2, std::size_t width2>
1879 template <typename Number2, std::size_t width2>
1883};
1884
1885
1886
1890template <>
1891DEAL_II_ALWAYS_INLINE inline void
1892vectorized_load_and_transpose(const unsigned int n_entries,
1893 const std::array<const double *, 2> &in,
1895{
1896 const unsigned int n_chunks = n_entries / 2;
1897 for (unsigned int i = 0; i < n_chunks; ++i)
1898 {
1899 __m128d u0 = _mm_loadu_pd(in[0] + 2 * i);
1900 __m128d u1 = _mm_loadu_pd(in[1] + 2 * i);
1901 out[2 * i + 0].data = _mm_unpacklo_pd(u0, u1);
1902 out[2 * i + 1].data = _mm_unpackhi_pd(u0, u1);
1903 }
1904
1905 // remainder loop of work that does not divide by 2
1906 for (unsigned int i = 2 * n_chunks; i < n_entries; ++i)
1907 for (unsigned int v = 0; v < 2; ++v)
1908 out[i][v] = in[v][i];
1909}
1910
1911
1912
1916template <>
1917DEAL_II_ALWAYS_INLINE inline void
1918vectorized_load_and_transpose(const unsigned int n_entries,
1919 const double *in,
1920 const unsigned int *offsets,
1922{
1924 {{in + offsets[0], in + offsets[1]}},
1925 out);
1926}
1927
1928
1929
1933template <>
1934DEAL_II_ALWAYS_INLINE inline void
1935vectorized_transpose_and_store(const bool add_into,
1936 const unsigned int n_entries,
1938 const std::array<double *, 2> &out)
1939{
1940 const unsigned int n_chunks = n_entries / 2;
1941 if (add_into)
1942 {
1943 for (unsigned int i = 0; i < n_chunks; ++i)
1944 {
1945 __m128d u0 = in[2 * i + 0].data;
1946 __m128d u1 = in[2 * i + 1].data;
1947 __m128d res0 = _mm_unpacklo_pd(u0, u1);
1948 __m128d res1 = _mm_unpackhi_pd(u0, u1);
1949 _mm_storeu_pd(out[0] + 2 * i,
1950 _mm_add_pd(_mm_loadu_pd(out[0] + 2 * i), res0));
1951 _mm_storeu_pd(out[1] + 2 * i,
1952 _mm_add_pd(_mm_loadu_pd(out[1] + 2 * i), res1));
1953 }
1954
1955 // remainder loop for lengths that do not divide by 2
1956 for (unsigned int i = 2 * n_chunks; i < n_entries; ++i)
1957 for (unsigned int v = 0; v < 2; ++v)
1958 out[v][i] += in[i][v];
1959 }
1960 else
1961 {
1962 for (unsigned int i = 0; i < n_chunks; ++i)
1963 {
1964 __m128d u0 = in[2 * i + 0].data;
1965 __m128d u1 = in[2 * i + 1].data;
1966 __m128d res0 = _mm_unpacklo_pd(u0, u1);
1967 __m128d res1 = _mm_unpackhi_pd(u0, u1);
1968 _mm_storeu_pd(out[0] + 2 * i, res0);
1969 _mm_storeu_pd(out[1] + 2 * i, res1);
1970 }
1971
1972 // remainder loop for lengths that do not divide by 2
1973 for (unsigned int i = 2 * n_chunks; i < n_entries; ++i)
1974 for (unsigned int v = 0; v < 2; ++v)
1975 out[v][i] = in[i][v];
1976 }
1977}
1978
1979
1980
1984template <>
1985DEAL_II_ALWAYS_INLINE inline void
1986vectorized_transpose_and_store(const bool add_into,
1987 const unsigned int n_entries,
1989 const unsigned int *offsets,
1990 double *out)
1991{
1993 n_entries,
1994 in,
1995 {{out + offsets[0], out + offsets[1]}});
1996}
1997
1998
1999
2003template <>
2004class VectorizedArray<float, 4>
2005 : public VectorizedArrayBase<VectorizedArray<float, 4>, 4>
2006{
2007public:
2011 using value_type = float;
2012
2017 static constexpr bool is_implemented = true;
2018
2023 VectorizedArray() = default;
2024
2028 VectorizedArray(const float scalar)
2029 {
2030 this->operator=(scalar);
2031 }
2032
2036 template <typename U>
2037 VectorizedArray(const std::initializer_list<U> &list)
2038 : VectorizedArrayBase<VectorizedArray<float, 4>, 4>(list)
2039 {}
2040
2045 operator=(const float x) &
2046 {
2047 data = _mm_set1_ps(x);
2048 return *this;
2049 }
2050
2057 operator=(const float scalar) && = delete;
2058
2062 DEAL_II_ALWAYS_INLINE inline float &
2063 operator[](const unsigned int comp)
2064 {
2065 AssertIndexRange(comp, 4);
2066 return *(reinterpret_cast<float *>(&data) + comp);
2067 }
2068
2072 DEAL_II_ALWAYS_INLINE inline const float &
2073 operator[](const unsigned int comp) const
2074 {
2075 AssertIndexRange(comp, 4);
2076 return *(reinterpret_cast<const float *>(&data) + comp);
2077 }
2078
2083 operator+=(const VectorizedArray &vec)
2084 {
2085# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2086 data += vec.data;
2087# else
2088 data = _mm_add_ps(data, vec.data);
2089# endif
2090 return *this;
2091 }
2092
2097 operator-=(const VectorizedArray &vec)
2098 {
2099# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2100 data -= vec.data;
2101# else
2102 data = _mm_sub_ps(data, vec.data);
2103# endif
2104 return *this;
2105 }
2106
2111 operator*=(const VectorizedArray &vec)
2112 {
2113# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2114 data *= vec.data;
2115# else
2116 data = _mm_mul_ps(data, vec.data);
2117# endif
2118 return *this;
2119 }
2120
2125 operator/=(const VectorizedArray &vec)
2126 {
2127# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2128 data /= vec.data;
2129# else
2130 data = _mm_div_ps(data, vec.data);
2131# endif
2132 return *this;
2133 }
2134
2140 DEAL_II_ALWAYS_INLINE inline void
2141 load(const float *ptr)
2142 {
2143 data = _mm_loadu_ps(ptr);
2144 }
2145
2152 DEAL_II_ALWAYS_INLINE inline void
2153 store(float *ptr) const
2154 {
2155 _mm_storeu_ps(ptr, data);
2156 }
2157
2162 DEAL_II_ALWAYS_INLINE inline void
2163 streaming_store(float *ptr) const
2164 {
2165 Assert(reinterpret_cast<std::size_t>(ptr) % 16 == 0,
2166 ExcMessage("Memory not aligned"));
2167 _mm_stream_ps(ptr, data);
2168 }
2169
2173 DEAL_II_ALWAYS_INLINE inline void
2174 gather(const float *base_ptr, const unsigned int *offsets)
2175 {
2176 this->operator=(0.f);
2177 for (unsigned int i = 0; i < 4; ++i)
2178 if (offsets[i] != numbers::invalid_unsigned_int)
2179 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
2180 }
2181
2185 DEAL_II_ALWAYS_INLINE inline void
2186 scatter(const unsigned int *offsets, float *base_ptr) const
2187 {
2188 for (unsigned int i = 0; i < 4; ++i)
2189 if (offsets[i] != numbers::invalid_unsigned_int)
2190 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
2191 }
2192
2197 float
2198 sum() const
2199 {
2200 __m128 t1 = _mm_movehl_ps(data, data);
2201 __m128 t2 = _mm_add_ps(data, t1);
2202 __m128 t3 = _mm_shuffle_ps(t2, t2, 1);
2203 __m128 t4 = _mm_add_ss(t2, t3);
2204 return _mm_cvtss_f32(t4);
2205 }
2206
2211 get_floor() const
2212 {
2213 VectorizedArray res;
2214 for (std::size_t i = 0; i < 4; ++i)
2215 res.data[i] = std::floor(data[i]);
2216 return res;
2217 }
2218
2224 __m128 data;
2225
2226private:
2232 get_sqrt() const
2233 {
2234 VectorizedArray res;
2235 res.data = _mm_sqrt_ps(data);
2236 return res;
2237 }
2238
2244 get_abs() const
2245 {
2246 // to compute the absolute value, perform bitwise andnot with -0. This
2247 // will leave all value and exponent bits unchanged but force the sign
2248 // value to +.
2249 __m128 mask = _mm_set1_ps(-0.f);
2250 VectorizedArray res;
2251 res.data = _mm_andnot_ps(mask, data);
2252 return res;
2253 }
2254
2260 get_max(const VectorizedArray &other) const
2261 {
2262 VectorizedArray res;
2263 res.data = _mm_max_ps(data, other.data);
2264 return res;
2265 }
2266
2272 get_min(const VectorizedArray &other) const
2273 {
2274 VectorizedArray res;
2275 res.data = _mm_min_ps(data, other.data);
2276 return res;
2277 }
2278
2279 // Make a few functions friends.
2280 template <typename Number2, std::size_t width2>
2283 template <typename Number2, std::size_t width2>
2286 template <typename Number2, std::size_t width2>
2290 template <typename Number2, std::size_t width2>
2294};
2295
2296
2297
2301template <>
2302DEAL_II_ALWAYS_INLINE inline void
2303vectorized_load_and_transpose(const unsigned int n_entries,
2304 const std::array<const float *, 4> &in,
2306{
2307 const unsigned int n_chunks = n_entries / 4;
2308 for (unsigned int i = 0; i < n_chunks; ++i)
2309 {
2310 __m128 u0 = _mm_loadu_ps(in[0] + 4 * i);
2311 __m128 u1 = _mm_loadu_ps(in[1] + 4 * i);
2312 __m128 u2 = _mm_loadu_ps(in[2] + 4 * i);
2313 __m128 u3 = _mm_loadu_ps(in[3] + 4 * i);
2314 __m128 v0 = _mm_shuffle_ps(u0, u1, 0x44);
2315 __m128 v1 = _mm_shuffle_ps(u0, u1, 0xee);
2316 __m128 v2 = _mm_shuffle_ps(u2, u3, 0x44);
2317 __m128 v3 = _mm_shuffle_ps(u2, u3, 0xee);
2318 out[4 * i + 0].data = _mm_shuffle_ps(v0, v2, 0x88);
2319 out[4 * i + 1].data = _mm_shuffle_ps(v0, v2, 0xdd);
2320 out[4 * i + 2].data = _mm_shuffle_ps(v1, v3, 0x88);
2321 out[4 * i + 3].data = _mm_shuffle_ps(v1, v3, 0xdd);
2322 }
2323
2324 // remainder loop for lengths not divisible by 4
2325 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2326 for (unsigned int v = 0; v < 4; ++v)
2327 out[i][v] = in[v][i];
2328}
2329
2330
2331
2335template <>
2336DEAL_II_ALWAYS_INLINE inline void
2337vectorized_load_and_transpose(const unsigned int n_entries,
2338 const float *in,
2339 const unsigned int *offsets,
2341{
2343 n_entries,
2344 {{in + offsets[0], in + offsets[1], in + offsets[2], in + offsets[3]}},
2345 out);
2346}
2347
2348
2349
2353template <>
2354DEAL_II_ALWAYS_INLINE inline void
2355vectorized_transpose_and_store(const bool add_into,
2356 const unsigned int n_entries,
2357 const VectorizedArray<float, 4> *in,
2358 const std::array<float *, 4> &out)
2359{
2360 const unsigned int n_chunks = n_entries / 4;
2361 for (unsigned int i = 0; i < n_chunks; ++i)
2362 {
2363 __m128 u0 = in[4 * i + 0].data;
2364 __m128 u1 = in[4 * i + 1].data;
2365 __m128 u2 = in[4 * i + 2].data;
2366 __m128 u3 = in[4 * i + 3].data;
2367 __m128 t0 = _mm_shuffle_ps(u0, u1, 0x44);
2368 __m128 t1 = _mm_shuffle_ps(u0, u1, 0xee);
2369 __m128 t2 = _mm_shuffle_ps(u2, u3, 0x44);
2370 __m128 t3 = _mm_shuffle_ps(u2, u3, 0xee);
2371 u0 = _mm_shuffle_ps(t0, t2, 0x88);
2372 u1 = _mm_shuffle_ps(t0, t2, 0xdd);
2373 u2 = _mm_shuffle_ps(t1, t3, 0x88);
2374 u3 = _mm_shuffle_ps(t1, t3, 0xdd);
2375
2376 // Cannot use the same store instructions in both paths of the 'if'
2377 // because the compiler cannot know that there is no aliasing between
2378 // pointers
2379 if (add_into)
2380 {
2381 u0 = _mm_add_ps(_mm_loadu_ps(out[0] + 4 * i), u0);
2382 _mm_storeu_ps(out[0] + 4 * i, u0);
2383 u1 = _mm_add_ps(_mm_loadu_ps(out[1] + 4 * i), u1);
2384 _mm_storeu_ps(out[1] + 4 * i, u1);
2385 u2 = _mm_add_ps(_mm_loadu_ps(out[2] + 4 * i), u2);
2386 _mm_storeu_ps(out[2] + 4 * i, u2);
2387 u3 = _mm_add_ps(_mm_loadu_ps(out[3] + 4 * i), u3);
2388 _mm_storeu_ps(out[3] + 4 * i, u3);
2389 }
2390 else
2391 {
2392 _mm_storeu_ps(out[0] + 4 * i, u0);
2393 _mm_storeu_ps(out[1] + 4 * i, u1);
2394 _mm_storeu_ps(out[2] + 4 * i, u2);
2395 _mm_storeu_ps(out[3] + 4 * i, u3);
2396 }
2397 }
2398
2399 // remainder loop of work that does not divide by 4
2400 if (add_into)
2401 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2402 for (unsigned int v = 0; v < 4; ++v)
2403 out[v][i] += in[i][v];
2404 else
2405 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2406 for (unsigned int v = 0; v < 4; ++v)
2407 out[v][i] = in[i][v];
2408}
2409
2410
2411
2415template <>
2416DEAL_II_ALWAYS_INLINE inline void
2417vectorized_transpose_and_store(const bool add_into,
2418 const unsigned int n_entries,
2419 const VectorizedArray<float, 4> *in,
2420 const unsigned int *offsets,
2421 float *out)
2422{
2424 add_into,
2425 n_entries,
2426 in,
2427 {{out + offsets[0], out + offsets[1], out + offsets[2], out + offsets[3]}});
2428}
2429
2430
2431
2432# endif // if DEAL_II_VECTORIZATION_WIDTH_IN_BITS > 0 && defined(__SSE2__)
2433
2434# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && defined(__AVX__)
2435
2439template <>
2440class VectorizedArray<double, 4>
2441 : public VectorizedArrayBase<VectorizedArray<double, 4>, 4>
2442{
2443public:
2447 using value_type = double;
2448
2453 static constexpr bool is_implemented = true;
2454
2459 VectorizedArray() = default;
2460
2464 VectorizedArray(const double scalar)
2465 {
2466 this->operator=(scalar);
2467 }
2468
2472 template <typename U>
2473 VectorizedArray(const std::initializer_list<U> &list)
2474 : VectorizedArrayBase<VectorizedArray<double, 4>, 4>(list)
2475 {}
2476
2481 operator=(const double x) &
2482 {
2483 data = _mm256_set1_pd(x);
2484 return *this;
2485 }
2486
2493 operator=(const double scalar) && = delete;
2494
2498 DEAL_II_ALWAYS_INLINE inline double &
2499 operator[](const unsigned int comp)
2500 {
2501 AssertIndexRange(comp, 4);
2502 return *(reinterpret_cast<double *>(&data) + comp);
2503 }
2504
2508 DEAL_II_ALWAYS_INLINE inline const double &
2509 operator[](const unsigned int comp) const
2510 {
2511 AssertIndexRange(comp, 4);
2512 return *(reinterpret_cast<const double *>(&data) + comp);
2513 }
2514
2519 operator+=(const VectorizedArray &vec)
2520 {
2521 // if the compiler supports vector arithmetic, we can simply use +=
2522 // operator on the given data type. this allows the compiler to combine
2523 // additions with multiplication (fused multiply-add) if those
2524 // instructions are available. Otherwise, we need to use the built-in
2525 // intrinsic command for __m256d
2526# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2527 data += vec.data;
2528# else
2529 data = _mm256_add_pd(data, vec.data);
2530# endif
2531 return *this;
2532 }
2533
2538 operator-=(const VectorizedArray &vec)
2539 {
2540# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2541 data -= vec.data;
2542# else
2543 data = _mm256_sub_pd(data, vec.data);
2544# endif
2545 return *this;
2546 }
2551 operator*=(const VectorizedArray &vec)
2552 {
2553# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2554 data *= vec.data;
2555# else
2556 data = _mm256_mul_pd(data, vec.data);
2557# endif
2558 return *this;
2559 }
2560
2565 operator/=(const VectorizedArray &vec)
2566 {
2567# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
2568 data /= vec.data;
2569# else
2570 data = _mm256_div_pd(data, vec.data);
2571# endif
2572 return *this;
2573 }
2574
2580 DEAL_II_ALWAYS_INLINE inline void
2581 load(const double *ptr)
2582 {
2583 data = _mm256_loadu_pd(ptr);
2584 }
2585
2586 DEAL_II_ALWAYS_INLINE inline void
2587 load(const float *ptr)
2588 {
2589 data = _mm256_cvtps_pd(_mm_loadu_ps(ptr));
2590 }
2591
2598 DEAL_II_ALWAYS_INLINE inline void
2599 store(double *ptr) const
2600 {
2601 _mm256_storeu_pd(ptr, data);
2602 }
2603
2604 DEAL_II_ALWAYS_INLINE inline void
2605 store(float *ptr) const
2606 {
2607 _mm_storeu_ps(ptr, _mm256_cvtpd_ps(data));
2608 }
2609
2614 DEAL_II_ALWAYS_INLINE inline void
2615 streaming_store(double *ptr) const
2616 {
2617 Assert(reinterpret_cast<std::size_t>(ptr) % 32 == 0,
2618 ExcMessage("Memory not aligned"));
2619 _mm256_stream_pd(ptr, data);
2620 }
2621
2625 DEAL_II_ALWAYS_INLINE inline void
2626 gather(const double *base_ptr, const unsigned int *offsets)
2627 {
2628# if defined(__AVX2__) && defined(DEAL_II_USE_VECTORIZATION_GATHER)
2629 // unfortunately, there does not appear to be a 128 bit integer load, so
2630 // do it by some reinterpret casts here. this is allowed because the Intel
2631 // API allows aliasing between different vector types.
2632 const __m128 index_val =
2633 _mm_loadu_ps(reinterpret_cast<const float *>(offsets));
2634 const __m128i index = *reinterpret_cast<const __m128i *>(&index_val);
2635
2636 // work around a warning with gcc-12 about an uninitialized initial state
2637 // for gather by starting with a zero guess, even though all lanes will be
2638 // overwritten
2639 __m256d zero = _mm256_setzero_pd();
2640 __m128i neq32 = _mm_andnot_si128(_mm_cmpeq_epi32(index, _mm_set1_epi32(-1)),
2641 _mm_set1_epi32(-1));
2642
2643 __m256d mask = _mm256_castsi256_pd(_mm256_cvtepi32_epi64(neq32));
2644
2645 data = _mm256_mask_i32gather_pd(zero, base_ptr, index, mask, 8);
2646# else
2647 this->operator=(0.);
2648 for (unsigned int i = 0; i < 4; ++i)
2649 if (offsets[i] != numbers::invalid_unsigned_int)
2650 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
2651# endif
2652 }
2653
2657 DEAL_II_ALWAYS_INLINE inline void
2658 scatter(const unsigned int *offsets, double *base_ptr) const
2659 {
2660 // no scatter operation in AVX/AVX2
2661 for (unsigned int i = 0; i < 4; ++i)
2662 if (offsets[i] != numbers::invalid_unsigned_int)
2663 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
2664 }
2665
2670 double
2671 sum() const
2672 {
2674 t1.data = _mm_add_pd(this->get_lower(), this->get_upper());
2675 return t1.sum();
2676 }
2677
2682 get_floor() const
2683 {
2684 VectorizedArray res;
2685 res.data = _mm256_floor_pd(data);
2686 return res;
2687 }
2688
2694 __m256d data;
2695
2696private:
2700 DEAL_II_ALWAYS_INLINE inline __m128d
2701 get_lower() const
2702 {
2703 return _mm256_castpd256_pd128(data);
2704 }
2705
2709 DEAL_II_ALWAYS_INLINE inline __m128d
2710 get_upper() const
2711 {
2712 return _mm256_extractf128_pd(data, 1);
2713 }
2714
2720 get_sqrt() const
2721 {
2722 VectorizedArray res;
2723 res.data = _mm256_sqrt_pd(data);
2724 return res;
2725 }
2726
2732 get_abs() const
2733 {
2734 // to compute the absolute value, perform bitwise andnot with -0. This
2735 // will leave all value and exponent bits unchanged but force the sign
2736 // value to +.
2737 __m256d mask = _mm256_set1_pd(-0.);
2738 VectorizedArray res;
2739 res.data = _mm256_andnot_pd(mask, data);
2740 return res;
2741 }
2742
2748 get_max(const VectorizedArray &other) const
2749 {
2750 VectorizedArray res;
2751 res.data = _mm256_max_pd(data, other.data);
2752 return res;
2753 }
2754
2760 get_min(const VectorizedArray &other) const
2761 {
2762 VectorizedArray res;
2763 res.data = _mm256_min_pd(data, other.data);
2764 return res;
2765 }
2766
2767 // Make a few functions friends.
2768 template <typename Number2, std::size_t width2>
2771 template <typename Number2, std::size_t width2>
2774 template <typename Number2, std::size_t width2>
2778 template <typename Number2, std::size_t width2>
2782};
2783
2784
2785
2789template <>
2790DEAL_II_ALWAYS_INLINE inline void
2791vectorized_load_and_transpose(const unsigned int n_entries,
2792 const std::array<const double *, 4> &in,
2794{
2795 const unsigned int n_chunks = n_entries / 4;
2796 const double *in0 = in[0];
2797 const double *in1 = in[1];
2798 const double *in2 = in[2];
2799 const double *in3 = in[3];
2800
2801 for (unsigned int i = 0; i < n_chunks; ++i)
2802 {
2803 __m256d u0 = _mm256_loadu_pd(in0 + 4 * i);
2804 __m256d u1 = _mm256_loadu_pd(in1 + 4 * i);
2805 __m256d u2 = _mm256_loadu_pd(in2 + 4 * i);
2806 __m256d u3 = _mm256_loadu_pd(in3 + 4 * i);
2807 __m256d t0 = _mm256_permute2f128_pd(u0, u2, 0x20);
2808 __m256d t1 = _mm256_permute2f128_pd(u1, u3, 0x20);
2809 __m256d t2 = _mm256_permute2f128_pd(u0, u2, 0x31);
2810 __m256d t3 = _mm256_permute2f128_pd(u1, u3, 0x31);
2811 out[4 * i + 0].data = _mm256_unpacklo_pd(t0, t1);
2812 out[4 * i + 1].data = _mm256_unpackhi_pd(t0, t1);
2813 out[4 * i + 2].data = _mm256_unpacklo_pd(t2, t3);
2814 out[4 * i + 3].data = _mm256_unpackhi_pd(t2, t3);
2815 }
2816
2817 // Specialized remainder code
2818 const unsigned int i = 4 * n_chunks;
2819 const unsigned int remainder = n_entries - i;
2820 if (remainder == 3)
2821 {
2822 // Use masked load of 3 doubles (this avoids accessing in[x][3], which
2823 // might be an invalid address)
2824 __m256i mask{-1, -1, -1, 0};
2825 __m256d u0 = _mm256_maskload_pd(in[0] + i, mask);
2826 __m256d u1 = _mm256_maskload_pd(in[1] + i, mask);
2827 __m256d u2 = _mm256_maskload_pd(in[2] + i, mask);
2828 __m256d u3 = _mm256_maskload_pd(in[3] + i, mask);
2829 __m256d t0 = _mm256_permute2f128_pd(u0, u2, 0x20);
2830 __m256d t1 = _mm256_permute2f128_pd(u1, u3, 0x20);
2831 __m256d t2 = _mm256_permute2f128_pd(u0, u2, 0x31);
2832 __m256d t3 = _mm256_permute2f128_pd(u1, u3, 0x31);
2833 out[i + 0].data = _mm256_unpacklo_pd(t0, t1);
2834 out[i + 1].data = _mm256_unpackhi_pd(t0, t1);
2835 out[i + 2].data = _mm256_unpacklo_pd(t2, t3);
2836 }
2837 else if (remainder == 2)
2838 {
2839 // Use 128 bit loads
2840 __m256d t0, t1 = {};
2841 t0 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[0] + i), 0);
2842 t0 = _mm256_insertf128_pd(t0, _mm_loadu_pd(in[2] + i), 1);
2843 t1 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[1] + i), 0);
2844 t1 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[3] + i), 1);
2845
2846 out[i + 0].data = _mm256_unpacklo_pd(t0, t1);
2847 out[i + 1].data = _mm256_unpackhi_pd(t0, t1);
2848 }
2849 else if (remainder == 1)
2850 for (unsigned int v = 0; v < 4; ++v)
2851 out[i][v] = in[v][i];
2852}
2853
2854
2855
2859template <>
2860DEAL_II_ALWAYS_INLINE inline void
2861vectorized_load_and_transpose(const unsigned int n_entries,
2862 const double *in,
2863 const unsigned int *offsets,
2865{
2867 n_entries,
2868 {{in + offsets[0], in + offsets[1], in + offsets[2], in + offsets[3]}},
2869 out);
2870}
2871
2872
2873
2877template <>
2878DEAL_II_ALWAYS_INLINE inline void
2879vectorized_transpose_and_store(const bool add_into,
2880 const unsigned int n_entries,
2882 const std::array<double *, 4> &out)
2883{
2884 const unsigned int n_chunks = n_entries / 4;
2885 double *out0 = out[0];
2886 double *out1 = out[1];
2887 double *out2 = out[2];
2888 double *out3 = out[3];
2889 for (unsigned int i = 0; i < n_chunks; ++i)
2890 {
2891 __m256d u0 = in[4 * i + 0].data;
2892 __m256d u1 = in[4 * i + 1].data;
2893 __m256d u2 = in[4 * i + 2].data;
2894 __m256d u3 = in[4 * i + 3].data;
2895 __m256d t0 = _mm256_permute2f128_pd(u0, u2, 0x20);
2896 __m256d t1 = _mm256_permute2f128_pd(u1, u3, 0x20);
2897 __m256d t2 = _mm256_permute2f128_pd(u0, u2, 0x31);
2898 __m256d t3 = _mm256_permute2f128_pd(u1, u3, 0x31);
2899 __m256d res0 = _mm256_unpacklo_pd(t0, t1);
2900 __m256d res1 = _mm256_unpackhi_pd(t0, t1);
2901 __m256d res2 = _mm256_unpacklo_pd(t2, t3);
2902 __m256d res3 = _mm256_unpackhi_pd(t2, t3);
2903
2904 // Cannot use the same store instructions in both paths of the 'if'
2905 // because the compiler cannot know that there is no aliasing between
2906 // pointers
2907 if (add_into)
2908 {
2909 res0 = _mm256_add_pd(_mm256_loadu_pd(out0 + 4 * i), res0);
2910 _mm256_storeu_pd(out0 + 4 * i, res0);
2911 res1 = _mm256_add_pd(_mm256_loadu_pd(out1 + 4 * i), res1);
2912 _mm256_storeu_pd(out1 + 4 * i, res1);
2913 res2 = _mm256_add_pd(_mm256_loadu_pd(out2 + 4 * i), res2);
2914 _mm256_storeu_pd(out2 + 4 * i, res2);
2915 res3 = _mm256_add_pd(_mm256_loadu_pd(out3 + 4 * i), res3);
2916 _mm256_storeu_pd(out3 + 4 * i, res3);
2917 }
2918 else
2919 {
2920 _mm256_storeu_pd(out0 + 4 * i, res0);
2921 _mm256_storeu_pd(out1 + 4 * i, res1);
2922 _mm256_storeu_pd(out2 + 4 * i, res2);
2923 _mm256_storeu_pd(out3 + 4 * i, res3);
2924 }
2925 }
2926
2927 // remainder loop of work that does not divide by 4
2928 if (add_into)
2929 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2930 for (unsigned int v = 0; v < 4; ++v)
2931 out[v][i] += in[i][v];
2932 else
2933 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
2934 for (unsigned int v = 0; v < 4; ++v)
2935 out[v][i] = in[i][v];
2936}
2937
2938
2939
2943template <>
2944DEAL_II_ALWAYS_INLINE inline void
2945vectorized_transpose_and_store(const bool add_into,
2946 const unsigned int n_entries,
2948 const unsigned int *offsets,
2949 double *out)
2950{
2952 add_into,
2953 n_entries,
2954 in,
2955 {{out + offsets[0], out + offsets[1], out + offsets[2], out + offsets[3]}});
2956}
2957
2958
2959
2963template <>
2964class VectorizedArray<float, 8>
2965 : public VectorizedArrayBase<VectorizedArray<float, 8>, 8>
2966{
2967public:
2971 using value_type = float;
2972
2977 static constexpr bool is_implemented = true;
2978
2983 VectorizedArray() = default;
2984
2988 VectorizedArray(const float scalar)
2989 {
2990 this->operator=(scalar);
2991 }
2992
2996 template <typename U>
2997 VectorizedArray(const std::initializer_list<U> &list)
2998 : VectorizedArrayBase<VectorizedArray<float, 8>, 8>(list)
2999 {}
3000
3005 operator=(const float x) &
3006 {
3007 data = _mm256_set1_ps(x);
3008 return *this;
3009 }
3010
3017 operator=(const float scalar) && = delete;
3018
3022 DEAL_II_ALWAYS_INLINE inline float &
3023 operator[](const unsigned int comp)
3024 {
3025 AssertIndexRange(comp, 8);
3026 return *(reinterpret_cast<float *>(&data) + comp);
3027 }
3028
3032 DEAL_II_ALWAYS_INLINE inline const float &
3033 operator[](const unsigned int comp) const
3034 {
3035 AssertIndexRange(comp, 8);
3036 return *(reinterpret_cast<const float *>(&data) + comp);
3037 }
3038
3043 operator+=(const VectorizedArray &vec)
3044 {
3045 // if the compiler supports vector arithmetic, we can simply use +=
3046 // operator on the given data type. this allows the compiler to combine
3047 // additions with multiplication (fused multiply-add) if those
3048 // instructions are available. Otherwise, we need to use the built-in
3049 // intrinsic command for __m256d
3050# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3051 data += vec.data;
3052# else
3053 data = _mm256_add_ps(data, vec.data);
3054# endif
3055 return *this;
3056 }
3057
3062 operator-=(const VectorizedArray &vec)
3063 {
3064# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3065 data -= vec.data;
3066# else
3067 data = _mm256_sub_ps(data, vec.data);
3068# endif
3069 return *this;
3070 }
3075 operator*=(const VectorizedArray &vec)
3076 {
3077# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3078 data *= vec.data;
3079# else
3080 data = _mm256_mul_ps(data, vec.data);
3081# endif
3082 return *this;
3083 }
3084
3089 operator/=(const VectorizedArray &vec)
3090 {
3091# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3092 data /= vec.data;
3093# else
3094 data = _mm256_div_ps(data, vec.data);
3095# endif
3096 return *this;
3097 }
3098
3104 DEAL_II_ALWAYS_INLINE inline void
3105 load(const float *ptr)
3106 {
3107 data = _mm256_loadu_ps(ptr);
3108 }
3109
3116 DEAL_II_ALWAYS_INLINE inline void
3117 store(float *ptr) const
3118 {
3119 _mm256_storeu_ps(ptr, data);
3120 }
3121
3126 DEAL_II_ALWAYS_INLINE inline void
3127 streaming_store(float *ptr) const
3128 {
3129 Assert(reinterpret_cast<std::size_t>(ptr) % 32 == 0,
3130 ExcMessage("Memory not aligned"));
3131 _mm256_stream_ps(ptr, data);
3132 }
3133
3137 DEAL_II_ALWAYS_INLINE inline void
3138 gather(const float *base_ptr, const unsigned int *offsets)
3139 {
3140# if defined(__AVX2__) && defined(DEAL_II_USE_VECTORIZATION_GATHER)
3141 // unfortunately, there does not appear to be a 256 bit integer load, so
3142 // do it by some reinterpret casts here. this is allowed because the Intel
3143 // API allows aliasing between different vector types.
3144 const __m256 index_val =
3145 _mm256_loadu_ps(reinterpret_cast<const float *>(offsets));
3146 const __m256i index = *reinterpret_cast<const __m256i *>(&index_val);
3147
3148 // work around a warning with gcc-12 about an uninitialized initial state
3149 // for gather by starting with a zero guess, even though all lanes will be
3150 // overwritten
3151 __m256 zero = _mm256_setzero_ps();
3152 __m256i invalid = _mm256_set1_epi32(numbers::invalid_unsigned_int);
3153 __m256i inverse_mask = _mm256_cmpeq_epi32(index, invalid);
3154 __m256i mask = _mm256_xor_si256(invalid, inverse_mask);
3155
3156 data = _mm256_mask_i32gather_ps(
3157 zero, base_ptr, index, *reinterpret_cast<__m256 *>(&mask), 4);
3158# else
3159 this->operator=(0.f);
3160 for (unsigned int i = 0; i < 8; ++i)
3161 if (offsets[i] != numbers::invalid_unsigned_int)
3162 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
3163# endif
3164 }
3165
3169 DEAL_II_ALWAYS_INLINE inline void
3170 scatter(const unsigned int *offsets, float *base_ptr) const
3171 {
3172 // no scatter operation in AVX/AVX2
3173 for (unsigned int i = 0; i < 8; ++i)
3174 if (offsets[i] != numbers::invalid_unsigned_int)
3175 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
3176 }
3177
3182 float
3183 sum() const
3184 {
3186 t1.data = _mm_add_ps(this->get_lower(), this->get_upper());
3187 return t1.sum();
3188 }
3189
3194 get_floor() const
3195 {
3196 VectorizedArray res;
3197 res.data = _mm256_floor_ps(data);
3198 return res;
3199 }
3200
3206 __m256 data;
3207
3208private:
3212 DEAL_II_ALWAYS_INLINE inline __m128
3213 get_lower() const
3214 {
3215 return _mm256_castps256_ps128(data);
3216 }
3217
3221 DEAL_II_ALWAYS_INLINE inline __m128
3222 get_upper() const
3223 {
3224 return _mm256_extractf128_ps(data, 1);
3225 }
3226
3232 get_sqrt() const
3233 {
3234 VectorizedArray res;
3235 res.data = _mm256_sqrt_ps(data);
3236 return res;
3237 }
3238
3244 get_abs() const
3245 {
3246 // to compute the absolute value, perform bitwise andnot with -0. This
3247 // will leave all value and exponent bits unchanged but force the sign
3248 // value to +.
3249 __m256 mask = _mm256_set1_ps(-0.f);
3250 VectorizedArray res;
3251 res.data = _mm256_andnot_ps(mask, data);
3252 return res;
3253 }
3254
3260 get_max(const VectorizedArray &other) const
3261 {
3262 VectorizedArray res;
3263 res.data = _mm256_max_ps(data, other.data);
3264 return res;
3265 }
3266
3272 get_min(const VectorizedArray &other) const
3273 {
3274 VectorizedArray res;
3275 res.data = _mm256_min_ps(data, other.data);
3276 return res;
3277 }
3278
3279 // Make a few functions friends.
3280 template <typename Number2, std::size_t width2>
3283 template <typename Number2, std::size_t width2>
3286 template <typename Number2, std::size_t width2>
3290 template <typename Number2, std::size_t width2>
3294};
3295
3296
3297
3301template <>
3302DEAL_II_ALWAYS_INLINE inline void
3303vectorized_load_and_transpose(const unsigned int n_entries,
3304 const std::array<const float *, 8> &in,
3306{
3307 const unsigned int n_chunks = n_entries / 4;
3308 for (unsigned int i = 0; i < n_chunks; ++i)
3309 {
3310 // To avoid warnings about uninitialized variables, need to initialize
3311 // one variable with zero before using it.
3312 __m256 t0, t1, t2, t3 = {};
3313 t0 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[0] + 4 * i), 0);
3314 t0 = _mm256_insertf128_ps(t0, _mm_loadu_ps(in[4] + 4 * i), 1);
3315 t1 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[1] + 4 * i), 0);
3316 t1 = _mm256_insertf128_ps(t1, _mm_loadu_ps(in[5] + 4 * i), 1);
3317 t2 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[2] + 4 * i), 0);
3318 t2 = _mm256_insertf128_ps(t2, _mm_loadu_ps(in[6] + 4 * i), 1);
3319 t3 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[3] + 4 * i), 0);
3320 t3 = _mm256_insertf128_ps(t3, _mm_loadu_ps(in[7] + 4 * i), 1);
3321
3322 __m256 v0 = _mm256_shuffle_ps(t0, t1, 0x44);
3323 __m256 v1 = _mm256_shuffle_ps(t0, t1, 0xee);
3324 __m256 v2 = _mm256_shuffle_ps(t2, t3, 0x44);
3325 __m256 v3 = _mm256_shuffle_ps(t2, t3, 0xee);
3326 out[4 * i + 0].data = _mm256_shuffle_ps(v0, v2, 0x88);
3327 out[4 * i + 1].data = _mm256_shuffle_ps(v0, v2, 0xdd);
3328 out[4 * i + 2].data = _mm256_shuffle_ps(v1, v3, 0x88);
3329 out[4 * i + 3].data = _mm256_shuffle_ps(v1, v3, 0xdd);
3330 }
3331
3332 // Specialized remainder code
3333 const unsigned int i = 4 * n_chunks;
3334 const unsigned int remainder = n_entries - i;
3335 if (remainder == 3)
3336 {
3337 // Use masked load of 3 floats (this avoids accessing in[x][3], which
3338 // might be an invalid address)
3339 __m128i mask = _mm_setr_epi32(-1, -1, -1, 0);
3340 __m256 t0, t1, t2, t3 = {};
3341 t0 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[0] + i, mask), 0);
3342 t0 = _mm256_insertf128_ps(t0, _mm_maskload_ps(in[4] + i, mask), 1);
3343 t1 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[1] + i, mask), 0);
3344 t1 = _mm256_insertf128_ps(t1, _mm_maskload_ps(in[5] + i, mask), 1);
3345 t2 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[2] + i, mask), 0);
3346 t2 = _mm256_insertf128_ps(t2, _mm_maskload_ps(in[6] + i, mask), 1);
3347 t3 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[3] + i, mask), 0);
3348 t3 = _mm256_insertf128_ps(t3, _mm_maskload_ps(in[7] + i, mask), 1);
3349 __m256 v0 = _mm256_shuffle_ps(t0, t1, 0x44);
3350 __m256 v1 = _mm256_shuffle_ps(t0, t1, 0xee);
3351 __m256 v2 = _mm256_shuffle_ps(t2, t3, 0x44);
3352 __m256 v3 = _mm256_shuffle_ps(t2, t3, 0xee);
3353 out[i + 0].data = _mm256_shuffle_ps(v0, v2, 0x88);
3354 out[i + 1].data = _mm256_shuffle_ps(v0, v2, 0xdd);
3355 out[i + 2].data = _mm256_shuffle_ps(v1, v3, 0x88);
3356 }
3357 else if (remainder == 2)
3358 {
3359 // Use 64-bit integer loads
3360 __m128i t0, t1;
3361 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[0] + i));
3362 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[1] + i));
3363 __m256i v0 = _mm256_castsi128_si256(_mm_unpacklo_epi64(t0, t1));
3364 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[4] + i));
3365 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[5] + i));
3366 v0 = _mm256_inserti128_si256(v0, _mm_unpacklo_epi64(t0, t1), 1);
3367 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[2] + i));
3368 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[3] + i));
3369 __m256i v1 = _mm256_castsi128_si256(_mm_unpacklo_epi64(t0, t1));
3370 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[6] + i));
3371 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[7] + i));
3372 v1 = _mm256_inserti128_si256(v1, _mm_unpacklo_epi64(t0, t1), 1);
3373 out[i + 0].data = _mm256_shuffle_ps(_mm256_castsi256_ps(v0),
3374 _mm256_castsi256_ps(v1),
3375 0x88);
3376 out[i + 1].data = _mm256_shuffle_ps(_mm256_castsi256_ps(v0),
3377 _mm256_castsi256_ps(v1),
3378 0xdd);
3379 }
3380 else if (remainder == 1)
3381 for (unsigned int v = 0; v < 8; ++v)
3382 out[i][v] = in[v][i];
3383}
3384
3385
3386
3390template <>
3391DEAL_II_ALWAYS_INLINE inline void
3392vectorized_load_and_transpose(const unsigned int n_entries,
3393 const float *in,
3394 const unsigned int *offsets,
3396{
3398 {{in + offsets[0],
3399 in + offsets[1],
3400 in + offsets[2],
3401 in + offsets[3],
3402 in + offsets[4],
3403 in + offsets[5],
3404 in + offsets[6],
3405 in + offsets[7]}},
3406 out);
3407}
3408
3409
3410
3414template <>
3415DEAL_II_ALWAYS_INLINE inline void
3416vectorized_transpose_and_store(const bool add_into,
3417 const unsigned int n_entries,
3418 const VectorizedArray<float, 8> *in,
3419 const std::array<float *, 8> &out)
3420{
3421 const unsigned int n_chunks = n_entries / 4;
3422 for (unsigned int i = 0; i < n_chunks; ++i)
3423 {
3424 __m256 u0 = in[4 * i + 0].data;
3425 __m256 u1 = in[4 * i + 1].data;
3426 __m256 u2 = in[4 * i + 2].data;
3427 __m256 u3 = in[4 * i + 3].data;
3428 __m256 t0 = _mm256_shuffle_ps(u0, u1, 0x44);
3429 __m256 t1 = _mm256_shuffle_ps(u0, u1, 0xee);
3430 __m256 t2 = _mm256_shuffle_ps(u2, u3, 0x44);
3431 __m256 t3 = _mm256_shuffle_ps(u2, u3, 0xee);
3432 u0 = _mm256_shuffle_ps(t0, t2, 0x88);
3433 u1 = _mm256_shuffle_ps(t0, t2, 0xdd);
3434 u2 = _mm256_shuffle_ps(t1, t3, 0x88);
3435 u3 = _mm256_shuffle_ps(t1, t3, 0xdd);
3436 __m128 res0 = _mm256_extractf128_ps(u0, 0);
3437 __m128 res4 = _mm256_extractf128_ps(u0, 1);
3438 __m128 res1 = _mm256_extractf128_ps(u1, 0);
3439 __m128 res5 = _mm256_extractf128_ps(u1, 1);
3440 __m128 res2 = _mm256_extractf128_ps(u2, 0);
3441 __m128 res6 = _mm256_extractf128_ps(u2, 1);
3442 __m128 res3 = _mm256_extractf128_ps(u3, 0);
3443 __m128 res7 = _mm256_extractf128_ps(u3, 1);
3444
3445 // Cannot use the same store instructions in both paths of the 'if'
3446 // because the compiler cannot know that there is no aliasing between
3447 // pointers
3448 if (add_into)
3449 {
3450 res0 = _mm_add_ps(_mm_loadu_ps(out[0] + 4 * i), res0);
3451 _mm_storeu_ps(out[0] + 4 * i, res0);
3452 res1 = _mm_add_ps(_mm_loadu_ps(out[1] + 4 * i), res1);
3453 _mm_storeu_ps(out[1] + 4 * i, res1);
3454 res2 = _mm_add_ps(_mm_loadu_ps(out[2] + 4 * i), res2);
3455 _mm_storeu_ps(out[2] + 4 * i, res2);
3456 res3 = _mm_add_ps(_mm_loadu_ps(out[3] + 4 * i), res3);
3457 _mm_storeu_ps(out[3] + 4 * i, res3);
3458 res4 = _mm_add_ps(_mm_loadu_ps(out[4] + 4 * i), res4);
3459 _mm_storeu_ps(out[4] + 4 * i, res4);
3460 res5 = _mm_add_ps(_mm_loadu_ps(out[5] + 4 * i), res5);
3461 _mm_storeu_ps(out[5] + 4 * i, res5);
3462 res6 = _mm_add_ps(_mm_loadu_ps(out[6] + 4 * i), res6);
3463 _mm_storeu_ps(out[6] + 4 * i, res6);
3464 res7 = _mm_add_ps(_mm_loadu_ps(out[7] + 4 * i), res7);
3465 _mm_storeu_ps(out[7] + 4 * i, res7);
3466 }
3467 else
3468 {
3469 _mm_storeu_ps(out[0] + 4 * i, res0);
3470 _mm_storeu_ps(out[1] + 4 * i, res1);
3471 _mm_storeu_ps(out[2] + 4 * i, res2);
3472 _mm_storeu_ps(out[3] + 4 * i, res3);
3473 _mm_storeu_ps(out[4] + 4 * i, res4);
3474 _mm_storeu_ps(out[5] + 4 * i, res5);
3475 _mm_storeu_ps(out[6] + 4 * i, res6);
3476 _mm_storeu_ps(out[7] + 4 * i, res7);
3477 }
3478 }
3479
3480 if (add_into)
3481 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
3482 for (unsigned int v = 0; v < 8; ++v)
3483 out[v][i] += in[i][v];
3484 else
3485 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
3486 for (unsigned int v = 0; v < 8; ++v)
3487 out[v][i] = in[i][v];
3488}
3489
3490
3491
3495template <>
3496DEAL_II_ALWAYS_INLINE inline void
3497vectorized_transpose_and_store(const bool add_into,
3498 const unsigned int n_entries,
3499 const VectorizedArray<float, 8> *in,
3500 const unsigned int *offsets,
3501 float *out)
3502{
3504 n_entries,
3505 in,
3506 {{out + offsets[0],
3507 out + offsets[1],
3508 out + offsets[2],
3509 out + offsets[3],
3510 out + offsets[4],
3511 out + offsets[5],
3512 out + offsets[6],
3513 out + offsets[7]}});
3514}
3515
3516# endif
3517
3518// for safety, also check that __AVX512F__ is defined in case the user manually
3519// set some conflicting compile flags which prevent compilation
3520
3521# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 512 && defined(__AVX512F__)
3522
3526template <>
3527class VectorizedArray<double, 8>
3528 : public VectorizedArrayBase<VectorizedArray<double, 8>, 8>
3529{
3530public:
3534 using value_type = double;
3535
3540 static constexpr bool is_implemented = true;
3541
3546 VectorizedArray() = default;
3547
3551 VectorizedArray(const double scalar)
3552 {
3553 this->operator=(scalar);
3554 }
3555
3559 template <typename U>
3560 VectorizedArray(const std::initializer_list<U> &list)
3561 : VectorizedArrayBase<VectorizedArray<double, 8>, 8>(list)
3562 {}
3563
3568 operator=(const double x) &
3569 {
3570 data = _mm512_set1_pd(x);
3571 return *this;
3572 }
3573
3574
3581 operator=(const double scalar) && = delete;
3582
3586 DEAL_II_ALWAYS_INLINE inline double &
3587 operator[](const unsigned int comp)
3588 {
3589 AssertIndexRange(comp, 8);
3590 return *(reinterpret_cast<double *>(&data) + comp);
3591 }
3592
3596 DEAL_II_ALWAYS_INLINE inline const double &
3597 operator[](const unsigned int comp) const
3598 {
3599 AssertIndexRange(comp, 8);
3600 return *(reinterpret_cast<const double *>(&data) + comp);
3601 }
3602
3607 operator+=(const VectorizedArray &vec)
3608 {
3609 // if the compiler supports vector arithmetic, we can simply use +=
3610 // operator on the given data type. this allows the compiler to combine
3611 // additions with multiplication (fused multiply-add) if those
3612 // instructions are available. Otherwise, we need to use the built-in
3613 // intrinsic command for __m512d
3614# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3615 data += vec.data;
3616# else
3617 data = _mm512_add_pd(data, vec.data);
3618# endif
3619 return *this;
3620 }
3621
3626 operator-=(const VectorizedArray &vec)
3627 {
3628# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3629 data -= vec.data;
3630# else
3631 data = _mm512_sub_pd(data, vec.data);
3632# endif
3633 return *this;
3634 }
3639 operator*=(const VectorizedArray &vec)
3640 {
3641# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3642 data *= vec.data;
3643# else
3644 data = _mm512_mul_pd(data, vec.data);
3645# endif
3646 return *this;
3647 }
3648
3653 operator/=(const VectorizedArray &vec)
3654 {
3655# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
3656 data /= vec.data;
3657# else
3658 data = _mm512_div_pd(data, vec.data);
3659# endif
3660 return *this;
3661 }
3662
3668 DEAL_II_ALWAYS_INLINE inline void
3669 load(const double *ptr)
3670 {
3671 data = _mm512_loadu_pd(ptr);
3672 }
3673
3674 DEAL_II_ALWAYS_INLINE inline void
3675 load(const float *ptr)
3676 {
3677 data = _mm512_cvtps_pd(_mm256_loadu_ps(ptr));
3678 }
3679
3686 DEAL_II_ALWAYS_INLINE inline void
3687 store(double *ptr) const
3688 {
3689 _mm512_storeu_pd(ptr, data);
3690 }
3691
3692 DEAL_II_ALWAYS_INLINE inline void
3693 store(float *ptr) const
3694 {
3695 _mm256_storeu_ps(ptr, _mm512_cvtpd_ps(data));
3696 }
3697
3702 DEAL_II_ALWAYS_INLINE inline void
3703 streaming_store(double *ptr) const
3704 {
3705 Assert(reinterpret_cast<std::size_t>(ptr) % 64 == 0,
3706 ExcMessage("Memory not aligned"));
3707 _mm512_stream_pd(ptr, data);
3708 }
3709
3713 DEAL_II_ALWAYS_INLINE inline void
3714 gather(const double *base_ptr, const unsigned int *offsets)
3715 {
3716# ifdef DEAL_II_USE_VECTORIZATION_GATHER
3717 // unfortunately, there does not appear to be a 256 bit integer load, so
3718 // do it by some reinterpret casts here. this is allowed because the Intel
3719 // API allows aliasing between different vector types.
3720 const __m256 index_val =
3721 _mm256_loadu_ps(reinterpret_cast<const float *>(offsets));
3722 const __m256i index = *reinterpret_cast<const __m256i *>(&index_val);
3723
3724 // work around a warning with gcc-12 about an uninitialized initial state
3725 // for gather by starting with a zero guess, even though all lanes will be
3726 // overwritten
3727 __m512d zero = {};
3728 const __m256i invalid = _mm256_set1_epi32(numbers::invalid_unsigned_int);
3729 const __m256i equal = _mm256_cmpeq_epi32(invalid, index);
3730 __mmask8 mask =
3731 static_cast<__mmask8>(~(_mm256_movemask_ps(_mm256_castsi256_ps(equal))));
3732
3733 data = _mm512_mask_i32gather_pd(zero, mask, index, base_ptr, 8);
3734# else
3735 this->operator=(0.);
3736 for (unsigned int i = 0; i < 8; ++i)
3737 if (offsets[i] != numbers::invalid_unsigned_int)
3738 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
3739# endif
3740 }
3741
3745 DEAL_II_ALWAYS_INLINE inline void
3746 scatter(const unsigned int *offsets, double *base_ptr) const
3747 {
3748# ifdef DEAL_II_USE_VECTORIZATION_GATHER
3749 for (unsigned int i = 0; i < 8; ++i)
3750 for (unsigned int j = i + 1; j < 8; ++j)
3751 Assert(offsets[i] == numbers::invalid_unsigned_int ||
3752 offsets[i] != offsets[j],
3753 ExcMessage("Result of scatter undefined if two offset elements"
3754 " point to the same position"));
3755
3756 // unfortunately, there does not appear to be a 256 bit integer load, so
3757 // do it by some reinterpret casts here. this is allowed because the Intel
3758 // API allows aliasing between different vector types.
3759 const __m256 index_val =
3760 _mm256_loadu_ps(reinterpret_cast<const float *>(offsets));
3761 const __m256i index = *reinterpret_cast<const __m256i *>(&index_val);
3762 const __m256i invalid = _mm256_set1_epi32(numbers::invalid_unsigned_int);
3763 const __m256i equal = _mm256_cmpeq_epi32(invalid, index);
3764 __mmask8 mask =
3765 static_cast<__mmask8>(~(_mm256_movemask_ps(_mm256_castsi256_ps(equal))));
3766 _mm512_mask_i32scatter_pd(base_ptr, mask, index, data, 8);
3767
3768# else
3769 for (unsigned int i = 0; i < 8; ++i)
3770 if (offsets[i] != numbers::invalid_unsigned_int)
3771 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
3772# endif
3773 }
3774
3779 double
3780 sum() const
3781 {
3783 t1.data = _mm256_add_pd(this->get_lower(), this->get_upper());
3784 return t1.sum();
3785 }
3786
3791 get_floor() const
3792 {
3793 VectorizedArray res;
3794 res.data = _mm512_roundscale_pd(data, _MM_FROUND_TO_NEG_INF);
3795 return res;
3796 }
3797
3803 __m512d data;
3804
3805private:
3809 DEAL_II_ALWAYS_INLINE inline __m256d
3810 get_lower() const
3811 {
3812 return _mm512_castpd512_pd256(data);
3813 }
3814
3818 DEAL_II_ALWAYS_INLINE inline __m256d
3819 get_upper() const
3820 {
3821 return _mm512_extractf64x4_pd(data, 1);
3822 }
3823
3829 get_sqrt() const
3830 {
3831 VectorizedArray res;
3832 res.data = _mm512_sqrt_pd(data);
3833 return res;
3834 }
3835
3841 get_abs() const
3842 {
3843 // to compute the absolute value, perform bitwise andnot with -0. This
3844 // will leave all value and exponent bits unchanged but force the sign
3845 // value to +. Since there is no andnot for AVX512, we interpret the data
3846 // as 64 bit integers and do the andnot on those types (note that andnot
3847 // is a bitwise operation so the data type does not matter)
3848 __m512d mask = _mm512_set1_pd(-0.);
3849 VectorizedArray res;
3850 res.data = reinterpret_cast<__m512d>(
3851 _mm512_andnot_epi64(reinterpret_cast<__m512i>(mask),
3852 reinterpret_cast<__m512i>(data)));
3853 return res;
3854 }
3855
3861 get_max(const VectorizedArray &other) const
3862 {
3863 VectorizedArray res;
3864 res.data = _mm512_max_pd(data, other.data);
3865 return res;
3866 }
3867
3873 get_min(const VectorizedArray &other) const
3874 {
3875 VectorizedArray res;
3876 res.data = _mm512_min_pd(data, other.data);
3877 return res;
3878 }
3879
3880 // Make a few functions friends.
3881 template <typename Number2, std::size_t width2>
3884 template <typename Number2, std::size_t width2>
3887 template <typename Number2, std::size_t width2>
3891 template <typename Number2, std::size_t width2>
3895};
3896
3897
3898
3902template <>
3903DEAL_II_ALWAYS_INLINE inline void
3904vectorized_load_and_transpose(const unsigned int n_entries,
3905 const std::array<const double *, 8> &in,
3907{
3908 // do not do full transpose because the code is long and will most
3909 // likely not pay off because many processors have two load units
3910 // (for the top 8 instructions) but only 1 permute unit (for the 8
3911 // shuffle/unpack instructions). rather start the transposition on the
3912 // vectorized array of half the size with 256 bits
3913 const unsigned int n_chunks = n_entries / 4;
3914 for (unsigned int i = 0; i < n_chunks; ++i)
3915 {
3916 __m512d t0, t1, t2, t3 = {};
3917
3918 t0 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[0] + 4 * i), 0);
3919 t0 = _mm512_insertf64x4(t0, _mm256_loadu_pd(in[2] + 4 * i), 1);
3920 t1 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[1] + 4 * i), 0);
3921 t1 = _mm512_insertf64x4(t1, _mm256_loadu_pd(in[3] + 4 * i), 1);
3922 t2 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[4] + 4 * i), 0);
3923 t2 = _mm512_insertf64x4(t2, _mm256_loadu_pd(in[6] + 4 * i), 1);
3924 t3 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[5] + 4 * i), 0);
3925 t3 = _mm512_insertf64x4(t3, _mm256_loadu_pd(in[7] + 4 * i), 1);
3926
3927 __m512d v0 = _mm512_shuffle_f64x2(t0, t2, 0x88);
3928 __m512d v1 = _mm512_shuffle_f64x2(t0, t2, 0xdd);
3929 __m512d v2 = _mm512_shuffle_f64x2(t1, t3, 0x88);
3930 __m512d v3 = _mm512_shuffle_f64x2(t1, t3, 0xdd);
3931 out[4 * i + 0].data = _mm512_unpacklo_pd(v0, v2);
3932 out[4 * i + 1].data = _mm512_unpackhi_pd(v0, v2);
3933 out[4 * i + 2].data = _mm512_unpacklo_pd(v1, v3);
3934 out[4 * i + 3].data = _mm512_unpackhi_pd(v1, v3);
3935 }
3936
3937 // Specialized remainder code
3938 const unsigned int i = 4 * n_chunks;
3939 const unsigned int remainder = n_entries - i;
3940 if (remainder == 3)
3941 {
3942 // Use masked loads to access the first 3 doubles along each pointer
3943 // without touching the possibly invalid addressed at in[x][3].
3944 __m256i mask{-1, -1, -1, 0};
3945 __m512d t0, t1, t2, t3 = {};
3946
3947 t0 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[0] + i, mask), 0);
3948 t0 = _mm512_insertf64x4(t0, _mm256_maskload_pd(in[2] + i, mask), 1);
3949 t1 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[1] + i, mask), 0);
3950 t1 = _mm512_insertf64x4(t1, _mm256_maskload_pd(in[3] + i, mask), 1);
3951 t2 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[4] + i, mask), 0);
3952 t2 = _mm512_insertf64x4(t2, _mm256_maskload_pd(in[6] + i, mask), 1);
3953 t3 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[5] + i, mask), 0);
3954 t3 = _mm512_insertf64x4(t3, _mm256_maskload_pd(in[7] + i, mask), 1);
3955
3956 __m512d v0 = _mm512_shuffle_f64x2(t0, t2, 0x88);
3957 __m512d v1 = _mm512_shuffle_f64x2(t0, t2, 0xdd);
3958 __m512d v2 = _mm512_shuffle_f64x2(t1, t3, 0x88);
3959 __m512d v3 = _mm512_shuffle_f64x2(t1, t3, 0xdd);
3960 out[i + 0].data = _mm512_unpacklo_pd(v0, v2);
3961 out[i + 1].data = _mm512_unpackhi_pd(v0, v2);
3962 out[i + 2].data = _mm512_unpacklo_pd(v1, v3);
3963 }
3964 else if (remainder == 2)
3965 {
3966 // Use 128-bit loads of 2 doubles
3967 __m256d t0, t1, t2, t3 = {};
3968 t0 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[0] + i), 0);
3969 t0 = _mm256_insertf128_pd(t0, _mm_loadu_pd(in[2] + i), 1);
3970 t1 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[1] + i), 0);
3971 t1 = _mm256_insertf128_pd(t1, _mm_loadu_pd(in[3] + i), 1);
3972 t2 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[4] + i), 0);
3973 t2 = _mm256_insertf128_pd(t2, _mm_loadu_pd(in[6] + i), 1);
3974 t3 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[5] + i), 0);
3975 t3 = _mm256_insertf128_pd(t3, _mm_loadu_pd(in[7] + i), 1);
3976
3977 __m512d v0, v1 = {};
3978 v0 = _mm512_insertf64x4(v1, t0, 0);
3979 v0 = _mm512_insertf64x4(v0, t2, 1);
3980 v1 = _mm512_insertf64x4(v1, t1, 0);
3981 v1 = _mm512_insertf64x4(v1, t3, 1);
3982 out[i + 0].data = _mm512_unpacklo_pd(v0, v1);
3983 out[i + 1].data = _mm512_unpackhi_pd(v0, v1);
3984 }
3985 else if (remainder == 1)
3986 for (unsigned int v = 0; v < 8; ++v)
3987 out[i][v] = in[v][i];
3988}
3989
3990
3991
3995template <>
3996DEAL_II_ALWAYS_INLINE inline void
3997vectorized_load_and_transpose(const unsigned int n_entries,
3998 const double *in,
3999 const unsigned int *offsets,
4001{
4003 {{in + offsets[0],
4004 in + offsets[1],
4005 in + offsets[2],
4006 in + offsets[3],
4007 in + offsets[4],
4008 in + offsets[5],
4009 in + offsets[6],
4010 in + offsets[7]}},
4011 out);
4012}
4013
4014
4015
4019template <>
4020DEAL_II_ALWAYS_INLINE inline void
4021vectorized_transpose_and_store(const bool add_into,
4022 const unsigned int n_entries,
4024 const std::array<double *, 8> &out)
4025{
4026 // as for the load, we split the store operations into 256 bit units to
4027 // better balance between code size, shuffle instructions, and stores
4028 const unsigned int n_chunks = n_entries / 4;
4029 __m512i mask1 = _mm512_set_epi64(0xd, 0xc, 0x5, 0x4, 0x9, 0x8, 0x1, 0x0);
4030 __m512i mask2 = _mm512_set_epi64(0xf, 0xe, 0x7, 0x6, 0xb, 0xa, 0x3, 0x2);
4031 for (unsigned int i = 0; i < n_chunks; ++i)
4032 {
4033 __m512d t0 = _mm512_unpacklo_pd(in[i * 4].data, in[i * 4 + 1].data);
4034 __m512d t1 = _mm512_unpackhi_pd(in[i * 4].data, in[i * 4 + 1].data);
4035 __m512d t2 = _mm512_unpacklo_pd(in[i * 4 + 2].data, in[i * 4 + 3].data);
4036 __m512d t3 = _mm512_unpackhi_pd(in[i * 4 + 2].data, in[i * 4 + 3].data);
4037 __m512d v0 = _mm512_permutex2var_pd(t0, mask1, t2);
4038 __m512d v1 = _mm512_permutex2var_pd(t0, mask2, t2);
4039 __m512d v2 = _mm512_permutex2var_pd(t1, mask1, t3);
4040 __m512d v3 = _mm512_permutex2var_pd(t1, mask2, t3);
4041 __m256d res0 = _mm512_extractf64x4_pd(v0, 0);
4042 __m256d res4 = _mm512_extractf64x4_pd(v0, 1);
4043 __m256d res1 = _mm512_extractf64x4_pd(v2, 0);
4044 __m256d res5 = _mm512_extractf64x4_pd(v2, 1);
4045 __m256d res2 = _mm512_extractf64x4_pd(v1, 0);
4046 __m256d res6 = _mm512_extractf64x4_pd(v1, 1);
4047 __m256d res3 = _mm512_extractf64x4_pd(v3, 0);
4048 __m256d res7 = _mm512_extractf64x4_pd(v3, 1);
4049
4050 // Cannot use the same store instructions in both paths of the 'if'
4051 // because the compiler cannot know that there is no aliasing
4052 // between pointers
4053 if (add_into)
4054 {
4055 res0 = _mm256_add_pd(_mm256_loadu_pd(out[0] + 4 * i), res0);
4056 _mm256_storeu_pd(out[0] + 4 * i, res0);
4057 res1 = _mm256_add_pd(_mm256_loadu_pd(out[1] + 4 * i), res1);
4058 _mm256_storeu_pd(out[1] + 4 * i, res1);
4059 res2 = _mm256_add_pd(_mm256_loadu_pd(out[2] + 4 * i), res2);
4060 _mm256_storeu_pd(out[2] + 4 * i, res2);
4061 res3 = _mm256_add_pd(_mm256_loadu_pd(out[3] + 4 * i), res3);
4062 _mm256_storeu_pd(out[3] + 4 * i, res3);
4063 res4 = _mm256_add_pd(_mm256_loadu_pd(out[4] + 4 * i), res4);
4064 _mm256_storeu_pd(out[4] + 4 * i, res4);
4065 res5 = _mm256_add_pd(_mm256_loadu_pd(out[5] + 4 * i), res5);
4066 _mm256_storeu_pd(out[5] + 4 * i, res5);
4067 res6 = _mm256_add_pd(_mm256_loadu_pd(out[6] + 4 * i), res6);
4068 _mm256_storeu_pd(out[6] + 4 * i, res6);
4069 res7 = _mm256_add_pd(_mm256_loadu_pd(out[7] + 4 * i), res7);
4070 _mm256_storeu_pd(out[7] + 4 * i, res7);
4071 }
4072 else
4073 {
4074 _mm256_storeu_pd(out[0] + 4 * i, res0);
4075 _mm256_storeu_pd(out[1] + 4 * i, res1);
4076 _mm256_storeu_pd(out[2] + 4 * i, res2);
4077 _mm256_storeu_pd(out[3] + 4 * i, res3);
4078 _mm256_storeu_pd(out[4] + 4 * i, res4);
4079 _mm256_storeu_pd(out[5] + 4 * i, res5);
4080 _mm256_storeu_pd(out[6] + 4 * i, res6);
4081 _mm256_storeu_pd(out[7] + 4 * i, res7);
4082 }
4083 }
4084
4085 if (add_into)
4086 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4087 for (unsigned int v = 0; v < 8; ++v)
4088 out[v][i] += in[i][v];
4089 else
4090 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4091 for (unsigned int v = 0; v < 8; ++v)
4092 out[v][i] = in[i][v];
4093}
4094
4095
4096
4100template <>
4101DEAL_II_ALWAYS_INLINE inline void
4102vectorized_transpose_and_store(const bool add_into,
4103 const unsigned int n_entries,
4105 const unsigned int *offsets,
4106 double *out)
4107{
4109 n_entries,
4110 in,
4111 {{out + offsets[0],
4112 out + offsets[1],
4113 out + offsets[2],
4114 out + offsets[3],
4115 out + offsets[4],
4116 out + offsets[5],
4117 out + offsets[6],
4118 out + offsets[7]}});
4119}
4120
4121
4122
4126template <>
4127class VectorizedArray<float, 16>
4128 : public VectorizedArrayBase<VectorizedArray<float, 16>, 16>
4129{
4130public:
4134 using value_type = float;
4135
4140 static constexpr bool is_implemented = true;
4141
4146 VectorizedArray() = default;
4147
4151 VectorizedArray(const float scalar)
4152 {
4153 this->operator=(scalar);
4154 }
4155
4159 template <typename U>
4160 VectorizedArray(const std::initializer_list<U> &list)
4161 : VectorizedArrayBase<VectorizedArray<float, 16>, 16>(list)
4162 {}
4163
4168 operator=(const float x) &
4169 {
4170 data = _mm512_set1_ps(x);
4171 return *this;
4172 }
4173
4180 operator=(const float scalar) && = delete;
4181
4185 DEAL_II_ALWAYS_INLINE inline float &
4186 operator[](const unsigned int comp)
4187 {
4188 AssertIndexRange(comp, 16);
4189 return *(reinterpret_cast<float *>(&data) + comp);
4190 }
4191
4195 DEAL_II_ALWAYS_INLINE inline const float &
4196 operator[](const unsigned int comp) const
4197 {
4198 AssertIndexRange(comp, 16);
4199 return *(reinterpret_cast<const float *>(&data) + comp);
4200 }
4201
4206 operator+=(const VectorizedArray &vec)
4207 {
4208 // if the compiler supports vector arithmetic, we can simply use +=
4209 // operator on the given data type. this allows the compiler to combine
4210 // additions with multiplication (fused multiply-add) if those
4211 // instructions are available. Otherwise, we need to use the built-in
4212 // intrinsic command for __m512d
4213# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4214 data += vec.data;
4215# else
4216 data = _mm512_add_ps(data, vec.data);
4217# endif
4218 return *this;
4219 }
4220
4225 operator-=(const VectorizedArray &vec)
4226 {
4227# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4228 data -= vec.data;
4229# else
4230 data = _mm512_sub_ps(data, vec.data);
4231# endif
4232 return *this;
4233 }
4238 operator*=(const VectorizedArray &vec)
4239 {
4240# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4241 data *= vec.data;
4242# else
4243 data = _mm512_mul_ps(data, vec.data);
4244# endif
4245 return *this;
4246 }
4247
4252 operator/=(const VectorizedArray &vec)
4253 {
4254# ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS
4255 data /= vec.data;
4256# else
4257 data = _mm512_div_ps(data, vec.data);
4258# endif
4259 return *this;
4260 }
4261
4267 DEAL_II_ALWAYS_INLINE inline void
4268 load(const float *ptr)
4269 {
4270 data = _mm512_loadu_ps(ptr);
4271 }
4272
4279 DEAL_II_ALWAYS_INLINE inline void
4280 store(float *ptr) const
4281 {
4282 _mm512_storeu_ps(ptr, data);
4283 }
4284
4289 DEAL_II_ALWAYS_INLINE inline void
4290 streaming_store(float *ptr) const
4291 {
4292 Assert(reinterpret_cast<std::size_t>(ptr) % 64 == 0,
4293 ExcMessage("Memory not aligned"));
4294 _mm512_stream_ps(ptr, data);
4295 }
4296
4300 DEAL_II_ALWAYS_INLINE inline void
4301 gather(const float *base_ptr, const unsigned int *offsets)
4302 {
4303# ifdef DEAL_II_USE_VECTORIZATION_GATHER
4304 // unfortunately, there does not appear to be a 512 bit integer load, so
4305 // do it by some reinterpret casts here. this is allowed because the Intel
4306 // API allows aliasing between different vector types.
4307 const __m512 index_val =
4308 _mm512_loadu_ps(reinterpret_cast<const float *>(offsets));
4309 const __m512i index = *reinterpret_cast<const __m512i *>(&index_val);
4310
4311 // work around a warning with gcc-12 about an uninitialized initial state
4312 // for gather by starting with a zero guess, even though all lanes will be
4313 // overwritten
4314 __m512 zero = {};
4315 const __m512i invalid = _mm512_set1_epi32(numbers::invalid_unsigned_int);
4316 __mmask16 mask = _mm512_cmpneq_epu32_mask(invalid, index);
4317
4318 data = _mm512_mask_i32gather_ps(zero, mask, index, base_ptr, 4);
4319# else
4320 this->operator=(0.f);
4321 for (unsigned int i = 0; i < 16; ++i)
4322 if (offsets[i] != numbers::invalid_unsigned_int)
4323 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
4324# endif
4325 }
4326
4330 DEAL_II_ALWAYS_INLINE inline void
4331 scatter(const unsigned int *offsets, float *base_ptr) const
4332 {
4333# ifdef DEAL_II_USE_VECTORIZATION_GATHER
4334 for (unsigned int i = 0; i < 16; ++i)
4335 for (unsigned int j = i + 1; j < 16; ++j)
4336 Assert(offsets[i] == numbers::invalid_unsigned_int ||
4337 offsets[i] != offsets[j],
4338 ExcMessage("Result of scatter undefined if two offset elements"
4339 " point to the same position"));
4340
4341 // unfortunately, there does not appear to be a 512 bit integer load, so
4342 // do it by some reinterpret casts here. this is allowed because the Intel
4343 // API allows aliasing between different vector types.
4344 const __m512 index_val =
4345 _mm512_loadu_ps(reinterpret_cast<const float *>(offsets));
4346 const __m512i index = *reinterpret_cast<const __m512i *>(&index_val);
4347 const __m512i invalid = _mm512_set1_epi32(numbers::invalid_unsigned_int);
4348 __mmask16 mask = _mm512_cmpneq_epu32_mask(invalid, index);
4349 _mm512_mask_i32scatter_ps(base_ptr, mask, index, data, 4);
4350# else
4351 for (unsigned int i = 0; i < 16; ++i)
4352 if (offsets[i] != numbers::invalid_unsigned_int)
4353 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
4354# endif
4355 }
4356
4361 float
4362 sum() const
4363 {
4365 t1.data = _mm256_add_ps(this->get_lower(), this->get_upper());
4366 return t1.sum();
4367 }
4368
4373 get_floor() const
4374 {
4375 VectorizedArray res;
4376 res.data = _mm512_roundscale_ps(data, _MM_FROUND_TO_NEG_INF);
4377 return res;
4378 }
4379
4385 __m512 data;
4386
4387private:
4391 DEAL_II_ALWAYS_INLINE inline __m256
4392 get_lower() const
4393 {
4394 return _mm512_castps512_ps256(data);
4395 }
4396
4400 DEAL_II_ALWAYS_INLINE inline __m256
4401 get_upper() const
4402 {
4403 return _mm256_castpd_ps(_mm512_extractf64x4_pd(_mm512_castps_pd(data), 1));
4404 }
4405
4411 get_sqrt() const
4412 {
4413 VectorizedArray res;
4414 res.data = _mm512_sqrt_ps(data);
4415 return res;
4416 }
4417
4423 get_abs() const
4424 {
4425 // to compute the absolute value, perform bitwise andnot with -0. This
4426 // will leave all value and exponent bits unchanged but force the sign
4427 // value to +. Since there is no andnot for AVX512, we interpret the data
4428 // as 32 bit integers and do the andnot on those types (note that andnot
4429 // is a bitwise operation so the data type does not matter)
4430 __m512 mask = _mm512_set1_ps(-0.f);
4431 VectorizedArray res;
4432 res.data = reinterpret_cast<__m512>(
4433 _mm512_andnot_epi32(reinterpret_cast<__m512i>(mask),
4434 reinterpret_cast<__m512i>(data)));
4435 return res;
4436 }
4437
4443 get_max(const VectorizedArray &other) const
4444 {
4445 VectorizedArray res;
4446 res.data = _mm512_max_ps(data, other.data);
4447 return res;
4448 }
4449
4455 get_min(const VectorizedArray &other) const
4456 {
4457 VectorizedArray res;
4458 res.data = _mm512_min_ps(data, other.data);
4459 return res;
4460 }
4461
4462 // Make a few functions friends.
4463 template <typename Number2, std::size_t width2>
4466 template <typename Number2, std::size_t width2>
4469 template <typename Number2, std::size_t width2>
4473 template <typename Number2, std::size_t width2>
4477};
4478
4479
4480
4484template <>
4485DEAL_II_ALWAYS_INLINE inline void
4486vectorized_load_and_transpose(const unsigned int n_entries,
4487 const std::array<const float *, 16> &in,
4489{
4490 // Similar to the double case, we perform the work on smaller entities. In
4491 // this case, we start from 128 bit arrays and insert them into a full 512
4492 // bit index. This reduces the code size and register pressure because we do
4493 // shuffles on 4 numbers rather than 16.
4494 const unsigned int n_chunks = n_entries / 4;
4495
4496 for (unsigned int i = 0; i < n_chunks; ++i)
4497 {
4498 __m512 t0, t1, t2, t3 = {};
4499
4500 t0 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[0] + 4 * i), 0);
4501 t0 = _mm512_insertf32x4(t0, _mm_loadu_ps(in[4] + 4 * i), 1);
4502 t0 = _mm512_insertf32x4(t0, _mm_loadu_ps(in[8] + 4 * i), 2);
4503 t0 = _mm512_insertf32x4(t0, _mm_loadu_ps(in[12] + 4 * i), 3);
4504 t1 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[1] + 4 * i), 0);
4505 t1 = _mm512_insertf32x4(t1, _mm_loadu_ps(in[5] + 4 * i), 1);
4506 t1 = _mm512_insertf32x4(t1, _mm_loadu_ps(in[9] + 4 * i), 2);
4507 t1 = _mm512_insertf32x4(t1, _mm_loadu_ps(in[13] + 4 * i), 3);
4508 t2 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[2] + 4 * i), 0);
4509 t2 = _mm512_insertf32x4(t2, _mm_loadu_ps(in[6] + 4 * i), 1);
4510 t2 = _mm512_insertf32x4(t2, _mm_loadu_ps(in[10] + 4 * i), 2);
4511 t2 = _mm512_insertf32x4(t2, _mm_loadu_ps(in[14] + 4 * i), 3);
4512 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[3] + 4 * i), 0);
4513 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[7] + 4 * i), 1);
4514 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[11] + 4 * i), 2);
4515 t3 = _mm512_insertf32x4(t3, _mm_loadu_ps(in[15] + 4 * i), 3);
4516
4517 __m512 v0 = _mm512_shuffle_ps(t0, t1, 0x44);
4518 __m512 v1 = _mm512_shuffle_ps(t0, t1, 0xee);
4519 __m512 v2 = _mm512_shuffle_ps(t2, t3, 0x44);
4520 __m512 v3 = _mm512_shuffle_ps(t2, t3, 0xee);
4521
4522 out[4 * i + 0].data = _mm512_shuffle_ps(v0, v2, 0x88);
4523 out[4 * i + 1].data = _mm512_shuffle_ps(v0, v2, 0xdd);
4524 out[4 * i + 2].data = _mm512_shuffle_ps(v1, v3, 0x88);
4525 out[4 * i + 3].data = _mm512_shuffle_ps(v1, v3, 0xdd);
4526 }
4527
4528 // Specialized remainder code
4529 const unsigned int i = 4 * n_chunks;
4530 const unsigned int remainder = n_entries - i;
4531 if (remainder == 3)
4532 {
4533 __m512 t0, t1, t2, t3 = {};
4534
4535 // Use masked load of 3 floats (this avoids accessing in[x][3], which
4536 // might be an invalid address)
4537 __m128i mask = _mm_setr_epi32(-1, -1, -1, 0);
4538 t0 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[0] + i, mask), 0);
4539 t0 = _mm512_insertf32x4(t0, _mm_maskload_ps(in[4] + i, mask), 1);
4540 t0 = _mm512_insertf32x4(t0, _mm_maskload_ps(in[8] + i, mask), 2);
4541 t0 = _mm512_insertf32x4(t0, _mm_maskload_ps(in[12] + i, mask), 3);
4542 t1 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[1] + i, mask), 0);
4543 t1 = _mm512_insertf32x4(t1, _mm_maskload_ps(in[5] + i, mask), 1);
4544 t1 = _mm512_insertf32x4(t1, _mm_maskload_ps(in[9] + i, mask), 2);
4545 t1 = _mm512_insertf32x4(t1, _mm_maskload_ps(in[13] + i, mask), 3);
4546 t2 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[2] + i, mask), 0);
4547 t2 = _mm512_insertf32x4(t2, _mm_maskload_ps(in[6] + i, mask), 1);
4548 t2 = _mm512_insertf32x4(t2, _mm_maskload_ps(in[10] + i, mask), 2);
4549 t2 = _mm512_insertf32x4(t2, _mm_maskload_ps(in[14] + i, mask), 3);
4550 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[3] + i, mask), 0);
4551 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[7] + i, mask), 1);
4552 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[11] + i, mask), 2);
4553 t3 = _mm512_insertf32x4(t3, _mm_maskload_ps(in[15] + i, mask), 3);
4554 __m512 v0 = _mm512_shuffle_ps(t0, t1, 0x44);
4555 __m512 v1 = _mm512_shuffle_ps(t0, t1, 0xee);
4556 __m512 v2 = _mm512_shuffle_ps(t2, t3, 0x44);
4557 __m512 v3 = _mm512_shuffle_ps(t2, t3, 0xee);
4558 out[i + 0].data = _mm512_shuffle_ps(v0, v2, 0x88);
4559 out[i + 1].data = _mm512_shuffle_ps(v0, v2, 0xdd);
4560 out[i + 2].data = _mm512_shuffle_ps(v1, v3, 0x88);
4561 }
4562 else if (remainder == 2)
4563 {
4564 __m512 v0, v1 = {};
4565
4566 // Use 64-bit integer loads
4567 __m128i t0, t1;
4568 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[0] + i));
4569 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[1] + i));
4570 v0 =
4571 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 0);
4572 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[4] + i));
4573 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[5] + i));
4574 v0 =
4575 _mm512_insertf32x4(v0, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 1);
4576 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[8] + i));
4577 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[9] + i));
4578 v0 =
4579 _mm512_insertf32x4(v0, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 2);
4580 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[12] + i));
4581 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[13] + i));
4582 v0 =
4583 _mm512_insertf32x4(v0, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 3);
4584 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[2] + i));
4585 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[3] + i));
4586 v1 =
4587 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 0);
4588 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[6] + i));
4589 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[7] + i));
4590 v1 =
4591 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 1);
4592 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[10] + i));
4593 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[11] + i));
4594 v1 =
4595 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 2);
4596 t0 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[14] + i));
4597 t1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(in[15] + i));
4598 v1 =
4599 _mm512_insertf32x4(v1, _mm_castsi128_ps(_mm_unpacklo_epi64(t0, t1)), 3);
4600 out[i + 0].data = _mm512_shuffle_ps(v0, v1, 0x88);
4601 out[i + 1].data = _mm512_shuffle_ps(v0, v1, 0xdd);
4602 }
4603 else if (remainder == 1)
4604 for (unsigned int v = 0; v < 16; ++v)
4605 out[i][v] = in[v][i];
4606}
4607
4608
4609
4613template <>
4614DEAL_II_ALWAYS_INLINE inline void
4615vectorized_load_and_transpose(const unsigned int n_entries,
4616 const float *in,
4617 const unsigned int *offsets,
4619{
4621 {{in + offsets[0],
4622 in + offsets[1],
4623 in + offsets[2],
4624 in + offsets[3],
4625 in + offsets[4],
4626 in + offsets[5],
4627 in + offsets[6],
4628 in + offsets[7],
4629 in + offsets[8],
4630 in + offsets[9],
4631 in + offsets[10],
4632 in + offsets[11],
4633 in + offsets[12],
4634 in + offsets[13],
4635 in + offsets[14],
4636 in + offsets[15]}},
4637 out);
4638}
4639
4640
4641
4645template <>
4646DEAL_II_ALWAYS_INLINE inline void
4647vectorized_transpose_and_store(const bool add_into,
4648 const unsigned int n_entries,
4650 const std::array<float *, 16> &out)
4651{
4652 const unsigned int n_chunks = n_entries / 4;
4653 for (unsigned int i = 0; i < n_chunks; ++i)
4654 {
4655 __m512 t0 = _mm512_shuffle_ps(in[4 * i].data, in[1 + 4 * i].data, 0x44);
4656 __m512 t1 = _mm512_shuffle_ps(in[4 * i].data, in[1 + 4 * i].data, 0xee);
4657 __m512 t2 =
4658 _mm512_shuffle_ps(in[2 + 4 * i].data, in[3 + 4 * i].data, 0x44);
4659 __m512 t3 =
4660 _mm512_shuffle_ps(in[2 + 4 * i].data, in[3 + 4 * i].data, 0xee);
4661 __m512 u0 = _mm512_shuffle_ps(t0, t2, 0x88);
4662 __m512 u1 = _mm512_shuffle_ps(t0, t2, 0xdd);
4663 __m512 u2 = _mm512_shuffle_ps(t1, t3, 0x88);
4664 __m512 u3 = _mm512_shuffle_ps(t1, t3, 0xdd);
4665
4666 __m128 res0 = _mm512_extractf32x4_ps(u0, 0);
4667 __m128 res4 = _mm512_extractf32x4_ps(u0, 1);
4668 __m128 res8 = _mm512_extractf32x4_ps(u0, 2);
4669 __m128 res12 = _mm512_extractf32x4_ps(u0, 3);
4670 __m128 res1 = _mm512_extractf32x4_ps(u1, 0);
4671 __m128 res5 = _mm512_extractf32x4_ps(u1, 1);
4672 __m128 res9 = _mm512_extractf32x4_ps(u1, 2);
4673 __m128 res13 = _mm512_extractf32x4_ps(u1, 3);
4674 __m128 res2 = _mm512_extractf32x4_ps(u2, 0);
4675 __m128 res6 = _mm512_extractf32x4_ps(u2, 1);
4676 __m128 res10 = _mm512_extractf32x4_ps(u2, 2);
4677 __m128 res14 = _mm512_extractf32x4_ps(u2, 3);
4678 __m128 res3 = _mm512_extractf32x4_ps(u3, 0);
4679 __m128 res7 = _mm512_extractf32x4_ps(u3, 1);
4680 __m128 res11 = _mm512_extractf32x4_ps(u3, 2);
4681 __m128 res15 = _mm512_extractf32x4_ps(u3, 3);
4682
4683 // Cannot use the same store instructions in both paths of the 'if'
4684 // because the compiler cannot know that there is no aliasing between
4685 // pointers
4686 if (add_into)
4687 {
4688 res0 = _mm_add_ps(_mm_loadu_ps(out[0] + 4 * i), res0);
4689 _mm_storeu_ps(out[0] + 4 * i, res0);
4690 res1 = _mm_add_ps(_mm_loadu_ps(out[1] + 4 * i), res1);
4691 _mm_storeu_ps(out[1] + 4 * i, res1);
4692 res2 = _mm_add_ps(_mm_loadu_ps(out[2] + 4 * i), res2);
4693 _mm_storeu_ps(out[2] + 4 * i, res2);
4694 res3 = _mm_add_ps(_mm_loadu_ps(out[3] + 4 * i), res3);
4695 _mm_storeu_ps(out[3] + 4 * i, res3);
4696 res4 = _mm_add_ps(_mm_loadu_ps(out[4] + 4 * i), res4);
4697 _mm_storeu_ps(out[4] + 4 * i, res4);
4698 res5 = _mm_add_ps(_mm_loadu_ps(out[5] + 4 * i), res5);
4699 _mm_storeu_ps(out[5] + 4 * i, res5);
4700 res6 = _mm_add_ps(_mm_loadu_ps(out[6] + 4 * i), res6);
4701 _mm_storeu_ps(out[6] + 4 * i, res6);
4702 res7 = _mm_add_ps(_mm_loadu_ps(out[7] + 4 * i), res7);
4703 _mm_storeu_ps(out[7] + 4 * i, res7);
4704 res8 = _mm_add_ps(_mm_loadu_ps(out[8] + 4 * i), res8);
4705 _mm_storeu_ps(out[8] + 4 * i, res8);
4706 res9 = _mm_add_ps(_mm_loadu_ps(out[9] + 4 * i), res9);
4707 _mm_storeu_ps(out[9] + 4 * i, res9);
4708 res10 = _mm_add_ps(_mm_loadu_ps(out[10] + 4 * i), res10);
4709 _mm_storeu_ps(out[10] + 4 * i, res10);
4710 res11 = _mm_add_ps(_mm_loadu_ps(out[11] + 4 * i), res11);
4711 _mm_storeu_ps(out[11] + 4 * i, res11);
4712 res12 = _mm_add_ps(_mm_loadu_ps(out[12] + 4 * i), res12);
4713 _mm_storeu_ps(out[12] + 4 * i, res12);
4714 res13 = _mm_add_ps(_mm_loadu_ps(out[13] + 4 * i), res13);
4715 _mm_storeu_ps(out[13] + 4 * i, res13);
4716 res14 = _mm_add_ps(_mm_loadu_ps(out[14] + 4 * i), res14);
4717 _mm_storeu_ps(out[14] + 4 * i, res14);
4718 res15 = _mm_add_ps(_mm_loadu_ps(out[15] + 4 * i), res15);
4719 _mm_storeu_ps(out[15] + 4 * i, res15);
4720 }
4721 else
4722 {
4723 _mm_storeu_ps(out[0] + 4 * i, res0);
4724 _mm_storeu_ps(out[1] + 4 * i, res1);
4725 _mm_storeu_ps(out[2] + 4 * i, res2);
4726 _mm_storeu_ps(out[3] + 4 * i, res3);
4727 _mm_storeu_ps(out[4] + 4 * i, res4);
4728 _mm_storeu_ps(out[5] + 4 * i, res5);
4729 _mm_storeu_ps(out[6] + 4 * i, res6);
4730 _mm_storeu_ps(out[7] + 4 * i, res7);
4731 _mm_storeu_ps(out[8] + 4 * i, res8);
4732 _mm_storeu_ps(out[9] + 4 * i, res9);
4733 _mm_storeu_ps(out[10] + 4 * i, res10);
4734 _mm_storeu_ps(out[11] + 4 * i, res11);
4735 _mm_storeu_ps(out[12] + 4 * i, res12);
4736 _mm_storeu_ps(out[13] + 4 * i, res13);
4737 _mm_storeu_ps(out[14] + 4 * i, res14);
4738 _mm_storeu_ps(out[15] + 4 * i, res15);
4739 }
4740 }
4741
4742 if (add_into)
4743 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4744 for (unsigned int v = 0; v < 16; ++v)
4745 out[v][i] += in[i][v];
4746 else
4747 for (unsigned int i = 4 * n_chunks; i < n_entries; ++i)
4748 for (unsigned int v = 0; v < 16; ++v)
4749 out[v][i] = in[i][v];
4750}
4751
4752
4753
4757template <>
4758DEAL_II_ALWAYS_INLINE inline void
4759vectorized_transpose_and_store(const bool add_into,
4760 const unsigned int n_entries,
4762 const unsigned int *offsets,
4763 float *out)
4764{
4766 n_entries,
4767 in,
4768 {{out + offsets[0],
4769 out + offsets[1],
4770 out + offsets[2],
4771 out + offsets[3],
4772 out + offsets[4],
4773 out + offsets[5],
4774 out + offsets[6],
4775 out + offsets[7],
4776 out + offsets[8],
4777 out + offsets[9],
4778 out + offsets[10],
4779 out + offsets[11],
4780 out + offsets[12],
4781 out + offsets[13],
4782 out + offsets[14],
4783 out + offsets[15]}});
4784}
4785
4786# endif
4787
4788# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__ALTIVEC__) && \
4789 defined(__VSX__)
4790
4791template <>
4792class VectorizedArray<double, 2>
4793 : public VectorizedArrayBase<VectorizedArray<double, 2>, 2>
4794{
4795public:
4799 using value_type = double;
4800
4805 static constexpr bool is_implemented = true;
4806
4811 VectorizedArray() = default;
4812
4816 VectorizedArray(const double scalar)
4817 {
4818 this->operator=(scalar);
4819 }
4820
4824 template <typename U>
4825 VectorizedArray(const std::initializer_list<U> &list)
4826 : VectorizedArrayBase<VectorizedArray<double, 2>, 2>(list)
4827 {}
4828
4833 operator=(const double x) &
4834 {
4835 data = vec_splats(x);
4836
4837 // Some compilers believe that vec_splats sets 'x', but that's not true.
4838 // They then warn about setting a variable and not using it. Suppress the
4839 // warning by "using" the variable:
4840 (void)x;
4841 return *this;
4842 }
4843
4850 operator=(const double scalar) && = delete;
4851
4855 DEAL_II_ALWAYS_INLINE inline double &
4856 operator[](const unsigned int comp)
4857 {
4858 AssertIndexRange(comp, 2);
4859 return *(reinterpret_cast<double *>(&data) + comp);
4860 }
4861
4865 DEAL_II_ALWAYS_INLINE inline const double &
4866 operator[](const unsigned int comp) const
4867 {
4868 AssertIndexRange(comp, 2);
4869 return *(reinterpret_cast<const double *>(&data) + comp);
4870 }
4871
4876 operator+=(const VectorizedArray &vec)
4877 {
4878 data = vec_add(data, vec.data);
4879 return *this;
4880 }
4881
4886 operator-=(const VectorizedArray &vec)
4887 {
4888 data = vec_sub(data, vec.data);
4889 return *this;
4890 }
4891
4896 operator*=(const VectorizedArray &vec)
4897 {
4898 data = vec_mul(data, vec.data);
4899 return *this;
4900 }
4901
4906 operator/=(const VectorizedArray &vec)
4907 {
4908 data = vec_div(data, vec.data);
4909 return *this;
4910 }
4911
4916 DEAL_II_ALWAYS_INLINE inline void
4917 load(const double *ptr)
4918 {
4919 data = vec_vsx_ld(0, ptr);
4920 }
4921
4926 DEAL_II_ALWAYS_INLINE inline void
4927 store(double *ptr) const
4928 {
4929 vec_vsx_st(data, 0, ptr);
4930 }
4931
4935 DEAL_II_ALWAYS_INLINE inline void
4936 streaming_store(double *ptr) const
4937 {
4938 store(ptr);
4939 }
4940
4944 DEAL_II_ALWAYS_INLINE inline void
4945 gather(const double *base_ptr, const unsigned int *offsets)
4946 {
4947 this->operator=(0.);
4948 for (unsigned int i = 0; i < 2; ++i)
4949 if (offsets[i] != numbers::invalid_unsigned_int)
4950 *(reinterpret_cast<double *>(&data) + i) = base_ptr[offsets[i]];
4951 }
4952
4956 DEAL_II_ALWAYS_INLINE inline void
4957 scatter(const unsigned int *offsets, double *base_ptr) const
4958 {
4959 for (unsigned int i = 0; i < 2; ++i)
4960 if (offsets[i] != numbers::invalid_unsigned_int)
4961 base_ptr[offsets[i]] = *(reinterpret_cast<const double *>(&data) + i);
4962 }
4963
4969 __vector double data;
4970
4971private:
4977 get_sqrt() const
4978 {
4979 VectorizedArray res;
4980 res.data = vec_sqrt(data);
4981 return res;
4982 }
4983
4989 get_abs() const
4990 {
4991 VectorizedArray res;
4992 res.data = vec_abs(data);
4993 return res;
4994 }
4995
5001 get_max(const VectorizedArray &other) const
5002 {
5003 VectorizedArray res;
5004 res.data = vec_max(data, other.data);
5005 return res;
5006 }
5007
5013 get_min(const VectorizedArray &other) const
5014 {
5015 VectorizedArray res;
5016 res.data = vec_min(data, other.data);
5017 return res;
5018 }
5019
5020 // Make a few functions friends.
5021 template <typename Number2, std::size_t width2>
5024 template <typename Number2, std::size_t width2>
5027 template <typename Number2, std::size_t width2>
5031 template <typename Number2, std::size_t width2>
5035};
5036
5037
5038
5039template <>
5040class VectorizedArray<float, 4>
5041 : public VectorizedArrayBase<VectorizedArray<float, 4>, 4>
5042{
5043public:
5047 using value_type = float;
5048
5053 static constexpr bool is_implemented = true;
5054
5059 VectorizedArray() = default;
5060
5064 VectorizedArray(const float scalar)
5065 {
5066 this->operator=(scalar);
5067 }
5068
5072 template <typename U>
5073 VectorizedArray(const std::initializer_list<U> &list)
5074 : VectorizedArrayBase<VectorizedArray<float, 4>, 4>(list)
5075 {}
5076
5081 operator=(const float x) &
5082 {
5083 data = vec_splats(x);
5084
5085 // Some compilers believe that vec_splats sets 'x', but that's not true.
5086 // They then warn about setting a variable and not using it. Suppress the
5087 // warning by "using" the variable:
5088 (void)x;
5089 return *this;
5090 }
5091
5098 operator=(const float scalar) && = delete;
5099
5103 DEAL_II_ALWAYS_INLINE inline float &
5104 operator[](const unsigned int comp)
5105 {
5106 AssertIndexRange(comp, 4);
5107 return *(reinterpret_cast<float *>(&data) + comp);
5108 }
5109
5113 DEAL_II_ALWAYS_INLINE inline const float &
5114 operator[](const unsigned int comp) const
5115 {
5116 AssertIndexRange(comp, 4);
5117 return *(reinterpret_cast<const float *>(&data) + comp);
5118 }
5119
5124 operator+=(const VectorizedArray &vec)
5125 {
5126 data = vec_add(data, vec.data);
5127 return *this;
5128 }
5129
5134 operator-=(const VectorizedArray &vec)
5135 {
5136 data = vec_sub(data, vec.data);
5137 return *this;
5138 }
5139
5144 operator*=(const VectorizedArray &vec)
5145 {
5146 data = vec_mul(data, vec.data);
5147 return *this;
5148 }
5149
5154 operator/=(const VectorizedArray &vec)
5155 {
5156 data = vec_div(data, vec.data);
5157 return *this;
5158 }
5159
5164 DEAL_II_ALWAYS_INLINE inline void
5165 load(const float *ptr)
5166 {
5167 data = vec_vsx_ld(0, ptr);
5168 }
5169
5174 DEAL_II_ALWAYS_INLINE inline void
5175 store(float *ptr) const
5176 {
5177 vec_vsx_st(data, 0, ptr);
5178 }
5179
5183 DEAL_II_ALWAYS_INLINE inline void
5184 streaming_store(float *ptr) const
5185 {
5186 store(ptr);
5187 }
5188
5192 DEAL_II_ALWAYS_INLINE inline void
5193 gather(const float *base_ptr, const unsigned int *offsets)
5194 {
5195 this->operator=(0.f);
5196 for (unsigned int i = 0; i < 4; ++i)
5197 if (offsets[i] != numbers::invalid_unsigned_int)
5198 *(reinterpret_cast<float *>(&data) + i) = base_ptr[offsets[i]];
5199 }
5200
5204 DEAL_II_ALWAYS_INLINE inline void
5205 scatter(const unsigned int *offsets, float *base_ptr) const
5206 {
5207 for (unsigned int i = 0; i < 4; ++i)
5208 if (offsets[i] != numbers::invalid_unsigned_int)
5209 base_ptr[offsets[i]] = *(reinterpret_cast<const float *>(&data) + i);
5210 }
5211
5217 __vector float data;
5218
5219private:
5225 get_sqrt() const
5226 {
5227 VectorizedArray res;
5228 res.data = vec_sqrt(data);
5229 return res;
5230 }
5231
5237 get_abs() const
5238 {
5239 VectorizedArray res;
5240 res.data = vec_abs(data);
5241 return res;
5242 }
5243
5249 get_max(const VectorizedArray &other) const
5250 {
5251 VectorizedArray res;
5252 res.data = vec_max(data, other.data);
5253 return res;
5254 }
5255
5261 get_min(const VectorizedArray &other) const
5262 {
5263 VectorizedArray res;
5264 res.data = vec_min(data, other.data);
5265 return res;
5266 }
5267
5268 // Make a few functions friends.
5269 template <typename Number2, std::size_t width2>
5272 template <typename Number2, std::size_t width2>
5275 template <typename Number2, std::size_t width2>
5279 template <typename Number2, std::size_t width2>
5283};
5284
5285# endif // if DEAL_II_VECTORIZATION_LEVEL >=1 && defined(__ALTIVEC__) &&
5286 // defined(__VSX__)
5287
5288
5289#endif // DOXYGEN
5290
5291
5292
5303template <typename Number, std::size_t width>
5304DEAL_II_ALWAYS_INLINE inline bool
5307{
5308 for (unsigned int i = 0; i < VectorizedArray<Number, width>::size(); ++i)
5309 if (lhs[i] != rhs[i])
5310 return false;
5311
5312 return true;
5313}
5314
5315
5321template <typename Number, std::size_t width>
5325{
5327 return tmp += v;
5328}
5329
5335template <typename Number, std::size_t width>
5339{
5341 return tmp -= v;
5342}
5343
5349template <typename Number, std::size_t width>
5353{
5355 return tmp *= v;
5356}
5357
5363template <typename Number, std::size_t width>
5367{
5369 return tmp /= v;
5370}
5371
5378template <typename Number, std::size_t width>
5381{
5383 return tmp += v;
5384}
5385
5394template <std::size_t width>
5397{
5399 return tmp += v;
5400}
5401
5408template <typename Number, std::size_t width>
5411{
5412 return u + v;
5413}
5414
5423template <std::size_t width>
5426{
5427 return u + v;
5428}
5429
5436template <typename Number, std::size_t width>
5439{
5441 return tmp -= v;
5442}
5443
5452template <std::size_t width>
5455{
5456 VectorizedArray<float, width> tmp = static_cast<float>(u);
5457 return tmp -= v;
5458}
5459
5466template <typename Number, std::size_t width>
5469{
5471 return v - tmp;
5472}
5473
5482template <std::size_t width>
5485{
5486 VectorizedArray<float, width> tmp = static_cast<float>(u);
5487 return v - tmp;
5488}
5489
5496template <typename Number, std::size_t width>
5499{
5501 return tmp *= v;
5502}
5503
5512template <std::size_t width>
5515{
5516 VectorizedArray<float, width> tmp = static_cast<float>(u);
5517 return tmp *= v;
5518}
5519
5526template <typename Number, std::size_t width>
5529{
5530 return u * v;
5531}
5532
5541template <std::size_t width>
5544{
5545 return u * v;
5546}
5547
5554template <typename Number, std::size_t width>
5557{
5559 return tmp /= v;
5560}
5561
5570template <std::size_t width>
5573{
5574 VectorizedArray<float, width> tmp = static_cast<float>(u);
5575 return tmp /= v;
5576}
5577
5584template <typename Number, std::size_t width>
5587{
5589 return v / tmp;
5590}
5591
5600template <std::size_t width>
5603{
5604 VectorizedArray<float, width> tmp = static_cast<float>(u);
5605 return v / tmp;
5606}
5607
5613template <typename Number, std::size_t width>
5616{
5617 return u;
5618}
5619
5625template <typename Number, std::size_t width>
5628{
5629 // to get a negative sign, subtract the input from zero (could also
5630 // multiply by -1, but this one is slightly simpler)
5631 return VectorizedArray<Number, width>() - u;
5632}
5633
5639template <typename Number, std::size_t width>
5640inline std::ostream &
5641operator<<(std::ostream &out, const VectorizedArray<Number, width> &p)
5642{
5643 constexpr unsigned int n = VectorizedArray<Number, width>::size();
5644 for (unsigned int i = 0; i < n - 1; ++i)
5645 out << p[i] << ' ';
5646 out << p[n - 1];
5647
5648 return out;
5649}
5650
5665enum class SIMDComparison : int
5666{
5667#if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && defined(__AVX__)
5668 equal = _CMP_EQ_OQ,
5669 not_equal = _CMP_NEQ_OQ,
5670 less_than = _CMP_LT_OQ,
5671 less_than_or_equal = _CMP_LE_OQ,
5672 greater_than = _CMP_GT_OQ,
5673 greater_than_or_equal = _CMP_GE_OQ
5674#else
5675 equal,
5676 not_equal,
5677 less_than,
5681#endif
5682};
5683
5684
5748template <SIMDComparison predicate, typename Number>
5750compare_and_apply_mask(const Number &left,
5751 const Number &right,
5752 const Number &true_value,
5753 const Number &false_value)
5754{
5755 bool mask;
5756 switch (predicate)
5757 {
5759 mask = (left == right);
5760 break;
5762 mask = (left != right);
5763 break;
5765 mask = (left < right);
5766 break;
5768 mask = (left <= right);
5769 break;
5771 mask = (left > right);
5772 break;
5774 mask = (left >= right);
5775 break;
5776 }
5777
5778 return mask ? true_value : false_value;
5779}
5780
5781
5786template <SIMDComparison predicate, typename Number>
5789 const VectorizedArray<Number, 1> &right,
5790 const VectorizedArray<Number, 1> &true_value,
5791 const VectorizedArray<Number, 1> &false_value)
5792{
5794 result.data = compare_and_apply_mask<predicate, Number>(left.data,
5795 right.data,
5796 true_value.data,
5797 false_value.data);
5798 return result;
5799}
5800
5803#ifndef DOXYGEN
5804# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 512 && defined(__AVX512F__)
5805
5806template <SIMDComparison predicate>
5809 const VectorizedArray<float, 16> &right,
5810 const VectorizedArray<float, 16> &true_values,
5811 const VectorizedArray<float, 16> &false_values)
5812{
5813 const __mmask16 mask =
5814 _mm512_cmp_ps_mask(left.data, right.data, static_cast<int>(predicate));
5816 result.data = _mm512_mask_mov_ps(false_values.data, mask, true_values.data);
5817 return result;
5818}
5819
5820
5821
5822template <SIMDComparison predicate>
5825 const VectorizedArray<double, 8> &right,
5826 const VectorizedArray<double, 8> &true_values,
5827 const VectorizedArray<double, 8> &false_values)
5828{
5829 const __mmask16 mask =
5830 _mm512_cmp_pd_mask(left.data, right.data, static_cast<int>(predicate));
5832 result.data = _mm512_mask_mov_pd(false_values.data, mask, true_values.data);
5833 return result;
5834}
5835
5836# endif
5837
5838# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 256 && defined(__AVX__)
5839
5840template <SIMDComparison predicate>
5843 const VectorizedArray<float, 8> &right,
5844 const VectorizedArray<float, 8> &true_values,
5845 const VectorizedArray<float, 8> &false_values)
5846{
5847 const auto mask =
5848 _mm256_cmp_ps(left.data, right.data, static_cast<int>(predicate));
5849
5851 result.data = _mm256_blendv_ps(false_values.data, true_values.data, mask);
5852 return result;
5853}
5854
5855
5856template <SIMDComparison predicate>
5859 const VectorizedArray<double, 4> &right,
5860 const VectorizedArray<double, 4> &true_values,
5861 const VectorizedArray<double, 4> &false_values)
5862{
5863 const auto mask =
5864 _mm256_cmp_pd(left.data, right.data, static_cast<int>(predicate));
5865
5867 result.data = _mm256_blendv_pd(false_values.data, true_values.data, mask);
5868 return result;
5869}
5870
5871# endif
5872
5873# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__SSE2__)
5874
5875template <SIMDComparison predicate>
5878 const VectorizedArray<float, 4> &right,
5879 const VectorizedArray<float, 4> &true_values,
5880 const VectorizedArray<float, 4> &false_values)
5881{
5882 __m128 mask;
5883 switch (predicate)
5884 {
5886 mask = _mm_cmpeq_ps(left.data, right.data);
5887 break;
5889 mask = _mm_cmpneq_ps(left.data, right.data);
5890 break;
5892 mask = _mm_cmplt_ps(left.data, right.data);
5893 break;
5895 mask = _mm_cmple_ps(left.data, right.data);
5896 break;
5898 mask = _mm_cmpgt_ps(left.data, right.data);
5899 break;
5901 mask = _mm_cmpge_ps(left.data, right.data);
5902 break;
5903 }
5904
5906 result.data = _mm_or_ps(_mm_and_ps(mask, true_values.data),
5907 _mm_andnot_ps(mask, false_values.data));
5908
5909 return result;
5910}
5911
5912
5913template <SIMDComparison predicate>
5916 const VectorizedArray<double, 2> &right,
5917 const VectorizedArray<double, 2> &true_values,
5918 const VectorizedArray<double, 2> &false_values)
5919{
5920 __m128d mask;
5921 switch (predicate)
5922 {
5924 mask = _mm_cmpeq_pd(left.data, right.data);
5925 break;
5927 mask = _mm_cmpneq_pd(left.data, right.data);
5928 break;
5930 mask = _mm_cmplt_pd(left.data, right.data);
5931 break;
5933 mask = _mm_cmple_pd(left.data, right.data);
5934 break;
5936 mask = _mm_cmpgt_pd(left.data, right.data);
5937 break;
5939 mask = _mm_cmpge_pd(left.data, right.data);
5940 break;
5941 }
5942
5944 result.data = _mm_or_pd(_mm_and_pd(mask, true_values.data),
5945 _mm_andnot_pd(mask, false_values.data));
5946
5947 return result;
5948}
5949
5950# endif
5951
5952# if DEAL_II_VECTORIZATION_WIDTH_IN_BITS >= 128 && defined(__ARM_NEON)
5953
5954template <SIMDComparison predicate>
5957 const VectorizedArray<float, 4> &right,
5958 const VectorizedArray<float, 4> &true_values,
5959 const VectorizedArray<float, 4> &false_values)
5960{
5961 uint32x4_t mask;
5962 switch (predicate)
5963 {
5965 mask = vceqq_f32(left.data, right.data);
5966 break;
5968 mask = vmvnq_u32(vceqq_f32(left.data, right.data));
5969 break;
5971 mask = vcltq_f32(left.data, right.data);
5972 break;
5974 mask = vcleq_f32(left.data, right.data);
5975 break;
5977 mask = vcgtq_f32(left.data, right.data);
5978 break;
5980 mask = vcgeq_f32(left.data, right.data);
5981 break;
5982 }
5983
5985 result.data = vreinterpretq_f32_u32(vorrq_u32(
5986 vandq_u32(mask, vreinterpretq_u32_f32(true_values.data)),
5987 vandq_u32(vmvnq_u32(mask), vreinterpretq_u32_f32(false_values.data))));
5988
5989 return result;
5990}
5991
5992
5993template <SIMDComparison predicate>
5996 const VectorizedArray<double, 2> &right,
5997 const VectorizedArray<double, 2> &true_values,
5998 const VectorizedArray<double, 2> &false_values)
5999{
6000 uint64x2_t mask;
6001 switch (predicate)
6002 {
6004 mask = vceqq_f64(left.data, right.data);
6005 break;
6007 mask = vreinterpretq_u64_u32(
6008 vmvnq_u32(vreinterpretq_u32_u64(vceqq_f64(left.data, right.data))));
6009 break;
6011 mask = vcltq_f64(left.data, right.data);
6012 break;
6014 mask = vcleq_f64(left.data, right.data);
6015 break;
6017 mask = vcgtq_f64(left.data, right.data);
6018 break;
6020 mask = vcgeq_f64(left.data, right.data);
6021 break;
6022 }
6023
6025 result.data = vreinterpretq_f64_u64(vorrq_u64(
6026 vandq_u64(mask, vreinterpretq_u64_f64(true_values.data)),
6027 vandq_u64(vreinterpretq_u64_u32(vmvnq_u32(vreinterpretq_u32_u64(mask))),
6028 vreinterpretq_u64_f64(false_values.data))));
6029
6030 return result;
6031}
6032
6033# endif
6034#endif // DOXYGEN
6035
6036
6037namespace internal
6038{
6039 template <typename T>
6041 {
6045 using value_type = T;
6046
6050 static constexpr std::size_t
6052 {
6053 return 1;
6054 }
6055
6060
6067 static constexpr std::size_t
6069 {
6071 }
6072
6076 static value_type &
6077 get(value_type &value, unsigned int c)
6078 {
6079 AssertIndexRange(c, 1);
6080 (void)c;
6081
6082 return value;
6083 }
6084
6088 static const value_type &
6089 get(const value_type &value, unsigned int c)
6090 {
6091 AssertIndexRange(c, 1);
6092 (void)c;
6093
6094 return value;
6095 }
6096
6100 static value_type &
6102 {
6104
6105 return values[c];
6106 }
6107
6112 static const value_type &
6113 get_from_vectorized(const vectorized_value_type &values, unsigned int c)
6114 {
6116
6117 return values[c];
6118 }
6119 };
6120
6121 template <typename T, std::size_t width_>
6123 {
6127 using value_type = T;
6128
6132 static constexpr std::size_t
6134 {
6135 return width_;
6136 }
6137
6142
6150 static constexpr std::size_t
6152 {
6153 return 1;
6154 }
6155
6159 static value_type &
6160 get(vectorized_value_type &values, unsigned int c)
6161 {
6162 AssertIndexRange(c, width_);
6163
6164 return values[c];
6165 }
6166
6170 static const value_type &
6171 get(const vectorized_value_type &values, unsigned int c)
6172 {
6173 AssertIndexRange(c, width_);
6174
6175 return values[c];
6176 }
6177
6181 static vectorized_value_type &
6183 {
6184 (void)c;
6186
6187 return values;
6188 }
6189
6194 static const vectorized_value_type &
6195 get_from_vectorized(const vectorized_value_type &values, unsigned int c)
6196 {
6197 (void)c;
6199
6200 return values;
6201 }
6202 };
6203} // namespace internal
6204
6205
6207
6214namespace std
6215{
6223 template <typename Number, std::size_t width>
6224 inline ::VectorizedArray<Number, width>
6225 sin(const ::VectorizedArray<Number, width> &x)
6226 {
6228 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6229 ++i)
6230 out[i] = std::sin(x[i]);
6231 return out;
6232 }
6233
6234
6235
6243 template <typename Number, std::size_t width>
6244 inline ::VectorizedArray<Number, width>
6245 cos(const ::VectorizedArray<Number, width> &x)
6246 {
6248 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6249 ++i)
6250 out[i] = std::cos(x[i]);
6251 return out;
6252 }
6253
6254
6255
6263 template <typename Number, std::size_t width>
6264 inline ::VectorizedArray<Number, width>
6265 tan(const ::VectorizedArray<Number, width> &x)
6266 {
6268 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6269 ++i)
6270 out[i] = std::tan(x[i]);
6271 return out;
6272 }
6273
6274
6275
6283 template <typename Number, std::size_t width>
6284 inline ::VectorizedArray<Number, width>
6285 acos(const ::VectorizedArray<Number, width> &x)
6286 {
6288 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6289 ++i)
6290 out[i] = std::acos(x[i]);
6291 return out;
6292 }
6293
6294
6295
6303 template <typename Number, std::size_t width>
6304 inline ::VectorizedArray<Number, width>
6305 asin(const ::VectorizedArray<Number, width> &x)
6306 {
6308 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6309 ++i)
6310 out[i] = std::asin(x[i]);
6311 return out;
6312 }
6313
6314
6315
6323 template <typename Number, std::size_t width>
6324 inline ::VectorizedArray<Number, width>
6325 atan(const ::VectorizedArray<Number, width> &x)
6326 {
6328 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6329 ++i)
6330 out[i] = std::atan(x[i]);
6331 return out;
6332 }
6333
6334
6335
6343 template <typename Number, std::size_t width>
6344 inline ::VectorizedArray<Number, width>
6345 cosh(const ::VectorizedArray<Number, width> &x)
6346 {
6348 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6349 ++i)
6350 out[i] = std::cosh(x[i]);
6351 return out;
6352 }
6353
6354
6355
6363 template <typename Number, std::size_t width>
6364 inline ::VectorizedArray<Number, width>
6365 sinh(const ::VectorizedArray<Number, width> &x)
6366 {
6368 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6369 ++i)
6370 out[i] = std::sinh(x[i]);
6371 return out;
6372 }
6373
6374
6375
6383 template <typename Number, std::size_t width>
6384 inline ::VectorizedArray<Number, width>
6385 tanh(const ::VectorizedArray<Number, width> &x)
6386 {
6388 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6389 ++i)
6390 out[i] = std::tanh(x[i]);
6391 return out;
6392 }
6393
6394
6395
6403 template <typename Number, std::size_t width>
6404 inline ::VectorizedArray<Number, width>
6405 acosh(const ::VectorizedArray<Number, width> &x)
6406 {
6408 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6409 ++i)
6410 out[i] = std::acosh(x[i]);
6411 return out;
6412 }
6413
6414
6415
6423 template <typename Number, std::size_t width>
6424 inline ::VectorizedArray<Number, width>
6425 asinh(const ::VectorizedArray<Number, width> &x)
6426 {
6428 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6429 ++i)
6430 out[i] = std::asinh(x[i]);
6431 return out;
6432 }
6433
6434
6435
6443 template <typename Number, std::size_t width>
6444 inline ::VectorizedArray<Number, width>
6445 atanh(const ::VectorizedArray<Number, width> &x)
6446 {
6448 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6449 ++i)
6450 out[i] = std::atanh(x[i]);
6451 return out;
6452 }
6453
6454
6455
6463 template <typename Number, std::size_t width>
6464 inline ::VectorizedArray<Number, width>
6465 exp(const ::VectorizedArray<Number, width> &x)
6466 {
6468 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6469 ++i)
6470 out[i] = std::exp(x[i]);
6471 return out;
6472 }
6473
6474
6475
6483 template <typename Number, std::size_t width>
6484 inline ::VectorizedArray<Number, width>
6485 log(const ::VectorizedArray<Number, width> &x)
6486 {
6488 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6489 ++i)
6490 out[i] = std::log(x[i]);
6491 return out;
6492 }
6493
6494
6495
6503 template <typename Number, std::size_t width>
6504 inline ::VectorizedArray<Number, width>
6505 sqrt(const ::VectorizedArray<Number, width> &x)
6506 {
6507 return x.get_sqrt();
6508 }
6509
6510
6511
6519 template <typename Number, std::size_t width>
6520 inline ::VectorizedArray<Number, width>
6521 pow(const ::VectorizedArray<Number, width> &x, const Number p)
6522 {
6524 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6525 ++i)
6526 out[i] = std::pow(x[i], p);
6527 return out;
6528 }
6529
6530
6531
6540 template <typename Number, std::size_t width>
6541 inline ::VectorizedArray<Number, width>
6542 pow(const ::VectorizedArray<Number, width> &x,
6543 const ::VectorizedArray<Number, width> &p)
6544 {
6546 for (unsigned int i = 0; i < ::VectorizedArray<Number, width>::size();
6547 ++i)
6548 out[i] = std::pow(x[i], p[i]);
6549 return out;
6550 }
6551
6552
6553
6561 template <typename Number, std::size_t width>
6562 inline ::VectorizedArray<Number, width>
6563 abs(const ::VectorizedArray<Number, width> &x)
6564 {
6565 return x.get_abs();
6566 }
6567
6568
6569
6577 template <typename Number, std::size_t width>
6578 inline ::VectorizedArray<Number, width>
6579 max(const ::VectorizedArray<Number, width> &x,
6580 const ::VectorizedArray<Number, width> &y)
6581 {
6582 return x.get_max(y);
6583 }
6584
6585
6586
6594 template <typename Number, std::size_t width>
6595 inline ::VectorizedArray<Number, width>
6596 min(const ::VectorizedArray<Number, width> &x,
6597 const ::VectorizedArray<Number, width> &y)
6598 {
6599 return x.get_min(y);
6600 }
6601
6602
6603
6607 template <class T>
6609 {
6610#ifdef DEAL_II_HAVE_CXX20
6611 using iterator_category = contiguous_iterator_tag;
6612#else
6613 using iterator_category = random_access_iterator_tag;
6614#endif
6615 using value_type = typename T::value_type;
6616 using difference_type = std::ptrdiff_t;
6617 };
6618
6619} // namespace std
6620
6621#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 > operator/(const VectorizedArray< Number, width > &v, const Number &u)
VectorizedArray< Number, width > asinh(const ::VectorizedArray< Number, width > &x)
VectorizedArray get_abs() const
VectorizedArray< float, width > operator/(const VectorizedArray< float, width > &v, const double u)
VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &x, const ::VectorizedArray< Number, width > &y)
VectorizedArray< Number, width > log(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > operator*(const VectorizedArray< Number, width > &v, const Number &u)
VectorizedArray< Number, width > exp(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > operator-(const VectorizedArray< Number, width > &v, const Number &u)
Number & operator[](const unsigned int comp)
VectorizedArray get_floor() const
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)
Number sum() const
VectorizedArray()=default
bool operator==(const VectorizedArray< Number, width > &lhs, const VectorizedArray< Number, width > &rhs)
VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &x)
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
VectorizedArray< Number, width > atan(const ::VectorizedArray< Number, width > &x)
const Number & operator[](const unsigned int comp) const
VectorizedArray< Number, width > tanh(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &x, const ::VectorizedArray< Number, width > &y)
VectorizedArray get_min(const VectorizedArray &other) const
VectorizedArray< Number, width > cosh(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &x, const Number p)
VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &x, const ::VectorizedArray< Number, width > &p)
VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &x)
void store(OtherNumber *ptr) const
VectorizedArray< float, width > operator-(const VectorizedArray< float, width > &v, const double u)
void load(const OtherNumber *ptr)
void scatter(const unsigned int *offsets, Number *base_ptr) const
VectorizedArray< Number, width > atanh(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > operator-(const Number &u, const VectorizedArray< Number, width > &v)
VectorizedArray & operator=(const Number scalar) &&=delete
VectorizedArray< Number, width > operator*(const VectorizedArray< Number, width > &u, const VectorizedArray< Number, width > &v)
VectorizedArray & operator-=(const VectorizedArray &vec)
VectorizedArray< Number, width > acos(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 > sinh(const ::VectorizedArray< Number, width > &x)
VectorizedArray get_sqrt() const
VectorizedArray< Number, width > asin(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > operator/(const Number &u, const VectorizedArray< Number, width > &v)
VectorizedArray & operator+=(const VectorizedArray &vec)
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 > acosh(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &x)
VectorizedArray< Number, width > sin(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< float, width > operator*(const double u, const VectorizedArray< float, width > &v)
static constexpr bool is_implemented
#define DEAL_II_ALWAYS_INLINE
Definition config.h:166
#define DEAL_II_OPENMP_SIMD_PRAGMA
Definition config.h:214
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
const unsigned int v0
const unsigned int v1
#define Assert(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcMessage(std::string arg1)
std::vector< index_type > data
Definition mpi.cc:734
constexpr types::blas_int zero
*  *  *  RotationFunction< dim, Number >::RotationFunction Number(dim)
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
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)
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)
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)