From ff8ac64d61ac846d89f975d7af88b89c7db064a8 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 12 Aug 2020 16:12:04 +0200 Subject: [PATCH] Add clamping SIMD helper --- benchmarks/BM_clamp.cpp | 52 ++++++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 1 + src/sfizz/SIMDHelpers.cpp | 10 +++++++ src/sfizz/SIMDHelpers.h | 26 +++++++++++++++++ src/sfizz/simd/HelpersSSE.cpp | 31 ++++++++++++++++++++ src/sfizz/simd/HelpersSSE.h | 1 + src/sfizz/simd/HelpersScalar.h | 14 +++++++++ tests/SIMDHelpersT.cpp | 27 ++++++++++++++++++ 8 files changed, 162 insertions(+) create mode 100644 benchmarks/BM_clamp.cpp diff --git a/benchmarks/BM_clamp.cpp b/benchmarks/BM_clamp.cpp new file mode 100644 index 00000000..beb66ae5 --- /dev/null +++ b/benchmarks/BM_clamp.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SIMDHelpers.h" +#include "Macros.h" +#include +#include +#include +#include +#include +#include + +class ClampArray : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) { + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0, 10 }; + input = std::vector(state.range(0)); + std::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& state) { + UNUSED(state); + } + + std::vector input; +}; + +BENCHMARK_DEFINE_F(ClampArray, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + sfz::setSIMDOpStatus(sfz::SIMDOps::clampAll, false); + sfz::clampAll(absl::MakeSpan(input), 1.2f, 3.8f); + } +} + +BENCHMARK_DEFINE_F(ClampArray, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + sfz::setSIMDOpStatus(sfz::SIMDOps::clampAll, true); + sfz::clampAll(absl::MakeSpan(input), 1.2f, 3.8f); + } +} + + +BENCHMARK_REGISTER_F(ClampArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(ClampArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index cb38feb6..0c6e981e 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -59,6 +59,7 @@ sfizz_add_benchmark(bm_maps BM_maps.cpp) target_link_libraries(bm_maps PRIVATE absl::flat_hash_map) sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp) sfizz_add_benchmark(bm_random BM_random.cpp) +sfizz_add_benchmark(bm_clamp BM_clamp.cpp) sfizz_add_benchmark(bm_logger BM_logger.cpp) target_link_libraries(bm_logger PRIVATE sfizz::sfizz) diff --git a/src/sfizz/SIMDHelpers.cpp b/src/sfizz/SIMDHelpers.cpp index c0e21d4e..c631a0ca 100644 --- a/src/sfizz/SIMDHelpers.cpp +++ b/src/sfizz/SIMDHelpers.cpp @@ -40,6 +40,7 @@ struct SIMDDispatch { decltype(&diffScalar) diff = &diffScalar; decltype(&meanScalar) mean = &meanScalar; decltype(&meanSquaredScalar) meanSquared = &meanSquaredScalar; + decltype(&clampAllScalar) clampAll = &clampAllScalar; private: std::array(SIMDOps::_sentinel)> simdStatus; @@ -84,6 +85,7 @@ void SIMDDispatch::setStatus(SIMDOps op, bool enable) SIMD_OP(diff) SIMD_OP(mean) SIMD_OP(meanSquared) + SIMD_OP(clampAll) } #undef SIMD_OP } @@ -119,6 +121,7 @@ void SIMDDispatch::setStatus(SIMDOps op, bool enable) SIMD_OP(diff) SIMD_OP(mean) SIMD_OP(meanSquared) + SIMD_OP(clampAll) } } #undef SIMD_OP @@ -159,6 +162,7 @@ void SIMDDispatch::resetStatus() setStatus(SIMDOps::mean, false); setStatus(SIMDOps::meanSquared, false); setStatus(SIMDOps::upsampling, true); + setStatus(SIMDOps::clampAll, false); } /// @@ -301,4 +305,10 @@ void diff(const float* input, float* output, unsigned size) noexcept return simdDispatch().diff(input, output, size); } +template <> +void clampAll(float* input, float low, float high, unsigned size) noexcept +{ + return simdDispatch().clampAll(input, low, high, size); +} + } diff --git a/src/sfizz/SIMDHelpers.h b/src/sfizz/SIMDHelpers.h index ec27824a..17b21c88 100644 --- a/src/sfizz/SIMDHelpers.h +++ b/src/sfizz/SIMDHelpers.h @@ -59,6 +59,7 @@ enum class SIMDOps { mean, meanSquared, upsampling, + clampAll, _sentinel // }; @@ -622,4 +623,29 @@ void diff(absl::Span input, absl::Span output) noexcept diff(input.data(), output.data(), minSpanSize(input, output)); } +/** + * @brief Clamp a vector between a low and high bound + * + * @tparam T the underlying type + * @param input + * @param output + * @param low + * @param high + * @param size + */ +template +void clampAll(T* input, T low, T high, unsigned size) noexcept +{ + clampAllScalar(input, low, high, size); +} + +template <> +void clampAll(float* input, float low, float high, unsigned size) noexcept; + +template +void clampAll(absl::Span input, T low, T high) noexcept +{ + clampAll(input.data(), low, high, input.size()); +} + } // namespace sfz diff --git a/src/sfizz/simd/HelpersSSE.cpp b/src/sfizz/simd/HelpersSSE.cpp index 079d6a75..7a0e7beb 100644 --- a/src/sfizz/simd/HelpersSSE.cpp +++ b/src/sfizz/simd/HelpersSSE.cpp @@ -472,3 +472,34 @@ void diffSSE(const float* input, float* output, unsigned size) noexcept incrementAll(input, output); } } + +void clampAllSSE(float* input, float low, float high, unsigned size) noexcept +{ + if (size == 0) + return; + + const auto sentinel = input + size; + +#if SFIZZ_HAVE_SSE2 + const auto* lastAligned = prevAligned(sentinel); + while (unaligned(input) && input < lastAligned){ + const float clampedAbove = *input > high ? high : *input; + *input = clampedAbove < low ? low : clampedAbove; + incrementAll(input); + } + + const auto mmLow = _mm_set1_ps(low); + const auto mmHigh = _mm_set1_ps(high); + while (input < lastAligned) { + const auto mmIn = _mm_load_ps(input); + _mm_store_ps(input, _mm_max_ps(_mm_min_ps(mmIn, mmHigh), mmLow)); + incrementAll(input); + } +#endif + + while (input < sentinel) { + const float clampedAbove = *input > high ? high : *input; + *input = clampedAbove < low ? low : clampedAbove; + incrementAll(input); + } +} diff --git a/src/sfizz/simd/HelpersSSE.h b/src/sfizz/simd/HelpersSSE.h index a6d5e0a5..7100ee89 100644 --- a/src/sfizz/simd/HelpersSSE.h +++ b/src/sfizz/simd/HelpersSSE.h @@ -25,3 +25,4 @@ float meanSSE(const float* vector, unsigned size) noexcept; float meanSquaredSSE(const float* vector, unsigned size) noexcept; void cumsumSSE(const float* input, float* output, unsigned size) noexcept; void diffSSE(const float* input, float* output, unsigned size) noexcept; +void clampAllSSE(float* input, float low, float high, unsigned size) noexcept; diff --git a/src/sfizz/simd/HelpersScalar.h b/src/sfizz/simd/HelpersScalar.h index ab1415a7..aa2017d1 100644 --- a/src/sfizz/simd/HelpersScalar.h +++ b/src/sfizz/simd/HelpersScalar.h @@ -186,3 +186,17 @@ void diffScalar(const T* input, T* output, unsigned size) noexcept incrementAll(input, output); } } + +template +void clampAllScalar(T* input, T low, T high, unsigned size ) noexcept +{ + if (size == 0) + return; + + const auto sentinel = input + size; + while (input < sentinel) { + const float clampedAbove = *input > high ? high : *input; + *input = clampedAbove < low ? low : clampedAbove; + incrementAll(input); + } +} diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp index 9a7c9175..ebc94feb 100644 --- a/tests/SIMDHelpersT.cpp +++ b/tests/SIMDHelpersT.cpp @@ -812,3 +812,30 @@ TEST_CASE("[Helpers] Width Scalar") REQUIRE(right[0] == Approx(1.0f).margin(0.001f)); } } + +TEST_CASE("[Helpers] clampAll") +{ + std::array inputScalar { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f }; + std::array inputSIMD; + sfz::copy(inputScalar, absl::MakeSpan(inputSIMD)); + std::array expected { 2.5f, 2.5f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 8.0f, 8.0f }; + sfz::setSIMDOpStatus(sfz::SIMDOps::clampAll, false); + sfz::clampAll(absl::MakeSpan(inputScalar), 2.5f, 8.0f); + REQUIRE( approxEqual(inputScalar, expected) ); + sfz::setSIMDOpStatus(sfz::SIMDOps::clampAll, true); + sfz::clampAll(absl::MakeSpan(inputSIMD), 2.5f, 8.0f); + REQUIRE( approxEqual(inputSIMD, expected) ); +} + +TEST_CASE("[Helpers] clampAll (SIMD vs scalar)") +{ + std::vector inputScalar(medBufferSize); + std::vector inputSIMD(medBufferSize); + absl::c_iota(inputScalar, 2.0f); + sfz::copy(inputScalar, absl::MakeSpan(inputSIMD)); + sfz::setSIMDOpStatus(sfz::SIMDOps::clampAll, false); + sfz::clampAll(absl::MakeSpan(inputScalar), 10.0f, 50.0f); + sfz::setSIMDOpStatus(sfz::SIMDOps::clampAll, true); + sfz::clampAll(absl::MakeSpan(inputSIMD), 10.0f, 50.0f); + REQUIRE( approxEqual(inputScalar, inputSIMD) ); +}