diff --git a/benchmarks/BM_allWithin.cpp b/benchmarks/BM_allWithin.cpp new file mode 100644 index 00000000..c1b3bec0 --- /dev/null +++ b/benchmarks/BM_allWithin.cpp @@ -0,0 +1,70 @@ +// 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 WithinArray : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) { + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 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(WithinArray, ScalarFalse)(benchmark::State& state) { + for (auto _ : state) + { + sfz::setSIMDOpStatus(sfz::SIMDOps::allWithin, false); + sfz::allWithin(input, 1.2f, 3.8f); + } +} + +BENCHMARK_DEFINE_F(WithinArray, SIMDFalse)(benchmark::State& state) { + for (auto _ : state) + { + sfz::setSIMDOpStatus(sfz::SIMDOps::allWithin, true); + sfz::allWithin(input, 1.2f, 3.8f); + } +} + +BENCHMARK_DEFINE_F(WithinArray, ScalarTrue)(benchmark::State& state) { + for (auto _ : state) + { + sfz::setSIMDOpStatus(sfz::SIMDOps::allWithin, false); + sfz::allWithin(input, 0.0f, 11.0f); + } +} + +BENCHMARK_DEFINE_F(WithinArray, SIMDTrue)(benchmark::State& state) { + for (auto _ : state) + { + sfz::setSIMDOpStatus(sfz::SIMDOps::allWithin, true); + sfz::allWithin(input, 0.0f, 11.0f); + } +} + + +BENCHMARK_REGISTER_F(WithinArray, ScalarFalse)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(WithinArray, SIMDFalse)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(WithinArray, ScalarTrue)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(WithinArray, SIMDTrue)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 0c6e981e..71fd0b14 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -60,6 +60,7 @@ 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_allWithin BM_allWithin.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 c631a0ca..c86cecf8 100644 --- a/src/sfizz/SIMDHelpers.cpp +++ b/src/sfizz/SIMDHelpers.cpp @@ -41,6 +41,7 @@ struct SIMDDispatch { decltype(&meanScalar) mean = &meanScalar; decltype(&meanSquaredScalar) meanSquared = &meanSquaredScalar; decltype(&clampAllScalar) clampAll = &clampAllScalar; + decltype(&allWithinScalar) allWithin = &allWithinScalar; private: std::array(SIMDOps::_sentinel)> simdStatus; @@ -86,6 +87,7 @@ void SIMDDispatch::setStatus(SIMDOps op, bool enable) SIMD_OP(mean) SIMD_OP(meanSquared) SIMD_OP(clampAll) + SIMD_OP(allWithin) } #undef SIMD_OP } @@ -122,6 +124,7 @@ void SIMDDispatch::setStatus(SIMDOps op, bool enable) SIMD_OP(mean) SIMD_OP(meanSquared) SIMD_OP(clampAll) + SIMD_OP(allWithin) } } #undef SIMD_OP @@ -163,6 +166,7 @@ void SIMDDispatch::resetStatus() setStatus(SIMDOps::meanSquared, false); setStatus(SIMDOps::upsampling, true); setStatus(SIMDOps::clampAll, false); + setStatus(SIMDOps::allWithin, true); } /// @@ -308,7 +312,13 @@ void diff(const float* input, float* output, unsigned size) noexcept template <> void clampAll(float* input, float low, float high, unsigned size) noexcept { - return simdDispatch().clampAll(input, low, high, size); + simdDispatch().clampAll(input, low, high, size); +} + +template <> +bool allWithin(const float* input, float low, float high, unsigned size) noexcept +{ + return simdDispatch().allWithin(input, low, high, size); } } diff --git a/src/sfizz/SIMDHelpers.h b/src/sfizz/SIMDHelpers.h index 17b21c88..1cfa48d1 100644 --- a/src/sfizz/SIMDHelpers.h +++ b/src/sfizz/SIMDHelpers.h @@ -60,6 +60,7 @@ enum class SIMDOps { meanSquared, upsampling, clampAll, + allWithin, _sentinel // }; @@ -628,7 +629,6 @@ void diff(absl::Span input, absl::Span output) noexcept * * @tparam T the underlying type * @param input - * @param output * @param low * @param high * @param size @@ -648,4 +648,28 @@ void clampAll(absl::Span input, T low, T high) noexcept clampAll(input.data(), low, high, input.size()); } +/** + * @brief Check that all values are within bounds (inclusive) + * + * @tparam T the underlying type + * @param input + * @param low + * @param high + * @param size + */ +template +bool allWithin(const T* input, T low, T high, unsigned size) noexcept +{ + return allWithinScalar(input, low, high, size); +} + +template <> +bool allWithin(const float* input, float low, float high, unsigned size) noexcept; + +template +bool allWithin(absl::Span input, T low, T high) noexcept +{ + return allWithin(input.data(), low, high, input.size()); +} + } // namespace sfz diff --git a/src/sfizz/simd/HelpersSSE.cpp b/src/sfizz/simd/HelpersSSE.cpp index 7a0e7beb..3ae8b984 100644 --- a/src/sfizz/simd/HelpersSSE.cpp +++ b/src/sfizz/simd/HelpersSSE.cpp @@ -503,3 +503,44 @@ void clampAllSSE(float* input, float low, float high, unsigned size) noexcept incrementAll(input); } } + +bool allWithinSSE(const float* input, float low, float high, unsigned size) noexcept +{ + if (size == 0) + return true; + + if (low > high) + std::swap(low, high); + + const auto sentinel = input + size; + +#if SFIZZ_HAVE_SSE2 + const auto* lastAligned = prevAligned(sentinel); + while (unaligned(input) && input < lastAligned){ + if (*input < low || *input > high) + return false; + + 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); + const auto mmOutside = _mm_or_ps(_mm_cmplt_ps(mmIn, mmLow), _mm_cmpgt_ps(mmIn, mmHigh)); + if (_mm_movemask_ps(mmOutside) != 0) + return false; + + incrementAll(input); + } +#endif + + while (input < sentinel) { + if (*input < low || *input > high) + return false; + + incrementAll(input); + } + + return true; +} diff --git a/src/sfizz/simd/HelpersSSE.h b/src/sfizz/simd/HelpersSSE.h index 7100ee89..cff28650 100644 --- a/src/sfizz/simd/HelpersSSE.h +++ b/src/sfizz/simd/HelpersSSE.h @@ -26,3 +26,4 @@ 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; +bool allWithinSSE(const float* input, float low, float high, unsigned size) noexcept; diff --git a/src/sfizz/simd/HelpersScalar.h b/src/sfizz/simd/HelpersScalar.h index aa2017d1..d5ac1162 100644 --- a/src/sfizz/simd/HelpersScalar.h +++ b/src/sfizz/simd/HelpersScalar.h @@ -200,3 +200,23 @@ void clampAllScalar(T* input, T low, T high, unsigned size ) noexcept incrementAll(input); } } + +template +bool allWithinScalar(const T* input, T low, T high, unsigned size ) noexcept +{ + if (size == 0) + return true; + + if (low > high) + std::swap(low, high); + + const auto sentinel = input + size; + while (input < sentinel) { + if (*input < low || *input > high) + return false; + + incrementAll(input); + } + + return true; +} diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp index ebc94feb..2dbfd94b 100644 --- a/tests/SIMDHelpersT.cpp +++ b/tests/SIMDHelpersT.cpp @@ -839,3 +839,14 @@ TEST_CASE("[Helpers] clampAll (SIMD vs scalar)") sfz::clampAll(absl::MakeSpan(inputSIMD), 10.0f, 50.0f); REQUIRE( approxEqual(inputScalar, inputSIMD) ); } + +TEST_CASE("[Helpers] allWithin") +{ + std::array input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f }; + sfz::setSIMDOpStatus(sfz::SIMDOps::allWithin, false); + REQUIRE( sfz::allWithin(input, 0.5f, 11.0f) ); + REQUIRE( !sfz::allWithin(input, 2.5f, 8.0f) ); + sfz::setSIMDOpStatus(sfz::SIMDOps::allWithin, true); + REQUIRE( sfz::allWithin(input, 0.5f, 11.0f) ); + REQUIRE( !sfz::allWithin(input, 2.5f, 8.0f) ); +}