271 lines
5.0 KiB
C++
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;
|
|
}
|
|
|
|
} |