Files
dl/dl/base/dl_array_simd.h
T
2026-09-16 14:07:40 +08:00

271 lines
5.0 KiB
C++

/**
* @file dl_array_simd.h
* @brief 向量化容器
*
*
* @version 1.0
* @author lveyou
* @date 26-02-05
*
* @note 基于simde库
* @note 经测试,N较小时没有性能没有明显提升,且增加代码复杂度,还要求内存对齐(所以有特定需求才使用)
*/
#pragma once
#include "base/dl_array.h"
#include "base/dl_type_op.h"
#if (DL_CPU_ARCH == DL_CPU_ARCH_AMD64 || DL_CPU_ARCH == DL_CPU_ARCH_X86)
#include <simde/x86/sse.h>
#else
namespace dl
{
using simde__m128 = Array<float, 4>;
inline simde__m128 simde_mm_load_ps(float arr[4])
{
simde__m128 ret;
ret[0] = arr[0];
ret[1] = arr[1];
ret[2] = arr[2];
ret[3] = arr[3];
return ret;
}
inline simde__m128 simde_mm_set1_ps(float a)
{
simde__m128 ret;
ret[0] = a;
ret[1] = a;
ret[2] = a;
ret[3] = a;
return ret;
}
inline void simde_mm_store_ps(simde__m128& ret, const simde__m128& a)
{
ret = a;
}
inline simde__m128 simde_mm_add_ps(const simde__m128& a, const simde__m128& b)
{
return a + b;
}
inline simde__m128 simde_mm_sub_ps(const simde__m128& a, const simde__m128& b)
{
return a - b;
}
inline simde__m128 simde_mm_mul_ps(const simde__m128& a, const simde__m128& b)
{
return a * b;
}
inline simde__m128 simde_mm_div_ps(const simde__m128& a, const simde__m128& b)
{
return a / b;
}
}
#endif
namespace dl
{
template<typename T, size_t N>
requires std::is_arithmetic_v<T>
class alignas(16) ArrayS2;
template<size_t N>
ArrayS2<float, N> simd_m128u_op(simde__m128(*op)(simde__m128, simde__m128),
const ArrayS2<float, N>& a, const Array<float, 2>& b)
{
ArrayS2<float, N> ret;
if constexpr (N == 4)
{
simde__m128 vec_ax = simde_mm_load_ps(a.x);
simde__m128 vec_bx = simde_mm_set1_ps(b.x);
vec_ax = op(vec_ax, vec_bx);
simde_mm_store_ps(ret.x, vec_ax);
simde__m128 vec_ay = simde_mm_load_ps(a.y);
simde__m128 vec_by = simde_mm_set1_ps(b.y);
vec_ay = op(vec_ay, vec_by);
simde_mm_store_ps(ret.y, vec_ay);
}
else
{
static_assert(N == 4);
}
return ret;
}
}
namespace dl
{
template<typename T, size_t N>
requires std::is_arithmetic_v<T>
class alignas(16) ArrayS2
{
public:
union
{
T _data[N * 2];
struct
{
T x[N];
T y[N];
};
};
ArrayS2() = default;
// slow
constexpr Array<T, 2> Get(size_t i) const
{
return { x[i], y[i] };
}
constexpr Array<T, 2> Sum(size_t i, size_t j) const
{
Array<T, 2> ret;
ret[0] = x[i] + x[j];
ret[1] = y[i] + y[j];
return ret;
}
/*constexpr const Array<T, 2> operator[](size_t i) const
{
return { x[i], y[i] };
}*/
constexpr ArrayS2(std::initializer_list<Array<T, 2>> data)
{
size_t N_LIST = std::size(data);
for (size_t i = 0; i < N; ++i)
x[i] = i < N_LIST ? data.x : T{};
for (size_t i = 0; i < N; ++i)
y[i] = i < N_LIST ? data.y : T{};
}
};
template<typename T, std::size_t N>
constexpr ArrayS2<T, N>& operator+=(ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
a = a + b;
else
{
for (size_t i = 0; i < N; ++i)
{
a.x[i] += b[0];
a.y[i] += b[1];
}
}
return a;
}
template<typename T, std::size_t N>
constexpr ArrayS2<T, N> operator+(const ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
return simd_m128u_op(simde_mm_add_ps, a, b);
else
{
ArrayS2<T, N> ret = a;
ret += b;
return ret;
}
}
template<typename T, std::size_t N>
constexpr ArrayS2<T, N>& operator-=(ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
a = a - b;
else
{
for (size_t i = 0; i < N; ++i)
{
a.x[i] -= b[0];
a.y[i] -= b[1];
}
}
return a;
}
template<typename T, std::size_t N>
constexpr ArrayS2<T, N> operator-(const ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
return simd_m128u_op(simde_mm_sub_ps, a, b);
else
{
ArrayS2<T, N> ret = a;
ret -= b;
return ret;
}
}
template<typename T, std::size_t N>
constexpr ArrayS2<T, N>& operator*=(ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
a = a * b;
else
{
for (size_t i = 0; i < N; ++i)
{
a.x[i] *= b[0];
a.y[i] *= b[1];
}
}
return a;
}
template<typename T, std::size_t N>
constexpr ArrayS2<T, N> operator*(const ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
return simd_m128u_op(simde_mm_mul_ps, a, b);
else
{
ArrayS2<T, N> ret = a;
ret *= b;
return ret;
}
}
template<typename T, std::size_t N>
constexpr ArrayS2<T, N>& operator/=(ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
a = a / b;
else
{
for (size_t i = 0; i < N; ++i)
{
a.x[i] /= b[0];
a.y[i] /= b[1];
}
}
return a;
}
template<typename T, std::size_t N>
constexpr ArrayS2<T, N> operator/(const ArrayS2<T, N>& a, const Array<T, 2>& b)
{
if constexpr (N == 4)
return simd_m128u_op(simde_mm_div_ps, a, b);
else
{
ArrayS2<T, N> ret = a;
ret /= b;
return ret;
}
}
template<size_t M = 1, typename T, size_t N>
constexpr ArrayS2<T, N - M> toShorter(const ArrayS2<T, N>& a)
{
static_assert(M <= N);
ArrayS2<T, N - M> ret;
for (size_t i = 0; i < N - M; ++i)
{
ret.x[i] = a.x[i];
ret.y[i] = a.y[i];
}
return ret;
}
}