ryujin 2.1.1 revision ee5cbcbf2346c1299c942d0e1f13b46449973c18
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
84 template <typename T>
85 constexpr unsigned int get_stride_size = 1;
86
87 template <typename T, std::size_t width>
88 constexpr unsigned int get_stride_size<dealii::VectorizedArray<T, width>> =
89 width;
91
92
93#ifndef DOXYGEN
94 namespace
95 {
96 template <typename Functor, size_t... Is>
97 auto generate_iterators_impl(Functor f, std::index_sequence<Is...>)
98 -> std::array<decltype(f(0)), sizeof...(Is)>
99 {
100 return {{f(Is)...}};
101 }
102 } /* namespace */
103#endif
104
105
117 template <unsigned int length, typename Functor>
118 DEAL_II_ALWAYS_INLINE inline auto generate_iterators(Functor f)
119 -> std::array<decltype(f(0)), length>
120 {
121 return generate_iterators_impl<>(f, std::make_index_sequence<length>());
122 }
123
124
130 template <typename T>
131 DEAL_II_ALWAYS_INLINE inline void increment_iterators(T &iterators)
132 {
133 for (auto &it : iterators)
134 it++;
135 }
136
138
142
148 template <typename Number>
149 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number positive_part(const Number number)
150 {
151 return std::max(Number(0.), number);
152 }
153
154
160 template <typename Number>
161 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number negative_part(const Number number)
162 {
163 return -std::min(Number(0.), number);
164 }
165
166
174 template <dealii::SIMDComparison predicate, typename Number>
175 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
176 compare_and_apply_mask(const Number &left,
177 const Number &right,
178 const Number &true_value,
179 const Number &false_value)
180 {
181 static_assert(std::is_floating_point_v<Number>,
182 "Only scalar number types are allowed");
183
184 bool mask = false;
185 if constexpr (predicate == dealii::SIMDComparison::equal)
186 mask = (left == right);
187 else if constexpr (predicate == dealii::SIMDComparison::not_equal)
188 mask = (left != right);
189 else if constexpr (predicate == dealii::SIMDComparison::less_than)
190 mask = (left < right);
191 else if constexpr (predicate == dealii::SIMDComparison::less_than_or_equal)
192 mask = (left <= right);
193 else if constexpr (predicate == dealii::SIMDComparison::greater_than)
194 mask = (left > right);
195 else
196 mask = (left >= right);
197
198 return mask ? true_value : false_value;
199 }
200
201
208 template <dealii::SIMDComparison predicate, typename T, std::size_t width>
209 DEAL_II_ALWAYS_INLINE inline dealii::VectorizedArray<T, width>
210 compare_and_apply_mask(const dealii::VectorizedArray<T, width> &left,
211 const dealii::VectorizedArray<T, width> &right,
212 const dealii::VectorizedArray<T, width> &true_value,
213 const dealii::VectorizedArray<T, width> &false_value)
214 {
215 return dealii::compare_and_apply_mask<predicate>(
216 left, right, true_value, false_value);
217 }
218
219
227 template <int N, typename T>
228 DEAL_II_HOST_DEVICE_ALWAYS_INLINE T fixed_power(const T x)
229 {
230 return dealii::Utilities::fixed_power<N, T>(x);
231 }
232
233
239 template <typename T>
240 DEAL_II_HOST_DEVICE T pow(const T x, const T b);
241
242
248 template <typename T, std::size_t width>
249 dealii::VectorizedArray<T, width>
250 pow(const dealii::VectorizedArray<T, width> x, const T b);
251
252
259 template <typename T, std::size_t width>
260 dealii::VectorizedArray<T, width>
261 pow(const dealii::VectorizedArray<T, width> x,
262 const dealii::VectorizedArray<T, width> b);
263
264
265 template <>
266 DEAL_II_HOST_DEVICE_ALWAYS_INLINE float pow(const float x, const float b)
267 {
268#ifdef RYUJIN_DEVICE_COMPILATION_PASS
269 /* Call generic std::pow() implementation: */
270 return std::pow(x, b);
271#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
272 /* Use a custom pow implementation instead of std::pow(): */
273 return pow(dealii::VectorizedArray<float, 4>(x), b)[0];
274#else
275 /* Call generic std::pow() implementation: */
276 return std::pow(x, b);
277#endif
278 }
279
280
281 template <>
282 DEAL_II_HOST_DEVICE_ALWAYS_INLINE double pow(const double x, const double b)
283 {
284#ifdef RYUJIN_DEVICE_COMPILATION_PASS
285 /* Call generic std::pow() implementation */
286 return std::pow(x, b);
287#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
288 /* Use a custom pow implementation instead of std::pow(): */
289 return pow(dealii::VectorizedArray<double, 2>(x), b)[0];
290#else
291 /* Call generic std::pow() implementation */
292 return std::pow(x, b);
293#endif
294 }
295
296
300 enum class Bias {
304 none,
305
309 max,
310
314 min
315 };
316
317
323 template <typename T>
324 DEAL_II_HOST_DEVICE T fast_pow(const T x,
325 const T b,
326 const Bias bias = Bias::none);
327
328
334 template <typename T, std::size_t width>
335 dealii::VectorizedArray<T, width>
336 fast_pow(const dealii::VectorizedArray<T, width> x,
337 const T b,
338 const Bias bias = Bias::none);
339
340
347 template <typename T, std::size_t width>
348 dealii::VectorizedArray<T, width>
349 fast_pow(const dealii::VectorizedArray<T, width> x,
350 const dealii::VectorizedArray<T, width> b,
351 const Bias bias = Bias::none);
352
353
354 template <>
355 DEAL_II_HOST_DEVICE_ALWAYS_INLINE float
356 fast_pow(const float x, const float b, [[maybe_unused]] const Bias bias)
357 {
358#ifdef RYUJIN_DEVICE_COMPILATION_PASS
359 /* Call generic std::pow() implementation */
360 return std::pow(x, b);
361#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
362 /* Use a custom fast_pow implementation instead of std::pow(): */
363 return fast_pow(dealii::VectorizedArray<float, 4>(x), b, bias)[0];
364#else
365 /* Call generic std::pow() implementation */
366 return std::pow(x, b);
367#endif
368 }
369
370
371 template <>
372 DEAL_II_HOST_DEVICE_ALWAYS_INLINE double
373 fast_pow(const double x, const double b, [[maybe_unused]] const Bias bias)
374 {
375#ifdef RYUJIN_DEVICE_COMPILATION_PASS
376 /* Call generic std::pow() implementation (in single precision) */
377 return std::pow(static_cast<float>(x), static_cast<float>(b));
378#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__)
379 /* Use a custom fast_pow implementation instead of std::pow(): */
380 return fast_pow(dealii::VectorizedArray<double, 2>(x), b, bias)[0];
381#else
382 /* Call generic std::pow() implementation (in single precision) */
383 return std::pow(static_cast<float>(x), static_cast<float>(b));
384#endif
385 }
386
388
392
399 template <typename T, typename V>
400 DEAL_II_ALWAYS_INLINE inline T read_entry(const V &vector, unsigned int i)
401 {
402 static_assert(std::is_same_v<typename get_value_type<T>::type,
403 typename V::value_type>,
404 "type mismatch");
405 T result;
406
407 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
408 /* Non-vectorized sequential access. */
409 result = vector.local_element(i);
410 } else {
411 /* Vectorized fast access. index must be divisible by simd_length */
412 result.load(vector.get_values() + i);
413 }
414
415 return result;
416 }
417
418
423 template <typename T, typename T2>
424 DEAL_II_ALWAYS_INLINE inline T read_entry(const std::vector<T2> &vector,
425 unsigned int i)
426 {
427 if constexpr (std::is_same_v<typename get_value_type<T>::type, T2>) {
428 /* Optimized default for source and destination with same type: */
429
430 T result;
431 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
432 /* Non-vectorized sequential access. */
433 result = vector[i];
434 } else {
435 /* Vectorized fast access. index must be divisible by simd_length */
436 result.load(vector.data() + i);
437 }
438 return result;
439
440 } else {
441 /* Fallback for mismatched types (float vs double): */
442 T result;
443 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
444 result = vector[i];
445 } else {
446 // FIXME: suboptimal
447 for (unsigned int k = 0; k < T::size(); ++k)
448 result[k] = vector[i + k];
449 }
450 return result;
451 }
452 }
453
454
461 template <typename T, typename V>
462 DEAL_II_ALWAYS_INLINE inline T read_entry(const V &vector,
463 const unsigned int *js)
464 {
465 static_assert(std::is_same_v<typename get_value_type<T>::type,
466 typename V::value_type>,
467 "type mismatch");
468 T result;
469
470 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
471 /* Non-vectorized sequential access. */
472 result = vector.local_element(js[0]);
473 } else {
474 /* Vectorized fast access. index must be divisible by simd_length */
475 result.gather(vector.get_values(), js);
476 }
477
478 return result;
479 }
480
481
486 template <typename T, typename T2>
487 DEAL_II_ALWAYS_INLINE inline T read_entry(const std::vector<T2> &vector,
488 const unsigned int *js)
489 {
490 static_assert(std::is_same_v<typename get_value_type<T>::type, T2>,
491 "type mismatch");
492 T result;
493
494 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
495 /* Non-vectorized sequential access. */
496 result = vector[js[0]];
497 } else {
498 /* Vectorized fast access. index must be divisible by simd_length */
499 result.load(vector.data(), js);
500 }
501
502 return result;
503 }
504
505
511 template <typename T, typename V>
512 DEAL_II_ALWAYS_INLINE inline void
513 write_entry(V &vector, const T &values, unsigned int i)
514 {
515 static_assert(std::is_same_v<typename get_value_type<T>::type,
516 typename V::value_type>,
517 "type mismatch");
518
519 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
520 /* Non-vectorized sequential access. */
521 vector.local_element(i) = values;
522 } else {
523 /* Vectorized fast access. index must be divisible by simd_length */
524 values.store(vector.get_values() + i);
525 }
526 }
527
528
533 template <typename T, typename T2>
534 DEAL_II_ALWAYS_INLINE inline void
535 write_entry(std::vector<T2> &vector, const T &values, unsigned int i)
536 {
537 if constexpr (std::is_same_v<typename get_value_type<T>::type, T2>) {
538 /* Optimized default for source and destination with same type: */
539
540 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
541 /* Non-vectorized sequential access. */
542 vector[i] = values;
543 } else {
544 /* Vectorized fast access. index must be divisible by simd_length */
545 values.store(vector.data() + i);
546 }
547
548 } else {
549 /* Fallback for mismatched types (float vs double): */
550 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>) {
551 vector[i] = values;
552 } else {
553 // FIXME: suboptimal
554 for (unsigned int k = 0; k < T::size(); ++k)
555 vector[i + k] = values[k];
556 }
557 }
558 }
559
560
566 template <int rank, int dim, std::size_t width, typename Number>
567 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<rank, dim, Number>
569 const dealii::Tensor<rank, dim, dealii::VectorizedArray<Number, width>>
570 &vectorized,
571 const unsigned int k)
572 {
573 Assert(k < width, dealii::ExcMessage("Index past VectorizedArray width"));
574 dealii::Tensor<rank, dim, Number> result;
575 if constexpr (rank == 1) {
576 for (unsigned int d = 0; d < dim; ++d)
577 result[d] = vectorized[d][k];
578 } else {
579 for (unsigned int d = 0; d < dim; ++d)
580 result[d] = serialize_tensor(vectorized[d], k);
581 }
582 return result;
583 }
584
585
592 template <int rank, int dim, typename Number>
593 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<rank, dim, Number>
594 serialize_tensor(const dealii::Tensor<rank, dim, Number> &serial,
595 const unsigned int k [[maybe_unused]])
596 {
597 Assert(k == 0,
598 dealii::ExcMessage(
599 "The given index k must be zero for a serial tensor"));
600 return serial;
601 }
602
603
609 template <int rank, int dim, std::size_t width, typename Number>
610 DEAL_II_ALWAYS_INLINE inline void assign_serial_tensor(
611 dealii::Tensor<rank, dim, dealii::VectorizedArray<Number, width>> &result,
612 const dealii::Tensor<rank, dim, Number> &serial,
613 const unsigned int k)
614 {
615 Assert(k < width, dealii::ExcMessage("Index past VectorizedArray width"));
616 if constexpr (rank == 1) {
617 for (unsigned int d = 0; d < dim; ++d)
618 result[d][k] = serial[d];
619 } else {
620 for (unsigned int d = 0; d < dim; ++d)
621 assign_serial_tensor(result[d], serial[d], k);
622 }
623 }
624
625
632 template <int rank, int dim, typename Number>
633 DEAL_II_ALWAYS_INLINE inline void
634 assign_serial_tensor(dealii::Tensor<rank, dim, Number> &result,
635 const dealii::Tensor<rank, dim, Number> &serial,
636 const unsigned int k [[maybe_unused]])
637 {
638 Assert(k == 0,
639 dealii::ExcMessage(
640 "The given index k must be zero for a serial tensor"));
641
642 result = serial;
643 }
644
646
647} // 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:400
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number positive_part(const Number number)
Definition simd.h:149
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number negative_part(const Number number)
Definition simd.h:161
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:610
DEAL_II_ALWAYS_INLINE auto generate_iterators(Functor f) -> std::array< auto, length >
Definition simd.h:118
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:228
constexpr unsigned int get_stride_size
Definition simd.h:85
DEAL_II_ALWAYS_INLINE void increment_iterators(T &iterators)
Definition simd.h:131
DEAL_II_ALWAYS_INLINE void write_entry(V &vector, const T &values, unsigned int i)
Definition simd.h:513
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:176
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:568
Bias
Definition simd.h:300