Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_SIMD.H
Go to the documentation of this file.
1#ifndef AMREX_SIMD_H_
2#define AMREX_SIMD_H_
3
4#include <AMReX_Config.H>
5
6#include <AMReX.H> // amrex::ignore_unused
7#include <AMReX_REAL.H>
8
9#ifdef AMREX_USE_SIMD
10// TODO make SIMD provider configurable: VIR (C++17 TS2) or C++26 (later)
11# include <vir/simd.h> // includes SIMD TS2 header <experimental/simd>
12# if __cplusplus >= 202002L
13# include <vir/simd_cvt.h>
14# endif
15#endif
16
17#include <cstdint>
18#include <type_traits>
19
20
21namespace amrex::simd
22{
23 // TODO make SIMD provider configurable: VIR (C++17 TS2) or C++26 (later)
24 // for https://en.cppreference.com/w/cpp/experimental/simd/simd_cast.html
25 namespace stdx {
26#ifdef AMREX_USE_SIMD
27 using namespace vir::stdx;
28# if __cplusplus >= 202002L
29 using vir::cvt;
30# endif
31
58 template <typename T, typename Abi>
60 vir::stdx::simd<T, Abi> select (
61 typename vir::stdx::simd<T, Abi>::mask_type const& mask,
62 vir::stdx::simd<T, Abi> const& true_val,
63 vir::stdx::simd<T, Abi> const& false_val)
64 {
65 vir::stdx::simd<T, Abi> result = false_val;
66 where(mask, result) = true_val;
67 return result;
68 }
69#else
70 // fallback implementations for functions that are commonly used in portable code paths
71
84 bool any_of (bool const v) { return v; }
85
87 namespace detail {
88 template <typename T>
89 struct where_expression {
90 bool mask;
91 T* value;
92
94 where_expression& operator= (T const& new_val)
95 {
96 if (mask) { *value = new_val; }
97 return *this;
98 }
99 };
100 }
102
118 template <typename T>
120 detail::where_expression<T> where (bool const mask, T& value)
121 {
122 return {mask, &value};
123 }
124
130 template <typename T>
132 T select (bool const mask, T const& true_val, T const& false_val)
133 {
134 return mask ? true_val : false_val;
135 }
136
150 template <typename T>
152 T reduce (T const& v) { return v; }
153
158 template <typename T, typename BinaryOperation>
160 T reduce (T const& v, BinaryOperation const&) { return v; }
161
166 template <typename T>
168 T hmin (T const& v) { return v; }
169
174 template <typename T>
176 T hmax (T const& v) { return v; }
177
182 template <typename T>
184 T min (T const& a, T const& b) { return (b < a) ? b : a; }
185
190 template <typename T>
192 T max (T const& a, T const& b) { return (a < b) ? b : a; }
193#endif
194 }
195
196 // TODO: move to AMReX_REAL.H?
197
198#ifdef AMREX_USE_SIMD
199 // TODO: not sure why std::experimental::simd_abi::native<T> does not work, so we use this long version
200 constexpr auto native_simd_size_real = stdx::native_simd<amrex::Real>::size();
201 constexpr auto native_simd_size_particlereal = stdx::native_simd<amrex::ParticleReal>::size();
202
203 // Note: to make use of not only vector registers but also ILP, user might want to use * 2 or more of the native size
204 // for selected compute kernels.
205 // TODO Check if a default with * 2 or similar is sensible.
206 template<int SIMD_WIDTH = native_simd_size_real>
207 using SIMDReal = stdx::fixed_size_simd<amrex::Real, SIMD_WIDTH>;
208
209 template<int SIMD_WIDTH = native_simd_size_particlereal>
210 using SIMDParticleReal = stdx::fixed_size_simd<amrex::ParticleReal, SIMD_WIDTH>;
211
212 // Type that has the same number of int SIMD elements as the SIMDParticleReal type
213 template<typename T_ParticleReal = SIMDParticleReal<>>
214 using SIMDInt = stdx::rebind_simd_t<int, T_ParticleReal>;
215
216 // Type that has the same number of IdCpu SIMD elements as the SIMDParticleReal type
217 template<typename T_ParticleReal = SIMDParticleReal<>>
218 using SIMDIdCpu = stdx::rebind_simd_t<std::uint64_t, T_ParticleReal>;
219#else
220 constexpr auto native_simd_size_real = 1;
222
223 template<int SIMD_WIDTH = native_simd_size_real>
225
226 template<int SIMD_WIDTH = native_simd_size_particlereal>
228
229 // Type that has the same number of int SIMD elements as the SIMDParticleReal type
230 template<typename T_ParticleReal = SIMDParticleReal<>>
231 using SIMDInt = int;
232
233 // Type that has the same number of IdCpu SIMD elements as the SIMDParticleReal type
234 template<typename T_ParticleReal = SIMDParticleReal<>>
235 using SIMDIdCpu = std::uint64_t;
236#endif
237
239 namespace detail {
240 struct InternalVectorized {};
241 }
243
270 template<int SIMD_WIDTH = native_simd_size_real>
271 struct
272 Vectorized : detail::InternalVectorized
273 {
275 static constexpr int simd_width = SIMD_WIDTH;
276 };
277
282 template<typename T>
283 constexpr bool is_vectorized = std::is_base_of_v<detail::InternalVectorized, T>;
284
305 template<typename R, typename... Args>
306 constexpr bool is_nth_arg_non_const (R(*)(Args...), int n)
307 {
308 constexpr bool val_arr[sizeof...(Args)] {!std::is_const_v<std::remove_reference_t<Args>>...};
309 return val_arr[n];
310 }
311 // same for functors (const/non-const ::operator() members)
312 template<typename C, typename R, typename... Args>
313 constexpr bool is_nth_arg_non_const (R(C::*)(Args...), int n)
314 {
315 constexpr bool val_arr[sizeof...(Args)] {!std::is_const_v<std::remove_reference_t<Args>>...};
316 return val_arr[n];
317 }
318 template<typename C, typename R, typename... Args>
319 constexpr bool is_nth_arg_non_const (R(C::*)(Args...) const, int n)
320 {
321 constexpr bool val_arr[sizeof...(Args)] {!std::is_const_v<std::remove_reference_t<Args>>...};
322 return val_arr[n];
323 }
324
338 template <typename T, typename IndexType>
340 decltype(auto) load_1d (T* ptr, IndexType const i)
341 {
342 if constexpr (std::is_integral_v<IndexType>) {
343 return ptr[i];
344 } else if constexpr (IndexType::width == 1) {
345 return ptr[i.index];
346 } else {
347#ifdef AMREX_USE_SIMD
348 using DataType = stdx::fixed_size_simd<std::decay_t<T>, IndexType::width>;
349
350 // initialize vector register
351 // TODO stdx::vector_aligned needs alignment guarantees
352 // https://github.com/AMReX-Codes/amrex/issues/4592
353 // https://en.cppreference.com/w/cpp/experimental/simd/simd/copy_from
354 DataType val;
355 val.copy_from(&ptr[i.index], stdx::element_aligned);
356 return val;
357
358#else
359 static_assert(IndexType::width == 1, "SIMD width must be 1 for non-SIMD builds");
360 return ptr[i.index];
361#endif
362 }
363 }
364
394 template <auto P_Method, int N, bool ForceWriteback = false,
395 typename T, typename IndexType, typename ValType>
397 void store_1d (
398 ValType const & AMREX_RESTRICT val,
399 T * const AMREX_RESTRICT ptr,
400 IndexType const i
401 )
402 {
403 // SIMD uses special vector register types in ValType that need to be copied back to RAM array type T
404 if constexpr (!std::is_same_v<ValType, T>) {
405 if constexpr (ForceWriteback || amrex::simd::is_nth_arg_non_const(P_Method, N)) {
406#ifdef AMREX_USE_SIMD
407 // write back to memory
408 // TODO stdx::vector_aligned needs alignment guarantees
409 // https://github.com/AMReX-Codes/amrex/issues/4592
410 // https://en.cppreference.com/w/cpp/experimental/simd/simd/copy_from
411 val.copy_to(&ptr[i.index], amrex::simd::stdx::element_aligned);
412#else
413 amrex::Abort("store_1d: ValType val must alias T ptr data (to make this a no-OP).");
414#endif
415 }
416 }
417 amrex::ignore_unused(val, ptr, i);
418 }
419
420} // namespace amrex::simd
421
422#endif
Runtime initialization/finalization helpers and global diagnostics.
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
#define AMREX_RESTRICT
Definition AMReX_Extension.H:37
#define AMREX_GPU_HOST_DEVICE
Definition AMReX_GpuQualifiers.H:20
Array4< int const > mask
Definition AMReX_InterpFaceRegister.cpp:93
amrex_real Real
Floating Point Type for Fields.
Definition AMReX_REAL.H:79
amrex_particle_real ParticleReal
Floating Point Type for Particles.
Definition AMReX_REAL.H:90
__host__ __device__ T max(T const &a, T const &b)
Definition AMReX_SIMD.H:192
__host__ __device__ T hmin(T const &v)
Definition AMReX_SIMD.H:168
__host__ __device__ T min(T const &a, T const &b)
Definition AMReX_SIMD.H:184
__host__ __device__ bool any_of(bool const v)
Definition AMReX_SIMD.H:84
__host__ __device__ T hmax(T const &v)
Definition AMReX_SIMD.H:176
__host__ __device__ detail::where_expression< T > where(bool const mask, T &value)
Definition AMReX_SIMD.H:120
__host__ __device__ T reduce(T const &v)
Definition AMReX_SIMD.H:152
__host__ __device__ T select(bool const mask, T const &true_val, T const &false_val)
Definition AMReX_SIMD.H:132
Definition AMReX_SIMD.H:22
__host__ __device__ void store_1d(ValType const &__restrict__ val, T *const __restrict__ ptr, IndexType const i)
Definition AMReX_SIMD.H:397
std::uint64_t SIMDIdCpu
Definition AMReX_SIMD.H:235
constexpr auto native_simd_size_real
Definition AMReX_SIMD.H:220
int SIMDInt
Definition AMReX_SIMD.H:231
constexpr bool is_vectorized
Definition AMReX_SIMD.H:283
constexpr bool is_nth_arg_non_const(R(*)(Args...), int n)
Definition AMReX_SIMD.H:306
amrex::ParticleReal SIMDParticleReal
Definition AMReX_SIMD.H:227
__host__ __device__ decltype(auto) load_1d(T *ptr, IndexType const i)
Definition AMReX_SIMD.H:340
constexpr auto native_simd_size_particlereal
Definition AMReX_SIMD.H:221
amrex::Real SIMDReal
Definition AMReX_SIMD.H:224
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:259
IndexTypeND< 3 > IndexType
IndexType is an alias for amrex::IndexTypeND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:41
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:241
const int[]
Definition AMReX_BLProfiler.cpp:1664
Definition AMReX_SIMD.H:273