50#ifndef OPENSWMM_ENGINE_SIMD_HPP
51#define OPENSWMM_ENGINE_SIMD_HPP
53#ifndef OPENSWMM_RESTRICT
55# define OPENSWMM_RESTRICT __restrict
57# define OPENSWMM_RESTRICT __restrict__
67# define OPENSWMM_IVDEP __pragma(loop(ivdep))
68# elif defined(__GNUC__) || defined(__clang__)
69# define OPENSWMM_IVDEP _Pragma("GCC ivdep")
71# define OPENSWMM_IVDEP
84#if !defined(OPENSWMM_SIMD_SCALAR)
86# include <immintrin.h>
87# define OPENSWMM_SIMD_AVX2 1
88# define OPENSWMM_SIMD_WIDTH 4
89# elif defined(__ARM_NEON) || defined(__aarch64__)
91# define OPENSWMM_SIMD_NEON 1
92# define OPENSWMM_SIMD_WIDTH 2
94# define OPENSWMM_SIMD_SCALAR 1
95# define OPENSWMM_SIMD_WIDTH 1
98# if !defined(OPENSWMM_SIMD_WIDTH)
99# define OPENSWMM_SIMD_WIDTH 1
106#if defined(OPENSWMM_SIMD_AVX2)
109#if defined(OPENSWMM_SIMD_NEON)
112#if defined(OPENSWMM_SIMD_SCALAR)
116 "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) dst[i] = a[i] + b[i];
151#elif defined(OPENSWMM_SIMD_NEON)
152 const std::size_t nv = n / 2;
153 for (std::size_t i = 0; i < nv; ++i) {
154 float64x2_t va = vld1q_f64(a + i * 2);
155 float64x2_t vb = vld1q_f64(b + i * 2);
156 vst1q_f64(dst + i * 2, vaddq_f64(va, vb));
158 for (std::size_t i = nv * 2; i < n; ++i) dst[i] = a[i] + b[i];
160 for (std::size_t i = 0; i < n; ++i) dst[i] = a[i] + b[i];
173#if defined(OPENSWMM_SIMD_AVX2)
174 const std::size_t nv = n / 4;
175 for (std::size_t i = 0; i < nv; ++i) {
176 __m256d va = _mm256_loadu_pd(a + i * 4);
177 __m256d vb = _mm256_loadu_pd(b + i * 4);
178 _mm256_storeu_pd(dst + i * 4, _mm256_mul_pd(va, vb));
180 for (std::size_t i = nv * 4; i < n; ++i) dst[i] = a[i] * b[i];
181#elif defined(OPENSWMM_SIMD_NEON)
182 const std::size_t nv = n / 2;
183 for (std::size_t i = 0; i < nv; ++i) {
184 float64x2_t va = vld1q_f64(a + i * 2);
185 float64x2_t vb = vld1q_f64(b + i * 2);
186 vst1q_f64(dst + i * 2, vmulq_f64(va, vb));
188 for (std::size_t i = nv * 2; i < n; ++i) dst[i] = a[i] * b[i];
190 for (std::size_t i = 0; i < n; ++i) dst[i] = a[i] * b[i];
201inline double min(
const double* a, std::size_t n)
noexcept {
203 double result = a[0];
204#if defined(OPENSWMM_SIMD_AVX2)
205 const std::size_t nv = n / 4;
207 __m256d vmin = _mm256_loadu_pd(a);
208 for (std::size_t i = 1; i < nv; ++i) {
209 vmin = _mm256_min_pd(vmin, _mm256_loadu_pd(a + i * 4));
212 __m128d hi = _mm256_extractf128_pd(vmin, 1);
213 __m128d lo = _mm256_castpd256_pd128(vmin);
214 __m128d m2 = _mm_min_pd(lo, hi);
215 __m128d m1 = _mm_min_pd(m2, _mm_shuffle_pd(m2, m2, 1));
216 result = _mm_cvtsd_f64(m1);
218 for (std::size_t i = nv * 4; i < n; ++i) result = std::min(result, a[i]);
219#elif defined(OPENSWMM_SIMD_NEON)
220 const std::size_t nv = n / 2;
222 float64x2_t vmin = vld1q_f64(a);
223 for (std::size_t i = 1; i < nv; ++i) {
224 vmin = vminq_f64(vmin, vld1q_f64(a + i * 2));
227 result = std::min(vgetq_lane_f64(vmin, 0), vgetq_lane_f64(vmin, 1));
229 for (std::size_t i = nv * 2; i < n; ++i) result = std::min(result, a[i]);
231 for (std::size_t i = 1; i < n; ++i) result = std::min(result, a[i]);
239inline double max(
const double* a, std::size_t n)
noexcept {
241 double result = a[0];
242#if defined(OPENSWMM_SIMD_AVX2)
243 const std::size_t nv = n / 4;
245 __m256d vmax = _mm256_loadu_pd(a);
246 for (std::size_t i = 1; i < nv; ++i) {
247 vmax = _mm256_max_pd(vmax, _mm256_loadu_pd(a + i * 4));
250 __m128d hi = _mm256_extractf128_pd(vmax, 1);
251 __m128d lo = _mm256_castpd256_pd128(vmax);
252 __m128d m2 = _mm_max_pd(lo, hi);
253 __m128d m1 = _mm_max_pd(m2, _mm_shuffle_pd(m2, m2, 1));
254 result = _mm_cvtsd_f64(m1);
256 for (std::size_t i = nv * 4; i < n; ++i) result = std::max(result, a[i]);
257#elif defined(OPENSWMM_SIMD_NEON)
258 const std::size_t nv = n / 2;
260 float64x2_t vmax = vld1q_f64(a);
261 for (std::size_t i = 1; i < nv; ++i) {
262 vmax = vmaxq_f64(vmax, vld1q_f64(a + i * 2));
264 result = std::max(vgetq_lane_f64(vmax, 0), vgetq_lane_f64(vmax, 1));
266 for (std::size_t i = nv * 2; i < n; ++i) result = std::max(result, a[i]);
268 for (std::size_t i = 1; i < n; ++i) result = std::max(result, a[i]);
281inline void clamp(
double* a,
double lo,
double hi, std::size_t n)
noexcept {
282#if defined(OPENSWMM_SIMD_AVX2)
283 __m256d vlo = _mm256_set1_pd(lo);
284 __m256d vhi = _mm256_set1_pd(hi);
285 const std::size_t nv = n / 4;
286 for (std::size_t i = 0; i < nv; ++i) {
287 __m256d v = _mm256_loadu_pd(a + i * 4);
288 v = _mm256_max_pd(vlo, _mm256_min_pd(vhi, v));
289 _mm256_storeu_pd(a + i * 4, v);
291 for (std::size_t i = nv * 4; i < n; ++i) {
292 a[i] = std::max(lo, std::min(hi, a[i]));
294#elif defined(OPENSWMM_SIMD_NEON)
295 float64x2_t vlo = vdupq_n_f64(lo);
296 float64x2_t vhi = vdupq_n_f64(hi);
297 const std::size_t nv = n / 2;
298 for (std::size_t i = 0; i < nv; ++i) {
299 float64x2_t v = vld1q_f64(a + i * 2);
300 v = vmaxq_f64(vlo, vminq_f64(vhi, v));
301 vst1q_f64(a + i * 2, v);
303 for (std::size_t i = nv * 2; i < n; ++i) {
304 a[i] = std::max(lo, std::min(hi, a[i]));
307 for (std::size_t i = 0; i < n; ++i) {
308 a[i] = std::max(lo, std::min(hi, a[i]));
330#if defined(OPENSWMM_SIMD_AVX2)
331 const std::size_t nv = n / 4;
333 __m256d vsum = _mm256_setzero_pd();
334 for (std::size_t i = 0; i < nv; ++i) {
335 __m256d va = _mm256_loadu_pd(a + i * 4);
336 __m256d vb = _mm256_loadu_pd(b + i * 4);
337 vsum = _mm256_fmadd_pd(va, vb, vsum);
340 __m128d hi = _mm256_extractf128_pd(vsum, 1);
341 __m128d lo = _mm256_castpd256_pd128(vsum);
342 __m128d s2 = _mm_add_pd(lo, hi);
343 __m128d s1 = _mm_add_pd(s2, _mm_shuffle_pd(s2, s2, 1));
344 result = _mm_cvtsd_f64(s1);
346 for (std::size_t i = nv * 4; i < n; ++i) result += a[i] * b[i];
347#elif defined(OPENSWMM_SIMD_NEON)
348 const std::size_t nv = n / 2;
350 float64x2_t vsum = vdupq_n_f64(0.0);
351 for (std::size_t i = 0; i < nv; ++i) {
352 float64x2_t va = vld1q_f64(a + i * 2);
353 float64x2_t vb = vld1q_f64(b + i * 2);
354 vsum = vfmaq_f64(vsum, va, vb);
357 result = vgetq_lane_f64(vsum, 0) + vgetq_lane_f64(vsum, 1);
359 for (std::size_t i = nv * 2; i < n; ++i) result += a[i] * b[i];
361 for (std::size_t i = 0; i < n; ++i) result += a[i] * b[i];
384 for (std::size_t i = 0; i < n; ++i) dst[i] = std::sqrt(a[i]);
396 for (std::size_t i = 0; i < n; ++i) dst[i] = std::fabs(a[i]);
410 for (std::size_t i = 0; i < n; ++i) dst[i] = a[i] * b[i] + c[i];
429 return x * std::sqrt(x);
434 return x * x * std::sqrt(x);
439 return x * std::cbrt(x * x);
444 return x * std::cbrt(x);
449 return std::cbrt(x * x);
452#if defined(OPENSWMM_SIMD_NEON)
469inline float64x2_t vcbrtq_f64(float64x2_t x)
noexcept {
474 float32x2_t xf = vcvt_f32_f64(x);
475 int32x2_t xi = vreinterpret_s32_f32(xf);
478 xi = vadd_s32(vshr_n_s32(vsub_s32(xi, vdup_n_s32(0x3F800000)), 2),
479 vdup_n_s32(0x3F800000 + (0x3F800000 / 3)));
480 float32x2_t cr = vreinterpret_f32_s32(xi);
483 float32x2_t cr2 = vmul_f32(cr, cr);
484 cr = vadd_f32(cr, vmul_f32(vdup_n_f32(0.333333333f),
485 vsub_f32(vdiv_f32(xf, cr2), cr)));
486 cr2 = vmul_f32(cr, cr);
487 cr = vadd_f32(cr, vmul_f32(vdup_n_f32(0.333333333f),
488 vsub_f32(vdiv_f32(xf, cr2), cr)));
491 float64x2_t c = vcvt_f64_f32(cr);
495 float64x2_t three = vdupq_n_f64(3.0);
496 float64x2_t onethird = vdupq_n_f64(0.333333333333333333);
497 for (
int i = 0; i < 3; ++i) {
498 float64x2_t c2 = vmulq_f64(c, c);
499 float64x2_t c3 = vmulq_f64(c2, c);
501 c = vsubq_f64(c, vmulq_f64(onethird,
502 vdivq_f64(vsubq_f64(c3, x),
503 vmulq_f64(three, c2))));
512inline float64x2_t batch_pow4_3(float64x2_t x)
noexcept {
513 return vmulq_f64(x, vcbrtq_f64(x));
520inline float64x2_t batch_pow2_3(float64x2_t x)
noexcept {
521 return vcbrtq_f64(vmulq_f64(x, x));
#define OPENSWMM_IVDEP
Definition SIMD.hpp:71
#define OPENSWMM_SIMD_WIDTH
Scalar fallback.
Definition SIMD.hpp:95
#define OPENSWMM_RESTRICT
Definition XSectBatch.hpp:57
double pow4_3(double x) noexcept
pow(x, 4/3) = x * cbrt(x). Manning friction.
Definition SIMD.hpp:443
double pow3_2(double x) noexcept
pow(x, 3/2) = x * sqrt(x). Weir TRANSVERSE / TRAPEZOIDAL.
Definition SIMD.hpp:428
double pow2_3(double x) noexcept
pow(x, 2/3) = cbrt(x²). Manning normal-flow.
Definition SIMD.hpp:448
double pow5_2(double x) noexcept
pow(x, 5/2) = x² * sqrt(x). Weir V-NOTCH.
Definition SIMD.hpp:433
double pow5_3(double x) noexcept
pow(x, 5/3) = x * cbrt(x²). Weir SIDEFLOW (legacy 1.67 exponent).
Definition SIMD.hpp:438
constexpr std::size_t lane_width() noexcept
Returns the SIMD lane width (doubles per register on this platform).
Definition SIMD.hpp:369
void multiply(const double *OPENSWMM_RESTRICT a, const double *OPENSWMM_RESTRICT b, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Element-wise multiplication: dst[i] = a[i] * b[i].
Definition SIMD.hpp:167
double min(const double *a, std::size_t n) noexcept
Find the minimum value in an array.
Definition SIMD.hpp:201
double max(const double *a, std::size_t n) noexcept
Find the maximum value in an array.
Definition SIMD.hpp:239
void sqrt_array(const double *OPENSWMM_RESTRICT a, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Element-wise sqrt: dst[i] = sqrt(a[i]). Written as a simple loop; compilers auto-vectorize to platfor...
Definition SIMD.hpp:378
void fabs_array(const double *OPENSWMM_RESTRICT a, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Element-wise fabs: dst[i] = fabs(a[i]).
Definition SIMD.hpp:390
void add(const double *OPENSWMM_RESTRICT a, const double *OPENSWMM_RESTRICT b, double *OPENSWMM_RESTRICT dst, std::size_t n) noexcept
Element-wise addition: dst[i] = a[i] + b[i].
Definition SIMD.hpp:135
double dot(const double *OPENSWMM_RESTRICT a, const double *OPENSWMM_RESTRICT b, std::size_t n) noexcept
Dot product of two arrays: sum(a[i] * b[i]).
Definition SIMD.hpp:324
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
Element-wise fused multiply-add: dst[i] = a[i] * b[i] + c[i].
Definition SIMD.hpp:402
void clamp(double *a, double lo, double hi, std::size_t n) noexcept
Clamp all elements of an array to [lo, hi].
Definition SIMD.hpp:281