35#ifndef NEKTAR_LIB_LIBUTILITES_SIMDLIB_TINYSIMD_H
36#define NEKTAR_LIB_LIBUTILITES_SIMDLIB_TINYSIMD_H
67 static constexpr unsigned int width = Width;
70template <
typename T,
typename =
void>
77 : std::integral_constant<unsigned int, T::width>
92 static_assert(!std::is_void_v<type>,
"unsupported SIMD type");
112 static_assert(
value > 0,
"unsupported SIMD type");
121#if defined(__clang__)
122#define TINYSIMD_PRAGMA_UNROLL _Pragma("clang loop unroll(full)")
123#elif defined(__GNUC__)
124#define TINYSIMD_PRAGMA_UNROLL _Pragma("GCC unroll 16")
126#define TINYSIMD_PRAGMA_UNROLL
130template <
typename ScalarType,
int width = 0,
131 template <
typename,
int>
class abi = abi::default_abi>
132using simd =
typename abi<ScalarType, width>::type;
139 using type = std::conditional_t<(
sizeof(
typename T::scalarType) <= 4),
140 std::uint32_t, std::uint64_t>;
146 using type =
typename T::scalarIndexType;
152template <
typename SimdType,
unsigned int W
idth>
struct long_simd
154 static_assert(is_vector_floating_point_v<SimdType>,
155 "longsimd requires a floating-point SIMD chunk type");
157 static constexpr unsigned int width = Width;
158 static constexpr unsigned int alignment = SimdType::alignment;
163 "longsimd width must be at least one native SIMD chunk");
165 "longsimd width must be a multiple of the chunk width");
175 "longsimd requires tightly packed SIMD chunk storage");
227 template <
class flag,
228 typename std::enable_if<is_load_tag_v<flag>,
bool>::type = 0>
247 template <
class flag,
248 typename std::enable_if<is_load_tag_v<flag>,
bool>::type = 0>
263 template <
typename IndexType>
269 _data[i].gather(p, indices[i]);
273 template <
typename IndexType>
279 _data[i].scatter(out, indices[i]);
316 template <
typename U,
317 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
323 template <
typename U,
324 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
331 typename U,
typename V,
332 typename std::enable_if<
333 std::is_arithmetic_v<U> && std::is_arithmetic_v<V>,
bool>::type = 0>
371 template <
typename U,
372 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
396 template <
typename U,
397 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
421 template <
typename U,
422 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
446 template <
typename U,
447 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
454template <
typename ScalarType,
int width,
455 template <
typename,
int>
class abi_policy>
470 "longsimd width must be at least the native SIMD width; use simd<> "
471 "for narrower types");
472 static_assert(
requested_width %
static_cast<int>(native_type::width) == 0,
473 "longsimd width must be a multiple of the native SIMD width");
475 using type = std::conditional_t<
482template <
typename ScalarType,
int width = 0,
487#if defined(__AVX2__) && defined(NEKTAR_ENABLE_SIMD_AVX2)
491#if defined(__AVX512F__) && defined(NEKTAR_ENABLE_SIMD_AVX512)
495template <
typename SimdType,
unsigned int W
idth>
504template <
typename SimdType,
unsigned int W
idth>
513template <
typename SimdType,
unsigned int W
idth>
522template <
typename SimdType,
unsigned int Width,
typename U,
523 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
531template <
typename SimdType,
unsigned int Width,
typename U,
532 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
537 return typename vec_t::chunkType(
538 static_cast<typename vec_t::scalarType
>(lhs)) +
542template <
typename SimdType,
unsigned int W
idth>
551template <
typename SimdType,
unsigned int W
idth>
560template <
typename SimdType,
unsigned int W
idth>
566 for (
size_t i = 0; i < rhs.
numChunks; ++i)
573template <
typename SimdType,
unsigned int Width,
typename U,
574 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
582template <
typename SimdType,
unsigned int Width,
typename U,
583 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
588 return typename vec_t::chunkType(
589 static_cast<typename vec_t::scalarType
>(lhs)) -
593template <
typename SimdType,
unsigned int W
idth>
598 for (
size_t i = 0; i < in.
numChunks; ++i)
605template <
typename SimdType,
unsigned int W
idth>
614template <
typename SimdType,
unsigned int W
idth>
623template <
typename SimdType,
unsigned int W
idth>
632template <
typename SimdType,
unsigned int Width,
typename U,
633 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
641template <
typename SimdType,
unsigned int Width,
typename U,
642 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
647 return typename vec_t::chunkType(
648 static_cast<typename vec_t::scalarType
>(lhs)) *
652template <
typename SimdType,
unsigned int W
idth>
661template <
typename SimdType,
unsigned int W
idth>
670template <
typename SimdType,
unsigned int W
idth>
676 for (
size_t i = 0; i < rhs.
numChunks; ++i)
683template <
typename SimdType,
unsigned int Width,
typename U,
684 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
692template <
typename SimdType,
unsigned int Width,
typename U,
693 typename std::enable_if<std::is_arithmetic_v<U>,
bool>::type = 0>
698 return typename vec_t::chunkType(
699 static_cast<typename vec_t::scalarType
>(lhs)) /
703template <
typename SimdType,
unsigned int W
idth>
708 for (
size_t i = 0; i < in.
numChunks; ++i)
715template <
typename SimdType,
unsigned int W
idth>
720 for (
size_t i = 0; i < in.
numChunks; ++i)
727template <
typename SimdType,
unsigned int W
idth>
733 for (
size_t i = 0; i < lhs.
numChunks; ++i)
740template <
typename SimdType,
unsigned int W
idth>
746 for (
size_t i = 0; i < lhs.
numChunks; ++i)
753template <
typename SimdType,
unsigned int W
idth>
758 for (
size_t i = 0; i < in.
numChunks; ++i)
765template <
typename SimdType,
unsigned int W
idth>
768 const std::uint32_t dataLen,
774 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp;
775 for (
size_t i = 0; i < dataLen; ++i)
777 for (
size_t j = 0; j < vec_t::width; ++j)
779 tmp[j] = in[i + j * dataLen];
785template <
typename SimdType,
unsigned int W
idth>
788 const std::uint32_t dataLen,
const std::uint32_t skipPads,
794 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp;
795 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp1;
796 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp2;
797 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp3;
799 size_t nBlocks = dataLen / 4;
800 const vec_t zero{
static_cast<typename vec_t::scalarType
>(0)};
802 for (
size_t i = 0; i < nBlocks; ++i)
809 for (
size_t j = 0; j < vec_t::width - skipPads; ++j)
811 tmp[j] = in[j * dataLen + 4 * i];
812 tmp1[j] = in[j * dataLen + 4 * i + 1];
813 tmp2[j] = in[j * dataLen + 4 * i + 2];
814 tmp3[j] = in[j * dataLen + 4 * i + 3];
817 out[4 * i].
load(tmp);
818 out[4 * i + 1].load(tmp1);
819 out[4 * i + 2].load(tmp2);
820 out[4 * i + 3].load(tmp3);
823 for (
size_t i = nBlocks * 4; i < dataLen; ++i)
826 for (
size_t j = 0; j < vec_t::width - skipPads; ++j)
828 tmp[j] = in[i + j * dataLen];
834template <
typename SimdType,
unsigned int W
idth>
837 std::uint32_t dataLen,
842 using index_t =
typename vec_t::chunkIndexType;
844 alignas(index_t::alignment)
typename index_t::scalarArray tmp;
845 index_t index[vec_t::numChunks];
847 for (
size_t chunk = 0; chunk < vec_t::numChunks; ++chunk)
849 for (
size_t lane = 0; lane < index_t::width; ++lane)
851 tmp[lane] =
static_cast<typename vec_t::scalarIndexType
>(
852 (chunk * index_t::width + lane) * dataLen);
854 index[chunk].
load(tmp);
857 for (
size_t i = 0; i < dataLen; ++i)
859 out[i].gather(in, index);
860 for (
auto &idx : index)
862 idx = idx + index_t(1);
867template <
typename SimdType,
unsigned int W
idth>
871 const std::uint32_t dataLen,
876 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp;
877 for (
size_t i = 0; i < dataLen; ++i)
880 for (
size_t j = 0; j < vec_t::width; ++j)
882 out[i + j * dataLen] = tmp[j];
887template <
typename SimdType,
unsigned int W
idth>
891 const std::uint32_t dataLen,
const std::uint32_t skipPads,
896 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp;
897 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp1;
898 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp2;
899 alignas(vec_t::alignment)
typename vec_t::scalarArray tmp3;
901 size_t nBlocks = dataLen / 4;
903 for (
size_t i = 0; i < nBlocks; ++i)
905 in[4 * i].
store(tmp);
906 in[4 * i + 1].store(tmp1);
907 in[4 * i + 2].store(tmp2);
908 in[4 * i + 3].store(tmp3);
910 for (
size_t j = 0; j < vec_t::width - skipPads; ++j)
912 out[j * dataLen + 4 * i] = tmp[j];
913 out[j * dataLen + 4 * i + 1] = tmp1[j];
914 out[j * dataLen + 4 * i + 2] = tmp2[j];
915 out[j * dataLen + 4 * i + 3] = tmp3[j];
919 for (
size_t i = nBlocks * 4; i < dataLen; ++i)
922 for (
size_t j = 0; j < vec_t::width - skipPads; ++j)
924 out[j * dataLen + i] = tmp[j];
929template <
typename SimdType,
unsigned int W
idth>
933 std::uint32_t dataLen,
937 using index_t =
typename vec_t::chunkIndexType;
939 alignas(index_t::alignment)
typename index_t::scalarArray tmp;
940 index_t index[vec_t::numChunks];
942 for (
size_t chunk = 0; chunk < vec_t::numChunks; ++chunk)
944 for (
size_t lane = 0; lane < index_t::width; ++lane)
946 tmp[lane] =
static_cast<typename vec_t::scalarIndexType
>(
947 (chunk * index_t::width + lane) * dataLen);
949 index[chunk].
load(tmp);
952 for (
size_t i = 0; i < dataLen; ++i)
954 in[i].scatter(out, index);
955 for (
auto &idx : index)
957 idx = idx + index_t(1);
962#undef TINYSIMD_PRAGMA_UNROLL
typename scalar_index_type< T >::type scalar_index_type_t
void load_interleave(const T *in, const size_t dataLen, std::vector< scalarT< T >, allocator< scalarT< T > > > &out)
scalarT< T > abs(scalarT< T > in)
static constexpr unsigned int PACKMULTIPLIER
void deinterleave_unalign_store(const std::vector< scalarT< T >, allocator< scalarT< T > > > &in, const size_t dataLen, T *out)
static constexpr struct tinysimd::is_aligned_t is_aligned
scalarT< T > operator-(scalarT< T > lhs, scalarT< T > rhs)
scalarT< T > operator/(scalarT< T > lhs, scalarT< T > rhs)
scalarT< T > max(scalarT< T > lhs, scalarT< T > rhs)
typename abi< ScalarType, width >::type simd
boost::alignment::aligned_allocator< T, T::alignment > allocator
scalarT< T > log(scalarT< T > in)
scalarT< T > operator*(scalarT< T > lhs, scalarT< T > rhs)
void load_unalign_interleave(const T *in, const size_t dataLen, std::vector< scalarT< T >, allocator< scalarT< T > > > &out)
void deinterleave_store(const std::vector< scalarT< T >, allocator< scalarT< T > > > &in, const size_t dataLen, T *out)
scalarT< T > min(scalarT< T > lhs, scalarT< T > rhs)
void deinterleave_unalign_store_skipPads(const std::vector< scalarT< T >, allocator< scalarT< T > > > &in, const size_t dataLen, const size_t skipPads, T *out)
static constexpr unsigned int PACKSIZE
scalarT< T > sqrt(scalarT< T > in)
void load_unalign_interleave_skipPads(const T *in, const size_t dataLen, const size_t skipPads, std::vector< scalarT< T >, allocator< scalarT< T > > > &out)
scalarT< T > operator+(scalarT< T > lhs, scalarT< T > rhs)
typename details::longsimd_selector< ScalarType, width, abi >::type longsimd
typename first_not_void_of< typename sve< T, width >::type, typename avx512< T, width >::type, typename avx2< T, width >::type, typename sse2< T, width >::type, typename simd64< T >::type, typename scalar< T >::type >::type type
static constexpr unsigned int value
typename default_abi< T, 0 >::type native_type
void gather(const scalarType *p, const IndexType *indices)
void fma(const long_simd &a, const chunkType &b)
void operator*=(const chunkType &rhs)
void load(const scalarType *p)
long_simd(const chunkType *rhs)
void broadcast(const chunkType &rhs)
scalarType[width] scalarArray
void operator/=(const chunkType &rhs)
void fma(U a, const long_simd &b)
static constexpr unsigned int chunkWidth
std::array< chunkType, numChunks > vectorType
void fma(const chunkType &a, const long_simd &b)
void load(const scalarType *p, flag f)
void broadcast(const scalarType rhs)
long_simd(const scalarType rhs)
scalar_index_type_t< SimdType > scalarIndexType
void fma(const long_simd &a, U b)
void operator*=(const long_simd &rhs)
void fma(const chunkType &a, const chunkType &b)
long_simd(const long_simd &rhs)=default
scalarType & operator[](size_t i)
void fma(const long_simd &a, const long_simd &b)
scalarType operator[](size_t i) const
long_simd(const vectorType &rhs)
void store(scalarType *p) const
void store(scalarType *p, flag f) const
chunkType _data[numChunks]
static constexpr unsigned int numChunks
simd< scalarIndexType, chunkWidth > chunkIndexType
void operator-=(const chunkType &rhs)
void operator+=(const chunkType &rhs)
static constexpr unsigned int width
void scatter(scalarType *out, const IndexType *indices) const
long_simd & operator=(const long_simd &)=default
void operator+=(const long_simd &rhs)
void operator/=(const long_simd &rhs)
void operator-=(const long_simd &rhs)
static constexpr unsigned int alignment
typename SimdType::scalarType scalarType
static constexpr int requested_width
static constexpr int default_width
std::conditional_t< requested_width==static_cast< int >(native_type::width), native_type, long_simd< native_type, static_cast< unsigned int >(requested_width)> > type
simd< ScalarType, 0, abi_policy > native_type
typename T::scalarIndexType type
std::conditional_t<(sizeof(typename T::scalarType)<=4), std::uint32_t, std::uint64_t > type
typename first_not_void_of< Rest... >::type type
static constexpr unsigned int width
#define TINYSIMD_PRAGMA_UNROLL