From 7975e30ce9b9ef1282fea91626daffa09b2d72ea Mon Sep 17 00:00:00 2001 From: Denis Yaroshevskiy Date: Mon, 23 Sep 2024 11:50:53 -0700 Subject: [PATCH] folly simd-friendly (#2279) Summary: Pull Request resolved: https://github.com/facebook/folly/pull/2279 extracting the common "simd-friendly" type helpers. Reviewed By: Gownta Differential Revision: D61205292 fbshipit-source-id: 17551ed2baa168a6b1e5c7dda15945263d97d5fe --- CMakeLists.txt | 4 +- folly/algorithm/simd/BUCK | 1 + folly/algorithm/simd/FindFixed.h | 14 +- folly/algorithm/simd/detail/BUCK | 10 ++ folly/algorithm/simd/detail/Traits.h | 100 +++++++++++++ folly/algorithm/simd/detail/test/BUCK | 10 ++ .../algorithm/simd/detail/test/TraitsTest.cpp | 139 ++++++++++++++++++ 7 files changed, 266 insertions(+), 12 deletions(-) create mode 100644 folly/algorithm/simd/detail/Traits.h create mode 100644 folly/algorithm/simd/detail/test/TraitsTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 885990ecc7c..c30fc9938ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -648,8 +648,10 @@ if (BUILD_TESTS OR BUILD_BENCHMARKS) folly_define_tests( DIRECTORY algorithm/simd/detail/test/ TEST algorithm_simd_detail_simd_any_of_test SOURCES SimdAnyOfTest.cpp - TEST algorithm_simd_detail_unroll_utils_test SOURCES UnrollUtilsTest.cpp TEST algorithm_simd_detail_simd_for_each_test SOURCES SimdForEachTest.cpp + TEST algorithm_simd_detail_unroll_utils_test SOURCES UnrollUtilsTest.cpp + # disabled until C++20 + # TEST algorithm_simd_detail_simd_traits_test SOURCES TraitsTest.cpp DIRECTORY algorithm/simd/test/ TEST algorithm_simd_find_fixed_test SOURCES FindFixedTest.cpp diff --git a/folly/algorithm/simd/BUCK b/folly/algorithm/simd/BUCK index 9e95c669b97..1343e8f9b73 100644 --- a/folly/algorithm/simd/BUCK +++ b/folly/algorithm/simd/BUCK @@ -19,5 +19,6 @@ cpp_library( exported_deps = [ ":movemask", "//folly:portability", + "//folly/algorithm/simd/detail:traits", ], ) diff --git a/folly/algorithm/simd/FindFixed.h b/folly/algorithm/simd/FindFixed.h index c76c8022341..547b02a6b3a 100644 --- a/folly/algorithm/simd/FindFixed.h +++ b/folly/algorithm/simd/FindFixed.h @@ -28,6 +28,7 @@ #include #include +#include #if FOLLY_X64 #include @@ -82,11 +83,6 @@ constexpr std::optional findFixed(std::span where, U x) // implementation --------------------------------------------------------- namespace find_fixed_detail { -template -std::optional findFixedCast(std::span& where, T x) { - std::span whereU{reinterpret_cast(where.data()), N}; - return findFixed(whereU, static_cast(x)); -} template constexpr std::optional findFixedConstexpr( @@ -295,13 +291,9 @@ constexpr std::optional findFixed(std::span where, U x) return findFixed(where, static_cast(x)); } else if (std::is_constant_evaluated()) { return find_fixed_detail::findFixedConstexpr(std::span(where), x); - } else if constexpr (std::is_enum_v) { - return find_fixed_detail::findFixedCast>( - where, x); - } else if constexpr (std::is_signed_v) { - return find_fixed_detail::findFixedCast>(where, x); } else { - return find_fixed_detail::findFixedDispatch(where, x); + return find_fixed_detail::findFixedDispatch( + detail::asSimdFriendlyUint(where), detail::asSimdFriendlyUint(x)); } } diff --git a/folly/algorithm/simd/detail/BUCK b/folly/algorithm/simd/detail/BUCK index c7501e203a1..53a172a2e6b 100644 --- a/folly/algorithm/simd/detail/BUCK +++ b/folly/algorithm/simd/detail/BUCK @@ -36,6 +36,16 @@ cpp_library( ], ) +cpp_library( + name = "traits", + headers = ["Traits.h"], + exported_deps = [ + "//folly:memory", + "//folly:traits", + "//folly/container:span", + ], +) + cpp_library( name = "unroll_utils", headers = ["UnrollUtils.h"], diff --git a/folly/algorithm/simd/detail/Traits.h b/folly/algorithm/simd/detail/Traits.h new file mode 100644 index 00000000000..ee2ef3d5105 --- /dev/null +++ b/folly/algorithm/simd/detail/Traits.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace folly::detail { + +template +auto findSimdFriendlyEquivalent() { + if constexpr (std::is_enum_v) { + return findSimdFriendlyEquivalent>(); + } else if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return float{}; + } else { + return double{}; + } + } else if constexpr (std::is_signed_v) { + if constexpr (sizeof(T) == 1) { + return std::int8_t{}; + } else if constexpr (sizeof(T) == 2) { + return std::int16_t{}; + } else if constexpr (sizeof(T) == 4) { + return std::int32_t{}; + } else if constexpr (sizeof(T) == 8) { + return std::int64_t{}; + } + } else if constexpr (std::is_unsigned_v) { + if constexpr (sizeof(T) == 1) { + return std::uint8_t{}; + } else if constexpr (sizeof(T) == 2) { + return std::uint16_t{}; + } else if constexpr (sizeof(T) == 4) { + return std::uint32_t{}; + } else if constexpr (sizeof(T) == 8) { + return std::uint64_t{}; + } + } +} + +template +concept has_simd_friendly_equivalent = + !std::is_same_v())>; + +template +using simd_friendly_equivalent_t = folly::like_t< // + T, + decltype(findSimdFriendlyEquivalent>())>; + +template +concept has_integral_simd_friendly_equivalent = + has_simd_friendly_equivalent && // have to explicitly specify this for + // subsumption to work + std::integral>; + +template +using integral_simd_friendly_equivalent = simd_friendly_equivalent_t; + +template +auto asSimdFriendly(folly::span s) { + return folly::reinterpret_span_cast>(s); +} + +template +constexpr auto asSimdFriendly(T x) { + return static_cast>(x); +} + +template +auto asSimdFriendlyUint(folly::span s) { + return folly::reinterpret_span_cast< + folly::like_t>>(s); +} + +template +constexpr auto asSimdFriendlyUint(T x) { + return static_cast>(x); +} + +} // namespace folly::detail diff --git a/folly/algorithm/simd/detail/test/BUCK b/folly/algorithm/simd/detail/test/BUCK index fe5871aa577..6661c2ce012 100644 --- a/folly/algorithm/simd/detail/test/BUCK +++ b/folly/algorithm/simd/detail/test/BUCK @@ -24,6 +24,16 @@ cpp_unittest( ], ) +cpp_unittest( + name = "traits_test", + srcs = ["TraitsTest.cpp"], + deps = [ + "//folly/algorithm/simd/detail:traits", + "//folly/portability:gmock", + "//folly/portability:gtest", + ], +) + cpp_unittest( name = "unroll_utils_test", srcs = [ diff --git a/folly/algorithm/simd/detail/test/TraitsTest.cpp b/folly/algorithm/simd/detail/test/TraitsTest.cpp new file mode 100644 index 00000000000..5d01d9630df --- /dev/null +++ b/folly/algorithm/simd/detail/test/TraitsTest.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +namespace folly::detail { + +struct FollySimdTraitsTest : testing::Test {}; + +namespace simd_friendly_equivalent_test { + +// ints +static_assert( + std::is_same_v>); +static_assert( + std::is_same_v>); + +static_assert(std::is_same_v>); +static_assert( + std::is_same_v>); + +static_assert(std::is_same_v>); +static_assert( + std::is_same_v>); + +static_assert( + std::is_same_v>); +static_assert( + std::is_same_v>); + +// floats +static_assert(std::is_same_v>); +static_assert(std::is_same_v>); + +// enum +enum SomeInt {}; +enum class SomeIntClass : std::int32_t {}; + +static_assert( + std::is_same_v>); +static_assert( + std::is_same_v>); + +// const + +static_assert( + std::is_same_v>); + +// sfinae +constexpr auto sfinae_call = + [](T) -> simd_friendly_equivalent_t { return {}; }; + +static_assert(std::invocable); + +struct NotSimdFriendly {}; +static_assert(!std::invocable); + +} // namespace simd_friendly_equivalent_test + +namespace integral_simd_friendly_equivalent_test { + +static_assert(std::is_same_v< // + std::int8_t, + integral_simd_friendly_equivalent>); + +struct Overloading { + constexpr int operator()(auto) { return 0; } + constexpr int operator()(has_simd_friendly_equivalent auto) { return 1; } + constexpr int operator()(has_integral_simd_friendly_equivalent auto) { + return 2; + } +}; + +// Subsumption tests +struct NotSimdFriendly {}; +enum class SomeInt {}; + +static_assert(Overloading{}(NotSimdFriendly{}) == 0); +static_assert(Overloading{}(float{}) == 1); +static_assert(Overloading{}(int{}) == 2); +static_assert(Overloading{}(SomeInt{}) == 2); + +} // namespace integral_simd_friendly_equivalent_test + +TEST_F(FollySimdTraitsTest, AsSimdFriendly) { + enum SomeEnum : int { Foo = 1, Bar, Baz }; + + static_assert(asSimdFriendly(SomeEnum::Foo) == 1); + + std::array arr{SomeEnum::Foo, SomeEnum::Bar, SomeEnum::Baz}; + folly::span castSpan = asSimdFriendly(folly::span(arr)); + ASSERT_THAT(castSpan, testing::ElementsAre(1, 2, 3)); +} + +template +void isSameTest(const T&, const U&) = delete; + +template +void isSameTest(const T&, const T&) {} + +template +void asSimdFriendlyUintTypeTest() { + isSameTest(asSimdFriendlyUint(From{}), To{}); + isSameTest(asSimdFriendlyUint(std::span{}), std::span{}); + isSameTest( + asSimdFriendlyUint(std::span{}), std::span{}); +} + +TEST_F(FollySimdTraitsTest, AsSimdFriendlyUint) { + enum SomeEnum : int { Foo = 1, Bar, Baz }; + + static_assert(asSimdFriendlyUint(SomeEnum::Foo) == 1U); + + asSimdFriendlyUintTypeTest(); + asSimdFriendlyUintTypeTest(); + asSimdFriendlyUintTypeTest(); + asSimdFriendlyUintTypeTest(); + asSimdFriendlyUintTypeTest(); + asSimdFriendlyUintTypeTest(); + asSimdFriendlyUintTypeTest(); +} + +} // namespace folly::detail