ryujin 2.1.1 revision b04fb4aba7bd97ddfa5994a6b4b6e8586eb15db2
Loading...
Searching...
No Matches
simd.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <compile_time_options.h>
10
11#include <deal.II/base/tensor.h>
12#include <deal.II/base/utilities.h>
13#include <deal.II/base/vectorization.h>
14
15#include <cmath>
16
17
22
23
34#define AssertThrowSIMD(variable, condition, exception) \
35 if constexpr (std::is_same< \
36 typename std::remove_const<decltype(variable)>::type, \
37 double>::value || \
38 std::is_same< \
39 typename std::remove_const<decltype(variable)>::type, \
40 float>::value) { \
41 AssertThrow(condition(variable), exception); \
42 } else { \
43 for (unsigned int k = 0; k < decltype(variable)::size(); ++k) { \
44 AssertThrow(condition((variable)[k]), exception); \
45 } \
46 }
47
48
49namespace ryujin
50{
52
56
57
65 template <typename T>
67 using type = T;
68 };
69
70
71 template <typename T, std::size_t width>
72 struct get_value_type<dealii::VectorizedArray<T, width>> {
73 using type = T;
74 };
76
77
78#ifndef DOXYGEN
79 namespace
80 {
81 template <typename Functor, size_t... Is>
82 auto generate_iterators_impl(Functor f, std::index_sequence<Is...>)
83 -> std::array<decltype(f(0)), sizeof...(Is)>
84 {
85 return {{f(Is)...}};
86 }
87 } /* namespace */
88#endif
89
90
102 template <unsigned int length, typename Functor>
103 DEAL_II_ALWAYS_INLINE inline auto generate_iterators(Functor f)
104 -> std::array<decltype(f(0)), length>
105 {
106 return generate_iterators_impl<>(f, std::make_index_sequence<length>());
107 }
108
109
115 template <typename T>
116 DEAL_II_ALWAYS_INLINE inline void increment_iterators(T &iterators)
117 {
118 for (auto &it : iterators)
119 it++;
120 }
121
123
127
133 template <typename Number>
134 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number positive_part(const Number number)
135 {
136 return std::max(Number(0.), number);
137 }
138
139
145 template <typename Number>
146 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number negative_part(const Number number)
147 {
148 return -std::min(Number(0.), number);
149 }
150
151
162 template <typename Number>
163 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
164 safe_division(const Number &numerator, const Number &denominator)
165 {
166 using ScalarNumber = typename get_value_type<Number>::type;
167 constexpr ScalarNumber min = std::numeric_limits<ScalarNumber>::min();
168
169 return std::max(numerator, Number(0.)) / std::max(denominator, Number(min));
170 }
171
172
180 template <dealii::SIMDComparison predicate, typename Number>
181 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
182 compare_and_apply_mask(const Number &left,
183 const Number &right,
184 const Number &true_value,
185 const Number &false_value)
186 {
187 static_assert(std::is_floating_point_v<Number>,
188 "Only scalar number types are allowed");
189
190 bool mask = false;
191 if constexpr (predicate == dealii::SIMDComparison::equal)
192 mask = (left == right);
193 else if constexpr (predicate == dealii::SIMDComparison::not_equal)
194 mask = (left != right);
195 else if constexpr (predicate == dealii::SIMDComparison::less_than)
196 mask = (left < right);
197 else if constexpr (predicate == dealii::SIMDComparison::less_than_or_equal)
198 mask = (left <= right);
199 else if constexpr (predicate == dealii::SIMDComparison::greater_than)
200 mask = (left > right);
201 else
202 mask = (left >= right);
203
204 return mask ? true_value : false_value;
205 }
206
207
214 template <dealii::SIMDComparison predicate, typename T, std::size_t width>
215 DEAL_II_ALWAYS_INLINE inline dealii::VectorizedArray<T, width>
216 compare_and_apply_mask(const dealii::VectorizedArray<T, width> &left,
217 const dealii::VectorizedArray<T, width> &right,
218 const dealii::VectorizedArray<T, width> &true_value,
219 const dealii::VectorizedArray<T, width> &false_value)
220 {
221 return dealii::compare_and_apply_mask<predicate>(
222 left, right, true_value, false_value);
223 }
224
225
233 template <int N, typename T>
234 DEAL_II_HOST_DEVICE_ALWAYS_INLINE T fixed_power(const T x)
235 {
236 return dealii::Utilities::fixed_power<N, T>(x);
237 }
238
239
245 template <typename T>
246 DEAL_II_HOST_DEVICE T pow(const T x, const T b);
247
248
254 template <typename T, std::size_t width>
255 dealii::VectorizedArray<T, width>
256 pow(const dealii::VectorizedArray<T, width> x, const T b);
257
258
265 template <typename T, std::size_t width>
266 dealii::VectorizedArray<T, width>
267 pow(const dealii::VectorizedArray<T, width> x,
268 const dealii::VectorizedArray<T, width> b);
269
270
271 template <>
272 DEAL_II_HOST_DEVICE_ALWAYS_INLINE float pow(const float x, const float b)
273 {
274#ifdef RYUJIN_DEVICE_COMPILATION_PASS
275 /* Call generic std::pow() implementation: */
276 return std::pow(x, b);
277#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
278 /* Use a custom pow implementation instead of std::pow(): */
279 return pow(dealii::VectorizedArray<float, 4>(x), b)[0];
280#else
281 /* Call generic std::pow() implementation: */
282 return std::pow(x, b);
283#endif
284 }
285
286
287 template <>
288 DEAL_II_HOST_DEVICE_ALWAYS_INLINE double pow(const double x, const double b)
289 {
290#ifdef RYUJIN_DEVICE_COMPILATION_PASS
291 /* Call generic std::pow() implementation */
292 return std::pow(x, b);
293#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
294 /* Use a custom pow implementation instead of std::pow(): */
295 return pow(dealii::VectorizedArray<double, 2>(x), b)[0];
296#else
297 /* Call generic std::pow() implementation */
298 return std::pow(x, b);
299#endif
300 }
301
302
306 enum class Bias {
310 none,
311
315 max,
316
320 min
321 };
322
323
329 template <typename T>
330 DEAL_II_HOST_DEVICE T fast_pow(const T x,
331 const T b,
332 const Bias bias = Bias::none);
333
334
340 template <typename T, std::size_t width>
341 dealii::VectorizedArray<T, width>
342 fast_pow(const dealii::VectorizedArray<T, width> x,
343 const T b,
344 const Bias bias = Bias::none);
345
346
353 template <typename T, std::size_t width>
354 dealii::VectorizedArray<T, width>
355 fast_pow(const dealii::VectorizedArray<T, width> x,
356 const dealii::VectorizedArray<T, width> b,
357 const Bias bias = Bias::none);
358
359
360 template <>
361 DEAL_II_HOST_DEVICE_ALWAYS_INLINE float
362 fast_pow(const float x, const float b, [[maybe_unused]] const Bias bias)
363 {
364#ifdef RYUJIN_DEVICE_COMPILATION_PASS
365 /* Call generic std::pow() implementation */
366 return std::pow(x, b);
367#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
368 /* Use a custom fast_pow implementation instead of std::pow(): */
369 return fast_pow(dealii::VectorizedArray<float, 4>(x), b, bias)[0];
370#else
371 /* Call generic std::pow() implementation */
372 return std::pow(x, b);
373#endif
374 }
375
376
377 template <>
378 DEAL_II_HOST_DEVICE_ALWAYS_INLINE double
379 fast_pow(const double x, const double b, [[maybe_unused]] const Bias bias)
380 {
381#ifdef RYUJIN_DEVICE_COMPILATION_PASS
382 /* Call generic std::pow() implementation (in single precision) */
383 return std::pow(static_cast<float>(x), static_cast<float>(b));
384#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
385 /* Use a custom fast_pow implementation instead of std::pow(): */
386 return fast_pow(dealii::VectorizedArray<double, 2>(x), b, bias)[0];
387#else
388 /* Call generic std::pow() implementation (in single precision) */
389 return std::pow(static_cast<float>(x), static_cast<float>(b));
390#endif
391 }
392
394
398
405 template <typename T, typename V>
406 DEAL_II_ALWAYS_INLINE inline T read_entry(const V &vector, unsigned int i)
407 {
408 static_assert(std::is_same_v<typename get_value_type<T>::type,
409 typename V::value_type>,
410 "type mismatch");
411 T result;
412
413 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
414 /* Non-vectorized sequential access. */
415 result = vector.local_element(i);
416 } else {
417 /* Vectorized fast access. index must be divisible by simd_length */
418 result.load(vector.get_values() + i);
419 }
420
421 return result;
422 }
423
424
429 template <typename T, typename T2>
430 DEAL_II_ALWAYS_INLINE inline T read_entry(const std::vector<T2> &vector,
431 unsigned int i)
432 {
433 if constexpr (std::is_same_v<typename get_value_type<T>::type, T2>) {
434 /* Optimized default for source and destination with same type: */
435
436 T result;
437 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
438 /* Non-vectorized sequential access. */
439 result = vector[i];
440 } else {
441 /* Vectorized fast access. index must be divisible by simd_length */
442 result.load(vector.data() + i);
443 }
444 return result;
445
446 } else {
447 /* Fallback for mismatched types (float vs double): */
448 T result;
449 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
450 result = vector[i];
451 } else {
452 // FIXME: suboptimal
453 for (unsigned int k = 0; k < T::size(); ++k)
454 result[k] = vector[i + k];
455 }
456 return result;
457 }
458 }
459
460
467 template <typename T, typename V>
468 DEAL_II_ALWAYS_INLINE inline T read_entry(const V &vector,
469 const unsigned int *js)
470 {
471 static_assert(std::is_same_v<typename get_value_type<T>::type,
472 typename V::value_type>,
473 "type mismatch");
474 T result;
475
476 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
477 /* Non-vectorized sequential access. */
478 result = vector.local_element(js[0]);
479 } else {
480 /* Vectorized fast access. index must be divisible by simd_length */
481 result.gather(vector.get_values(), js);
482 }
483
484 return result;
485 }
486
487
492 template <typename T, typename T2>
493 DEAL_II_ALWAYS_INLINE inline T read_entry(const std::vector<T2> &vector,
494 const unsigned int *js)
495 {
496 static_assert(std::is_same_v<typename get_value_type<T>::type, T2>,
497 "type mismatch");
498 T result;
499
500 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
501 /* Non-vectorized sequential access. */
502 result = vector[js[0]];
503 } else {
504 /* Vectorized fast access. index must be divisible by simd_length */
505 result.load(vector.data(), js);
506 }
507
508 return result;
509 }
510
511
517 template <typename T, typename V>
518 DEAL_II_ALWAYS_INLINE inline void
519 write_entry(V &vector, const T &values, unsigned int i)
520 {
521 static_assert(std::is_same_v<typename get_value_type<T>::type,
522 typename V::value_type>,
523 "type mismatch");
524
525 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
526 /* Non-vectorized sequential access. */
527 vector.local_element(i) = values;
528 } else {
529 /* Vectorized fast access. index must be divisible by simd_length */
530 values.store(vector.get_values() + i);
531 }
532 }
533
534
539 template <typename T, typename T2>
540 DEAL_II_ALWAYS_INLINE inline void
541 write_entry(std::vector<T2> &vector, const T &values, unsigned int i)
542 {
543 if constexpr (std::is_same_v<typename get_value_type<T>::type, T2>) {
544 /* Optimized default for source and destination with same type: */
545
546 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
547 /* Non-vectorized sequential access. */
548 vector[i] = values;
549 } else {
550 /* Vectorized fast access. index must be divisible by simd_length */
551 values.store(vector.data() + i);
552 }
553
554 } else {
555 /* Fallback for mismatched types (float vs double): */
556 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
557 vector[i] = values;
558 } else {
559 // FIXME: suboptimal
560 for (unsigned int k = 0; k < T::size(); ++k)
561 vector[i + k] = values[k];
562 }
563 }
564 }
565
566
572 template <int rank, int dim, std::size_t width, typename Number>
573 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<rank, dim, Number>
575 const dealii::Tensor<rank, dim, dealii::VectorizedArray<Number, width>>
576 &vectorized,
577 const unsigned int k)
578 {
579 Assert(k < width, dealii::ExcMessage("Index past VectorizedArray width"));
580 dealii::Tensor<rank, dim, Number> result;
581 if constexpr (rank == 1) {
582 for (unsigned int d = 0; d < dim; ++d)
583 result[d] = vectorized[d][k];
584 } else {
585 for (unsigned int d = 0; d < dim; ++d)
586 result[d] = serialize_tensor(vectorized[d], k);
587 }
588 return result;
589 }
590
591
598 template <int rank, int dim, typename Number>
599 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<rank, dim, Number>
600 serialize_tensor(const dealii::Tensor<rank, dim, Number> &serial,
601 const unsigned int k [[maybe_unused]])
602 {
603 Assert(k == 0,
604 dealii::ExcMessage(
605 "The given index k must be zero for a serial tensor"));
606 return serial;
607 }
608
609
615 template <int rank, int dim, std::size_t width, typename Number>
616 DEAL_II_ALWAYS_INLINE inline void assign_serial_tensor(
617 dealii::Tensor<rank, dim, dealii::VectorizedArray<Number, width>> &result,
618 const dealii::Tensor<rank, dim, Number> &serial,
619 const unsigned int k)
620 {
621 Assert(k < width, dealii::ExcMessage("Index past VectorizedArray width"));
622 if constexpr (rank == 1) {
623 for (unsigned int d = 0; d < dim; ++d)
624 result[d][k] = serial[d];
625 } else {
626 for (unsigned int d = 0; d < dim; ++d)
627 assign_serial_tensor(result[d], serial[d], k);
628 }
629 }
630
631
638 template <int rank, int dim, typename Number>
639 DEAL_II_ALWAYS_INLINE inline void
640 assign_serial_tensor(dealii::Tensor<rank, dim, Number> &result,
641 const dealii::Tensor<rank, dim, Number> &serial,
642 const unsigned int k [[maybe_unused]])
643 {
644 Assert(k == 0,
645 dealii::ExcMessage(
646 "The given index k must be zero for a serial tensor"));
647
648 result = serial;
649 }
650
652
653} // namespace ryujin
DEAL_II_HOST_DEVICE T pow(const T x, const T b)
DEAL_II_ALWAYS_INLINE T read_entry(const V &vector, unsigned int i)
Definition simd.h:406
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number positive_part(const Number number)
Definition simd.h:134
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number negative_part(const Number number)
Definition simd.h:146
DEAL_II_ALWAYS_INLINE void assign_serial_tensor(dealii::Tensor< rank, dim, dealii::VectorizedArray< Number, width > > &result, const dealii::Tensor< rank, dim, Number > &serial, const unsigned int k)
Definition simd.h:616
DEAL_II_ALWAYS_INLINE auto generate_iterators(Functor f) -> std::array< auto, length >
Definition simd.h:103
DEAL_II_HOST_DEVICE T fast_pow(const T x, const T b, const Bias bias=Bias::none)
DEAL_II_HOST_DEVICE_ALWAYS_INLINE T fixed_power(const T x)
Definition simd.h:234
DEAL_II_ALWAYS_INLINE void increment_iterators(T &iterators)
Definition simd.h:116
DEAL_II_ALWAYS_INLINE void write_entry(V &vector, const T &values, unsigned int i)
Definition simd.h:519
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number compare_and_apply_mask(const Number &left, const Number &right, const Number &true_value, const Number &false_value)
Definition simd.h:182
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number safe_division(const Number &numerator, const Number &denominator)
Definition simd.h:164
DEAL_II_ALWAYS_INLINE dealii::Tensor< rank, dim, Number > serialize_tensor(const dealii::Tensor< rank, dim, dealii::VectorizedArray< Number, width > > &vectorized, const unsigned int k)
Definition simd.h:574
Bias
Definition simd.h:306