56#ifndef OPENSWMM_ENGINE_SIMD_HPP
57#define OPENSWMM_ENGINE_SIMD_HPP
59#ifndef OPENSWMM_RESTRICT
61#define OPENSWMM_RESTRICT __restrict
63#define OPENSWMM_RESTRICT __restrict__
73#define OPENSWMM_IVDEP __pragma(loop(ivdep))
74#elif defined(__GNUC__) || defined(__clang__)
75#define OPENSWMM_IVDEP _Pragma("GCC ivdep")
90#if !defined(OPENSWMM_SIMD_SCALAR)
93#define OPENSWMM_SIMD_AVX2 1
94#define OPENSWMM_SIMD_WIDTH 4
95#elif defined(__ARM_NEON) || defined(__aarch64__)
97#define OPENSWMM_SIMD_NEON 1
98#define OPENSWMM_SIMD_WIDTH 2
100#define OPENSWMM_SIMD_SCALAR 1
101#define OPENSWMM_SIMD_WIDTH 1
104#if !defined(OPENSWMM_SIMD_WIDTH)
105#define OPENSWMM_SIMD_WIDTH 1
111#if defined(OPENSWMM_SIMD_AVX2)
114#if defined(OPENSWMM_SIMD_NEON)
117#if defined(OPENSWMM_SIMD_SCALAR)
121 "Exactly one SIMD backend must be active (AVX2, NEON, or SCALAR)");
141#if defined(OPENSWMM_SIMD_AVX2)
142 const std::size_t nv = n / 4;
143 const std::size_t nr = n % 4;
144 for (std::size_t i = 0; i < nv; ++i) {
145 __m256d va = _mm256_loadu_pd(a + i * 4);
146 __m256d vb = _mm256_loadu_pd(b + i * 4);
147 _mm256_storeu_pd(dst + i * 4, _mm256_add_pd(va, vb));
149 for (std::size_t i = nv * 4; i < n; ++i)
150 dst[i] = a[i] + b[i];
152#elif defined(OPENSWMM_SIMD_NEON)
153 const std::size_t nv = n / 2;
154 for (std::size_t i = 0; i < nv; ++i) {
155 float64x2_t va = vld1q_f64(a + i * 2);
156 float64x2_t vb = vld1q_f64(b + i * 2);
157 vst1q_f64(dst + i * 2, vaddq_f64(va, vb));
159 for (std::size_t i = nv * 2; i < n; ++i)
160 dst[i] = a[i] + b[i];
162 for (std::size_t i = 0; i < n; ++i)
163 dst[i] = a[i] + b[i];
179#if defined(OPENSWMM_SIMD_AVX2)
180 const std::size_t nv = n / 4;
181 for (std::size_t i = 0; i < nv; ++i) {
182 __m256d va = _mm256_loadu_pd(a + i * 4);
183 __m256d vb = _mm256_loadu_pd(b + i * 4);
184 _mm256_storeu_pd(dst + i * 4, _mm256_mul_pd(va, vb));
186 for (std::size_t i = nv * 4; i < n; ++i)
187 dst[i] = a[i] * b[i];
188#elif defined(OPENSWMM_SIMD_NEON)
189 const std::size_t nv = n / 2;
190 for (std::size_t i = 0; i < nv; ++i) {
191 float64x2_t va = vld1q_f64(a + i * 2);
192 float64x2_t vb = vld1q_f64(b + i * 2);
193 vst1q_f64(dst + i * 2, vmulq_f64(va, vb));
195 for (std::size_t i = nv * 2; i < n; ++i)
196 dst[i] = a[i] * b[i];
198 for (std::size_t i = 0; i < n; ++i)
199 dst[i] = a[i] * b[i];
211inline double min(
const double* a, std::size_t n)
noexcept {
213 double result = a[0];
214#if defined(OPENSWMM_SIMD_AVX2)
215 const std::size_t nv = n / 4;
217 __m256d vmin = _mm256_loadu_pd(a);
218 for (std::size_t i = 1; i < nv; ++i) {
219 vmin = _mm256_min_pd(vmin, _mm256_loadu_pd(a + i * 4));
222 __m128d hi = _mm256_extractf128_pd(vmin, 1);
223 __m128d lo = _mm256_castpd256_pd128(vmin);
224 __m128d m2 = _mm_min_pd(lo, hi);
225 __m128d m1 = _mm_min_pd(m2, _mm_shuffle_pd(m2, m2, 1));
226 result = _mm_cvtsd_f64(m1);
228 for (std::size_t i = nv * 4; i < n; ++i)
229 result = std::min(result, a[i]);
230#elif defined(OPENSWMM_SIMD_NEON)
231 const std::size_t nv = n / 2;
233 float64x2_t vmin = vld1q_f64(a);
234 for (std::size_t i = 1; i < nv; ++i) {
235 vmin = vminq_f64(vmin, vld1q_f64(a + i * 2));
238 result = std::min(vgetq_lane_f64(vmin, 0), vgetq_lane_f64(vmin, 1));
240 for (std::size_t i = nv * 2; i < n; ++i)
241 result = std::min(result, a[i]);
243 for (std::size_t i = 1; i < n; ++i)
244 result = std::min(result, a[i]);
257inline double max(
const double* a, std::size_t n)
noexcept {
259 double result = a[0];
260#if defined(OPENSWMM_SIMD_AVX2)
261 const std::size_t nv = n / 4;
263 __m256d vmax = _mm256_loadu_pd(a);
264 for (std::size_t i = 1; i < nv; ++i) {
265 vmax = _mm256_max_pd(vmax, _mm256_loadu_pd(a + i * 4));
268 __m128d hi = _mm256_extractf128_pd(vmax, 1);
269 __m128d lo = _mm256_castpd256_pd128(vmax);
270 __m128d m2 = _mm_max_pd(lo, hi);
271 __m128d m1 = _mm_max_pd(m2, _mm_shuffle_pd(m2, m2, 1));
272 result = _mm_cvtsd_f64(m1);
274 for (std::size_t i = nv * 4; i < n; ++i)
275 result = std::max(result, a[i]);
276#elif defined(OPENSWMM_SIMD_NEON)
277 const std::size_t nv = n / 2;
279 float64x2_t vmax = vld1q_f64(a);
280 for (std::size_t i = 1; i < nv; ++i) {
281 vmax = vmaxq_f64(vmax, vld1q_f64(a + i * 2));
283 result = std::max(vgetq_lane_f64(vmax, 0), vgetq_lane_f64(vmax, 1));
285 for (std::size_t i = nv * 2; i < n; ++i)
286 result = std::max(result, a[i]);
288 for (std::size_t i = 1; i < n; ++i)
289 result = std::max(result, a[i]);
303inline void clamp(
double* a,
double lo,
double hi, std::size_t n)
noexcept {
304#if defined(OPENSWMM_SIMD_AVX2)
305 __m256d vlo = _mm256_set1_pd(lo);
306 __m256d vhi = _mm256_set1_pd(hi);
307 const std::size_t nv = n / 4;
308 for (std::size_t i = 0; i < nv; ++i) {
309 __m256d v = _mm256_loadu_pd(a + i * 4);
310 v = _mm256_max_pd(vlo, _mm256_min_pd(vhi, v));
311 _mm256_storeu_pd(a + i * 4, v);
313 for (std::size_t i = nv * 4; i < n; ++i) {
314 a[i] = std::max(lo, std::min(hi, a[i]));
316#elif defined(OPENSWMM_SIMD_NEON)
317 float64x2_t vlo = vdupq_n_f64(lo);
318 float64x2_t vhi = vdupq_n_f64(hi);
319 const std::size_t nv = n / 2;
320 for (std::size_t i = 0; i < nv; ++i) {
321 float64x2_t v = vld1q_f64(a + i * 2);
322 v = vmaxq_f64(vlo, vminq_f64(vhi, v));
323 vst1q_f64(a + i * 2, v);
325 for (std::size_t i = nv * 2; i < n; ++i) {
326 a[i] = std::max(lo, std::min(hi, a[i]));
329 for (std::size_t i = 0; i < n; ++i) {
330 a[i] = std::max(lo, std::min(hi, a[i]));
347 std::size_t n)
noexcept {
349#if defined(OPENSWMM_SIMD_AVX2)
350 const std::size_t nv = n / 4;
352 __m256d vsum = _mm256_setzero_pd();
353 for (std::size_t i = 0; i < nv; ++i) {
354 __m256d va = _mm256_loadu_pd(a + i * 4);
355 __m256d vb = _mm256_loadu_pd(b + i * 4);
356 vsum = _mm256_fmadd_pd(va, vb, vsum);
359 __m128d hi = _mm256_extractf128_pd(vsum, 1);
360 __m128d lo = _mm256_castpd256_pd128(vsum);
361 __m128d s2 = _mm_add_pd(lo, hi);
362 __m128d s1 = _mm_add_pd(s2, _mm_shuffle_pd(s2, s2, 1));
363 result = _mm_cvtsd_f64(s1);
365 for (std::size_t i = nv * 4; i < n; ++i)
366 result += a[i] * b[i];
367#elif defined(OPENSWMM_SIMD_NEON)
368 const std::size_t nv = n / 2;
370 float64x2_t vsum = vdupq_n_f64(0.0);
371 for (std::size_t i = 0; i < nv; ++i) {
372 float64x2_t va = vld1q_f64(a + i * 2);
373 float64x2_t vb = vld1q_f64(b + i * 2);
374 vsum = vfmaq_f64(vsum, va, vb);
377 result = vgetq_lane_f64(vsum, 0) + vgetq_lane_f64(vsum, 1);
379 for (std::size_t i = nv * 2; i < n; ++i)
380 result += a[i] * b[i];
382 for (std::size_t i = 0; i < n; ++i)
383 result += a[i] * b[i];
406 std::size_t n)
noexcept {
408 for (std::size_t i = 0; i < n; ++i)
409 dst[i] = std::sqrt(a[i]);
422 std::size_t n)
noexcept {
424 for (std::size_t i = 0; i < n; ++i)
425 dst[i] = std::fabs(a[i]);
441 std::size_t n)
noexcept {
443 for (std::size_t i = 0; i < n; ++i)
444 dst[i] = a[i] * b[i] + c[i];
468 return x * std::sqrt(x);
478 return x * x * std::sqrt(x);
488 return x * std::cbrt(x * x);
498 return x * std::cbrt(x);
508 return std::cbrt(x * x);
511#if defined(OPENSWMM_SIMD_NEON)
532inline float64x2_t vcbrtq_f64(float64x2_t x)
noexcept {
537 float32x2_t xf = vcvt_f32_f64(x);
538 int32x2_t xi = vreinterpret_s32_f32(xf);
541 xi = vadd_s32(vshr_n_s32(vsub_s32(xi, vdup_n_s32(0x3F800000)), 2),
542 vdup_n_s32(0x3F800000 + (0x3F800000 / 3)));
543 float32x2_t cr = vreinterpret_f32_s32(xi);
546 float32x2_t cr2 = vmul_f32(cr, cr);
547 cr = vadd_f32(cr, vmul_f32(vdup_n_f32(0.333333333f), vsub_f32(vdiv_f32(xf, cr2), cr)));
548 cr2 = vmul_f32(cr, cr);
549 cr = vadd_f32(cr, vmul_f32(vdup_n_f32(0.333333333f), vsub_f32(vdiv_f32(xf, cr2), cr)));
552 float64x2_t c = vcvt_f64_f32(cr);
556 float64x2_t three = vdupq_n_f64(3.0);
557 float64x2_t onethird = vdupq_n_f64(0.333333333333333333);
558 for (
int i = 0; i < 3; ++i) {
559 float64x2_t c2 = vmulq_f64(c, c);
560 float64x2_t c3 = vmulq_f64(c2, c);
562 c = vsubq_f64(c, vmulq_f64(onethird, vdivq_f64(vsubq_f64(c3, x), vmulq_f64(three, c2))));
574inline float64x2_t batch_pow4_3(float64x2_t x)
noexcept {
575 return vmulq_f64(x, vcbrtq_f64(x));
585inline float64x2_t batch_pow2_3(float64x2_t x)
noexcept {
586 return vcbrtq_f64(vmulq_f64(x, x));
#define OPENSWMM_RESTRICT
Definition KernelPointers.hpp:9
#define OPENSWMM_IVDEP
Definition SIMD.hpp:77
#define OPENSWMM_SIMD_WIDTH
Scalar fallback.
Definition SIMD.hpp:101
double pow4_3(double x) noexcept
Compute the real 4/3 power using square or cube roots.
Definition SIMD.hpp:497
double pow3_2(double x) noexcept
Compute the real 3/2 power using square or cube roots.
Definition SIMD.hpp:467
double pow2_3(double x) noexcept
Compute the real 2/3 power using square or cube roots.
Definition SIMD.hpp:507
double pow5_2(double x) noexcept
Compute the real 5/2 power using square or cube roots.
Definition SIMD.hpp:477
double pow5_3(double x) noexcept
Compute the real 5/3 power using square or cube roots.
Definition SIMD.hpp:487
constexpr std::size_t lane_width() noexcept
Report the compile-time number of doubles per SIMD register.
Definition SIMD.hpp:392
void multiply(const double *OPENSWMM_RESTRICT a, const double *OPENSWMM_RESTRICT b, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Compute dst[i] = a[i] * b[i] over n doubles.
Definition SIMD.hpp:177
double min(const double *a, std::size_t n) noexcept
Reduce an array to its minimum value.
Definition SIMD.hpp:211
double max(const double *a, std::size_t n) noexcept
Reduce an array to its maximum value.
Definition SIMD.hpp:257
void sqrt_array(const double *OPENSWMM_RESTRICT a, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Compute dst[i] = sqrt(a[i]) over n doubles.
Definition SIMD.hpp:405
void fabs_array(const double *OPENSWMM_RESTRICT a, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Compute dst[i] = fabs(a[i]) over n doubles.
Definition SIMD.hpp:421
void add(const double *OPENSWMM_RESTRICT a, const double *OPENSWMM_RESTRICT b, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Compute dst[i] = a[i] + b[i] over n doubles.
Definition SIMD.hpp:139
double dot(const double *OPENSWMM_RESTRICT a, const double *OPENSWMM_RESTRICT b, std::size_t n) noexcept
Sum corresponding products from two arrays.
Definition SIMD.hpp:346
void fma_array(const double *OPENSWMM_RESTRICT a, const double *OPENSWMM_RESTRICT b, const double *OPENSWMM_RESTRICT c, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Compute dst[i] = a[i] * b[i] + c[i] over n doubles.
Definition SIMD.hpp:439
void clamp(double *a, double lo, double hi, std::size_t n) noexcept
Clamp each array element in place to an inclusive interval.
Definition SIMD.hpp:303