diff options
author | fixthgame <fixthgame@yandex-team.com> | 2023-10-23 11:32:53 +0300 |
---|---|---|
committer | fixthgame <fixthgame@yandex-team.com> | 2023-10-23 12:05:42 +0300 |
commit | 6fbfea3ad8bd28aa205d7e594543af7d4670280c (patch) | |
tree | 272c8b3e4dbaabba5c66b1df15da2485302ff322 | |
parent | a1e69eeebcd7f2be7c421e9f450d877f7660bc7a (diff) | |
download | ydb-6fbfea3ad8bd28aa205d7e594543af7d4670280c.tar.gz |
Traits
Using shortcuts
Traits
-rw-r--r-- | ydb/library/yql/utils/simd/simd.h | 34 | ||||
-rw-r--r-- | ydb/library/yql/utils/simd/simd_avx2.h | 51 | ||||
-rw-r--r-- | ydb/library/yql/utils/simd/simd_fallback.h | 259 | ||||
-rw-r--r-- | ydb/library/yql/utils/simd/simd_sse42.h | 52 | ||||
-rw-r--r-- | ydb/library/yql/utils/simd/simd_ut.cpp | 85 |
5 files changed, 432 insertions, 49 deletions
diff --git a/ydb/library/yql/utils/simd/simd.h b/ydb/library/yql/utils/simd/simd.h new file mode 100644 index 00000000000..ba94889351a --- /dev/null +++ b/ydb/library/yql/utils/simd/simd.h @@ -0,0 +1,34 @@ +#pragma once + +#include "simd_avx2.h" +#include "simd_sse42.h" +#include "simd_fallback.h" + +namespace NSimd { + +template<int RegisterSize, typename TBaseRegister, template<typename> typename TSimd> +struct TSimdTraits { + template<typename T> + using TSimd8 = TSimd<T>; + using TRegister = TBaseRegister; + + static constexpr int Size = RegisterSize; +}; + +using TSimdAVX2Traits = TSimdTraits<32, __m256i, NSimd::NAVX2::TSimd8>; +using TSimdSSE42Traits = TSimdTraits<16, __m128i, NSimd::NSSE42::TSimd8>; +using TSimdFallbackTraits = TSimdTraits<8, ui64, NSimd::NFallback::TSimd8>; + + +template<typename TFactory> +auto SelectSimdTraits(const TFactory& factory) { + if (NX86::HaveAVX2()) { + return factory.template Create<TSimdAVX2Traits>(); + } else if (NX86::HaveSSE42()) { + return factory.template Create<TSimdSSE42Traits>(); + } else { + return factory.template Create<TSimdFallbackTraits>(); + } +} + +}
\ No newline at end of file diff --git a/ydb/library/yql/utils/simd/simd_avx2.h b/ydb/library/yql/utils/simd/simd_avx2.h index 97e3d951999..f6c1979fe50 100644 --- a/ydb/library/yql/utils/simd/simd_avx2.h +++ b/ydb/library/yql/utils/simd/simd_avx2.h @@ -3,6 +3,9 @@ #include <cstdint> #include <immintrin.h> + +#pragma clang attribute push(__attribute__((target("avx2"))), apply_to=function) + namespace NSimd { namespace NAVX2 { @@ -21,24 +24,24 @@ struct TBase { : Value(value) { } - inline operator const __m256i&() const { + explicit inline operator const __m256i&() const { return this->Value; } - inline operator __m256i&() { + explicit inline operator __m256i&() { return this->Value; } inline Child operator|(const Child other) const { - return _mm256_or_si256(*this, other); + return _mm256_or_si256(this->Value, other.Value); } inline Child operator&(const Child other) const { - return _mm256_and_si256(*this, other); + return _mm256_and_si256(this->Value, other.Value); } inline Child operator^(const Child other) const { - return _mm256_xor_si256(*this, other); + return _mm256_xor_si256(this->Value, other.Value); } inline Child BitAndNot(const Child other) const { - return _mm256_andnot_si256(*this, other); + return _mm256_andnot_si256(this->Value, other.Value); }; inline Child& operator|=(const Child other) { auto cast = static_cast<Child*>(*this); @@ -71,7 +74,7 @@ struct TBase8: TBase<TSimd8<T>> { } friend inline Mask operator==(const TSimd8<T> lhs, const TSimd8<T> rhs) { - return _mm256_cmpeq_epi8(lhs, rhs); + return _mm256_cmpeq_epi8(lhs.Value, rhs.Value); } static const int SIZE = sizeof(TBase<T>::Value); @@ -100,7 +103,7 @@ struct TSimd8<bool>: TBase8<bool> { } inline bool Any() const { - return !_mm256_testz_si256(*this, *this); + return !_mm256_testz_si256(this->Value, this->Value); } inline TSimd8<bool> operator~() const { @@ -143,14 +146,14 @@ struct TBase8Numeric: TBase8<T> { } inline void Store(T dst[32]) const { - return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); + return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), this->Value); } inline TSimd8<T> operator+(const TSimd8<T> other) const { - return _mm256_add_epi8(*this, other); + return _mm256_add_epi8(this->Value, other.Value); } inline TSimd8<T> operator-(const TSimd8<T> other) const { - return _mm256_sub_epi8(*this, other); + return _mm256_sub_epi8(this->Value, other.Value); } inline TSimd8<T>& operator+=(const TSimd8<T> other) { *this = *this + other; @@ -212,16 +215,16 @@ struct TSimd8<i8> : TBase8Numeric<i8> { } inline TSimd8<i8> MaxValue(const TSimd8<i8> other) const { - return _mm256_max_epi8(*this, other); + return _mm256_max_epi8(this->Value, other.Value); } inline TSimd8<i8> MinValue(const TSimd8<i8> other) const { - return _mm256_min_epi8(*this, other); + return _mm256_min_epi8(this->Value, other.Value); } inline TSimd8<bool> operator>(const TSimd8<i8> other) const { - return _mm256_cmpgt_epi8(*this, other); + return _mm256_cmpgt_epi8(this->Value, other.Value); } inline TSimd8<bool> operator<(const TSimd8<i8> other) const { - return _mm256_cmpgt_epi8(other, *this); + return _mm256_cmpgt_epi8(other.Value, this->Value); } }; @@ -268,10 +271,10 @@ struct TSimd8<ui8>: TBase8Numeric<ui8> { } inline TSimd8<ui8> MaxValue(const TSimd8<ui8> other) const { - return _mm256_max_epu8(*this, other); + return _mm256_max_epu8(this->Value, other.Value); } inline TSimd8<ui8> MinValue(const TSimd8<ui8> other) const { - return _mm256_min_epu8(other, *this); + return _mm256_min_epu8(other.Value, this->Value); } inline TSimd8<bool> operator<=(const TSimd8<ui8> other) const { return other.MaxValue(*this) == other; @@ -287,13 +290,13 @@ struct TSimd8<ui8>: TBase8Numeric<ui8> { return ~this->BitsNotSet(); } inline bool BitsNotSetAnywhere() const { - return _mm256_testz_si256(*this, *this); + return _mm256_testz_si256(this->Value, this->Value); } inline bool AnyBitsSetAnywhere() const { return !BitsNotSetAnywhere(); } inline bool BitsNotSetAnywhere(TSimd8<ui8> bits) const { - return _mm256_testz_si256(*this, bits); + return _mm256_testz_si256(this->Value, bits.Value); } inline bool AnyBitsSetAnywhere(TSimd8<ui8> bits) const { return !BitsNotSetAnywhere(bits); @@ -301,18 +304,20 @@ struct TSimd8<ui8>: TBase8Numeric<ui8> { template<int N> inline TSimd8<ui8> Shr() const { - return TSimd8<ui8>(_mm256_srli_epi16(*this, N)) & ui8(0xFFu >> N); + return TSimd8<ui8>(_mm256_srli_epi16(this->Value, N)) & ui8(0xFFu >> N); } template<int N> inline TSimd8<ui8> Shl() const { - return TSimd8<ui8>(_mm256_slli_epi16(*this, N)) & ui8(0xFFu << N); + return TSimd8<ui8>(_mm256_slli_epi16(this->Value, N)) & ui8(0xFFu << N); } template<int N> inline int GetBit() const { - return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); + return _mm256_movemask_epi8(_mm256_slli_epi16(this->Value, 7-N)); } }; } -}
\ No newline at end of file +} + +#pragma clang attribute pop
\ No newline at end of file diff --git a/ydb/library/yql/utils/simd/simd_fallback.h b/ydb/library/yql/utils/simd/simd_fallback.h new file mode 100644 index 00000000000..0bdaf3e303b --- /dev/null +++ b/ydb/library/yql/utils/simd/simd_fallback.h @@ -0,0 +1,259 @@ +#pragma once + +#include <cstdint> +#include <immintrin.h> + +namespace NSimd { +namespace NFallback { + +template <typename T> +struct TSimd8; + +template<typename Child> +struct TBase { + ui64 Value; + + inline TBase() + : Value{ui64()} { + } + + inline TBase(const ui64 value) + : Value(value) { + } + + explicit inline operator const ui64&() const { + return this->Value; + } + explicit inline operator ui64&() { + return this->Value; + } + + inline Child operator|(const Child other) const { + return this->Value | other.Value; + } + inline Child operator&(const Child other) const { + return this->Value & other.Value; + } + inline Child operator^(const Child other) const { + return this->Value ^ other.Value; + } + inline Child BitAndNot(const Child other) const { + return (~this->Value) & other.Value; + }; + inline Child& operator|=(const Child other) { + auto cast = static_cast<Child*>(*this); + *cast = *cast | other; + return *cast; + } + inline Child& operator&=(const Child other) { + auto cast = static_cast<Child*>(*this); + *cast = *cast & other; + return *cast; + }; + inline Child& operator^=(const Child other) { + auto cast = static_cast<Child*>(*this); + *cast = *cast ^ other; + return *cast; + }; +}; + +template<typename T, typename Mask=TSimd8<bool>> +struct TBase8: TBase<TSimd8<T>> { + + inline TBase8() + : TBase<TSimd8<T>>() + { + } + + inline TBase8(const ui64 _value) + : TBase<TSimd8<T>>(_value) + { + } + + friend inline Mask operator==(const TSimd8<T> lhs, const TSimd8<T> rhs) { + return lhs.Value == rhs.Value; + } + + static const int SIZE = sizeof(TBase<T>::Value); +}; + +template<> +struct TSimd8<bool>: TBase8<bool> { + + inline TSimd8<bool>() + : TBase8() + { + } + + inline TSimd8<bool>(const ui64 value) + : TBase8<bool>(value) + { + } + + inline TSimd8<bool>(bool value) + : TBase8<bool>(Set(value)) + { + } + + static inline TSimd8<bool> Set(bool value) { + return ui64(-value); + } + + inline bool Any() const { + return Value != 0; + } + + inline TSimd8<bool> operator~() const { + return *this ^ true; + } +}; + +template<typename T> +struct TBase8Numeric: TBase8<T> { + + inline TBase8Numeric() + : TBase8<T>() + { + } + inline TBase8Numeric(const ui64 value) + : TBase8<T>(value) + { + } + + static inline TSimd8<T> Set(T value) { + TSimd8<T> result = TSimd8<T>::Zero(); + auto dst = (ui8*)(&result.Value); + dst[0] = dst[1] = dst[2] = dst[3] = value; + dst[4] = dst[5] = dst[6] = dst[7] = value; + return result; + } + static inline TSimd8<T> Zero() { + return (ui64) 0; + } + static inline TSimd8<T> Load(const T values[8]) { + return TSimd8<T>(*((const ui64*) values)); + } + + inline void Store(T dst[8]) const { + *((ui64*) dst) = this->Value; + } + + inline TSimd8<T> operator+(const TSimd8<T> other) const { + return this->Value + other.Value; + } + inline TSimd8<T> operator-(const TSimd8<T> other) const { + return this->Value - other.Value; + } + inline TSimd8<T>& operator+=(const TSimd8<T> other) { + *this = *this + other; + return *static_cast<TSimd8<T>*>(this); + } + inline TSimd8<T>& operator-=(const TSimd8<T> other) { + *this = *this - other; + return *static_cast<TSimd8<T>*>(this); + } + + // 0xFFu = 11111111 = 2^8 - 1 + inline TSimd8<T> operator~() const { + return *this ^ ui8(0xFFu); + } +}; + +template<> +struct TSimd8<i8> : TBase8Numeric<i8> { + inline TSimd8() + : TBase8Numeric<i8>() + { + } + inline TSimd8(const ui64 value) + : TBase8Numeric<i8>(value) + { + } + inline TSimd8(i8 value) + : TSimd8(Set(value)) + { + } + inline TSimd8(const i8 values[8]) + : TSimd8(Load(values)) + { + } + inline TSimd8( + i8 v0, i8 v1, i8 v2, i8 v3, i8 v4, i8 v5, i8 v6, i8 v7 + ) : TSimd8({v0, v1, v2, v3, v4, v5, v6, v7}) + { + } + + inline TSimd8<i8> MaxValue(const TSimd8<i8> other) const { + return (*this > other).Any() ? *this : other; + } + inline TSimd8<i8> MinValue(const TSimd8<i8> other) const { + return (*this < other).Any() ? *this : other; + } + + inline TSimd8<bool> operator>(const TSimd8<i8> other) const { + return i64(this->Value) > i64(other.Value); + } + inline TSimd8<bool> operator<(const TSimd8<i8> other) const { + return i64(this->Value) < i64(other.Value); + } +}; + +template<> +struct TSimd8<ui8>: TBase8Numeric<ui8> { + inline TSimd8() + : TBase8Numeric<ui8>() + { + } + inline TSimd8(const ui64 _value) + : TBase8Numeric<ui8>(_value) + { + } + inline TSimd8(ui8 _value) + : TSimd8(Set(_value)) + { + } + inline TSimd8(const ui8 values[8]) + : TSimd8(Load(values)) + { + } + inline TSimd8( + ui8 v0, ui8 v1, ui8 v2, ui8 v3, ui8 v4, ui8 v5, ui8 v6, ui8 v7 + ) : TSimd8({v0, v1, v2, v3, v4, v5, v6, v7} + ) {} + + inline TSimd8<ui8> MaxValue(const TSimd8<ui8> other) const { + return this->Value > other.Value ? *this : other; + } + inline TSimd8<ui8> MinValue(const TSimd8<ui8> other) const { + return this->Value < other.Value ? *this : other; + } + + inline TSimd8<bool> operator<=(const TSimd8<ui8> other) const { + return other.MaxValue(*this) == other; + } + inline TSimd8<bool> operator>=(const TSimd8<ui8> other) const { + return other.MinValue(*this) == other; + } + + inline TSimd8<bool> BitsNotSet() const { + return this->Value == 0; + } + inline TSimd8<bool> AnyBitsSet() const { + return ~this->BitsNotSet(); + } + inline bool BitsNotSetAnywhere() const { + return BitsNotSet().Any(); + } + inline bool AnyBitsSetAnywhere() const { + return !BitsNotSetAnywhere(); + } + inline bool BitsNotSetAnywhere(TSimd8<ui8> bits) const { + return ((*this) & bits).Value == 0; + } + inline bool AnyBitsSetAnywhere(TSimd8<ui8> bits) const { + return !BitsNotSetAnywhere(bits); + } +}; + +} +} diff --git a/ydb/library/yql/utils/simd/simd_sse42.h b/ydb/library/yql/utils/simd/simd_sse42.h index d2d74dc0e72..3e6e1a1c9a8 100644 --- a/ydb/library/yql/utils/simd/simd_sse42.h +++ b/ydb/library/yql/utils/simd/simd_sse42.h @@ -3,6 +3,8 @@ #include <cstdint> #include <immintrin.h> +#pragma clang attribute push(__attribute__((target("sse4.2"))), apply_to=function) + namespace NSimd { namespace NSSE42 { @@ -21,24 +23,24 @@ struct TBase { : Value(value) { } - inline operator const __m128i&() const { + explicit inline operator const __m128i&() const { return this->Value; } - inline operator __m128i&() { + explicit inline operator __m128i&() { return this->Value; } inline Child operator|(const Child other) const { - return _mm_or_si128(*this, other); + return _mm_or_si128(this->Value, other.Value); } inline Child operator&(const Child other) const { - return _mm_and_si128(*this, other); + return _mm_and_si128(this->Value, other.Value); } inline Child operator^(const Child other) const { - return _mm_xor_si128(*this, other); + return _mm_xor_si128(this->Value, other.Value); } inline Child BitAndNot(const Child other) const { - return _mm_andnot_si128(*this, other); + return _mm_andnot_si128(this->Value, other.Value); }; inline Child& operator|=(const Child other) { auto cast = static_cast<Child*>(*this); @@ -71,7 +73,7 @@ struct TBase8: TBase<TSimd8<T>> { } friend inline Mask operator==(const TSimd8<T> lhs, const TSimd8<T> rhs) { - return _mm_cmpeq_epi8(lhs, rhs); + return _mm_cmpeq_epi8(lhs.Value, rhs.Value); } static const int SIZE = sizeof(TBase<T>::Value); @@ -100,7 +102,7 @@ struct TSimd8<bool>: TBase8<bool> { } inline bool Any() const { - return !_mm_testz_si128(*this, *this); + return !_mm_testz_si128(this->Value, this->Value); } inline TSimd8<bool> operator~() const { @@ -141,14 +143,14 @@ struct TBase8Numeric: TBase8<T> { } inline void Store(T dst[16]) const { - return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); + return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), this->Value); } inline TSimd8<T> operator+(const TSimd8<T> other) const { - return _mm_add_epi8(*this, other); + return _mm_add_epi8(this->Value, other.Value); } inline TSimd8<T> operator-(const TSimd8<T> other) const { - return _mm_sub_epi8(*this, other); + return _mm_sub_epi8(this->Value, other.Value); } inline TSimd8<T>& operator+=(const TSimd8<T> other) { *this = *this + other; @@ -204,16 +206,16 @@ struct TSimd8<i8> : TBase8Numeric<i8> { } inline TSimd8<i8> MaxValue(const TSimd8<i8> other) const { - return _mm_max_epi8(*this, other); + return _mm_max_epi8(this->Value, other.Value); } inline TSimd8<i8> MinValue(const TSimd8<i8> other) const { - return _mm_min_epi8(*this, other); + return _mm_min_epi8(this->Value, other.Value); } inline TSimd8<bool> operator>(const TSimd8<i8> other) const { - return _mm_cmpgt_epi8(*this, other); + return _mm_cmpgt_epi8(this->Value, other.Value); } inline TSimd8<bool> operator<(const TSimd8<i8> other) const { - return _mm_cmpgt_epi8(other, *this); + return _mm_cmpgt_epi8(other.Value, this->Value); } }; @@ -254,10 +256,10 @@ struct TSimd8<ui8>: TBase8Numeric<ui8> { } inline TSimd8<ui8> MaxValue(const TSimd8<ui8> other) const { - return _mm_max_epu8(*this, other); + return _mm_max_epu8(this->Value, other.Value); } inline TSimd8<ui8> MinValue(const TSimd8<ui8> other) const { - return _mm_min_epu8(other, *this); + return _mm_min_epu8(other.Value, this->Value); } inline TSimd8<bool> operator<=(const TSimd8<ui8> other) const { return other.MaxValue(*this) == other; @@ -273,13 +275,13 @@ struct TSimd8<ui8>: TBase8Numeric<ui8> { return ~this->BitsNotSet(); } inline bool BitsNotSetAnywhere() const { - return _mm_testz_si128(*this, *this); + return _mm_testz_si128(this->Value, this->Value); } inline bool AnyBitsSetAnywhere() const { return !BitsNotSetAnywhere(); } inline bool BitsNotSetAnywhere(TSimd8<ui8> bits) const { - return _mm_testz_si128(*this, bits); + return _mm_testz_si128(this->Value, bits.Value); } inline bool AnyBitsSetAnywhere(TSimd8<ui8> bits) const { return !BitsNotSetAnywhere(bits); @@ -287,18 +289,22 @@ struct TSimd8<ui8>: TBase8Numeric<ui8> { template<int N> inline TSimd8<ui8> Shr() const { - return TSimd8<ui8>(_mm_srli_epi16(*this, N)) & ui8(0xFFu >> N); + return TSimd8<ui8>(_mm_srli_epi16(this->Value, N)) & ui8(0xFFu >> N); } template<int N> inline TSimd8<ui8> Shl() const { - return TSimd8<ui8>(_mm_slli_epi16(*this, N)) & ui8(0xFFu << N); + return TSimd8<ui8>(_mm_slli_epi16(this->Value, N)) & ui8(0xFFu << N); } template<int N> inline int GetBit() const { - return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); + return _mm_movemask_epi8(_mm_slli_epi16(this->Value, 7-N)); } }; } -}
\ No newline at end of file +} + + + +#pragma clang attribute pop
\ No newline at end of file diff --git a/ydb/library/yql/utils/simd/simd_ut.cpp b/ydb/library/yql/utils/simd/simd_ut.cpp index b13421a6e48..5fb5b299573 100644 --- a/ydb/library/yql/utils/simd/simd_ut.cpp +++ b/ydb/library/yql/utils/simd/simd_ut.cpp @@ -1,8 +1,30 @@ #include <library/cpp/testing/unittest/registar.h> #include <util/system/cpu_id.h> +#include "simd.h" + +template<typename TTraits> +void Reverse(ui8* buf, ui8 *result_buf, int len) { + using TSimdUI8 = typename TTraits::template TSimd8<ui8>; + int id = 0; + while (id + TTraits::Size <= len) { + TSimdUI8 x(buf + id); + (~x).Store(result_buf + id); + id += TTraits::Size; + } + while (id < len) { + *(result_buf + id) = ~(*(buf + id)); + id += 1; + } +} + +struct TTestFactory { + template<typename T> + int Create() const { + return T::Size; + } +}; #pragma clang attribute push(__attribute__((target("avx2"))), apply_to=function) -#include <ydb/library/yql/utils/simd/simd_avx2.h> Y_UNIT_TEST_SUITE(TSimdAVX2) { using namespace NSimd::NAVX2; Y_UNIT_TEST(SimdBool) { @@ -86,11 +108,27 @@ Y_UNIT_TEST_SUITE(TSimdAVX2) { UNIT_ASSERT_EQUAL(((a + b) == a3).Any(), true); UNIT_ASSERT_EQUAL(((a - b) == a2).Any(), true); } + + Y_UNIT_TEST(SimdTrait) { + if (!NX86::HaveAVX2()) { + return; + } + + ui8 buf[1000]; + for (int i = 0; i < 1000; i += 1) { + buf[i] = i; + } + ui8 result_buf[1000] = {0}; + Reverse<NSimd::TSimdAVX2Traits>(buf, result_buf, 1000); + for (int i = 0; i < 1000; i += 1) { + UNIT_ASSERT_EQUAL(result_buf[i], ui8(~buf[i])); + } + } } + #pragma clang attribute pop #pragma clang attribute push(__attribute__((target("sse4.2"))), apply_to=function) -#include <ydb/library/yql/utils/simd/simd_sse42.h> Y_UNIT_TEST_SUITE(TSimdSSE42) { using namespace NSimd::NSSE42; Y_UNIT_TEST(SimdBool) { @@ -174,5 +212,46 @@ Y_UNIT_TEST_SUITE(TSimdSSE42) { UNIT_ASSERT_EQUAL(((a + b) == a3).Any(), true); UNIT_ASSERT_EQUAL(((a - b) == a2).Any(), true); } + + Y_UNIT_TEST(SimdTrait) { + if (!NX86::HaveSSE42()) { + return; + } + + ui8 buf[1000]; + for (int i = 0; i < 1000; i += 1) { + buf[i] = i; + } + ui8 result_buf[1000] = {0}; + Reverse<NSimd::TSimdSSE42Traits>(buf, result_buf, 1000); + for (int i = 0; i < 1000; i += 1) { + UNIT_ASSERT_EQUAL(result_buf[i], ui8(~buf[i])); + } + } } -#pragma clang attribute pop
\ No newline at end of file +#pragma clang attribute pop + +Y_UNIT_TEST_SUITE(SimdFallback) { + Y_UNIT_TEST(SimdTrait) { + ui8 buf[1000]; + for (int i = 0; i < 1000; i += 1) { + buf[i] = i; + } + ui8 result_buf[1000] = {0}; + Reverse<NSimd::TSimdFallbackTraits>(buf, result_buf, 1000); + for (int i = 0; i < 1000; i += 1) { + UNIT_ASSERT_EQUAL(result_buf[i], ui8(~buf[i])); + } + } + + Y_UNIT_TEST(BestTrait) { + TTestFactory x; + if (NX86::HaveAVX2()) { + UNIT_ASSERT_EQUAL(NSimd::SelectSimdTraits(x), 32); + } else if (NX86::HaveSSE42()) { + UNIT_ASSERT_EQUAL(NSimd::SelectSimdTraits(x), 16); + } else { + UNIT_ASSERT_EQUAL(NSimd::SelectSimdTraits(x), 8); + } + } +}
\ No newline at end of file |