From ced4178643c8a49eed28037f7577aac0eb4202d1 Mon Sep 17 00:00:00 2001 From: paulfd Date: Sun, 15 Sep 2019 20:17:52 +0200 Subject: [PATCH] Added a SIMD helper for the "floating index to sample index + interpolation coefficients" --- benchmarks/BM_interpolationCast.cpp | 93 +++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 4 ++ sfizz/Config.h | 1 + sfizz/SIMDDummy.cpp | 6 ++ sfizz/SIMDHelpers.h | 32 ++++++++++ sfizz/SIMDSSE.cpp | 72 +++++++++++++++++++--- 6 files changed, 200 insertions(+), 8 deletions(-) create mode 100644 benchmarks/BM_interpolationCast.cpp diff --git a/benchmarks/BM_interpolationCast.cpp b/benchmarks/BM_interpolationCast.cpp new file mode 100644 index 00000000..0910a200 --- /dev/null +++ b/benchmarks/BM_interpolationCast.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2019, Paul Ferrand +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include +#include "../sfizz/SIMDHelpers.h" + +// In this one we have an array of jumps + +constexpr float maxJump { 4 }; + +class InterpolationCast : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) { + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0, maxJump }; + jumps = std::vector(state.range(0)); + leftCoeffs = std::vector(state.range(0)); + rightCoeffs = std::vector(state.range(0)); + floatJumps = std::vector(state.range(0)); + absl::c_generate(floatJumps, [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + std::vector jumps; + std::vector leftCoeffs; + std::vector rightCoeffs; + std::vector floatJumps; +}; + + +BENCHMARK_DEFINE_F(InterpolationCast, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + sfzInterpolationCast(floatJumps, absl::MakeSpan(jumps), absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs)); + } +} + +BENCHMARK_DEFINE_F(InterpolationCast, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + sfzInterpolationCast(floatJumps, absl::MakeSpan(jumps), absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs)); + } +} + +BENCHMARK_DEFINE_F(InterpolationCast, Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + sfzInterpolationCast(absl::MakeSpan(floatJumps).subspan(1), absl::MakeSpan(jumps).subspan(3), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1)); + } +} + +BENCHMARK_DEFINE_F(InterpolationCast, SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + sfzInterpolationCast(absl::MakeSpan(floatJumps).subspan(1), absl::MakeSpan(jumps).subspan(3), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1)); + } +} + + +// Register the function as a benchmark +BENCHMARK_REGISTER_F(InterpolationCast, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_REGISTER_F(InterpolationCast, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_REGISTER_F(InterpolationCast, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_REGISTER_F(InterpolationCast, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index c16b203a..ac2278a7 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -78,6 +78,9 @@ target_link_libraries(bm_meanSquared benchmark absl::span absl::algorithm) add_executable(bm_cumsum BM_cumsum.cpp ${SFIZZ_SIMD_SOURCES}) target_link_libraries(bm_cumsum benchmark absl::span absl::algorithm) +add_executable(bm_interpolationCast BM_interpolationCast.cpp ${SFIZZ_SIMD_SOURCES}) +target_link_libraries(bm_interpolationCast benchmark absl::span absl::algorithm) + add_custom_target(sfizz_benchmarks) add_dependencies(sfizz_benchmarks bm_opf_high_vs_low @@ -87,6 +90,7 @@ add_dependencies(sfizz_benchmarks bm_meanSquared bm_fill bm_cumsum + bm_interpolationCast bm_mathfuns bm_gain bm_looping diff --git a/sfizz/Config.h b/sfizz/Config.h index a2da6ec8..78f3141a 100644 --- a/sfizz/Config.h +++ b/sfizz/Config.h @@ -62,6 +62,7 @@ namespace SIMDConfig { constexpr bool copy { false }; constexpr bool pan { true }; constexpr bool cumsum { true }; + constexpr bool sfzInterpolationCast { true }; constexpr bool mean { false }; constexpr bool meanSquared { false }; } \ No newline at end of file diff --git a/sfizz/SIMDDummy.cpp b/sfizz/SIMDDummy.cpp index d57ead41..a56c5949 100644 --- a/sfizz/SIMDDummy.cpp +++ b/sfizz/SIMDDummy.cpp @@ -155,3 +155,9 @@ void cumsum(absl::Span input, absl::Span output { cumsum(input, output); } + +template<> +void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs) noexcept +{ + sfzInterpolationCast(floatJumps, jumps, leftCoeffs, rightCoeffs); +} \ No newline at end of file diff --git a/sfizz/SIMDHelpers.h b/sfizz/SIMDHelpers.h index 064999e4..9e8f161a 100644 --- a/sfizz/SIMDHelpers.h +++ b/sfizz/SIMDHelpers.h @@ -494,3 +494,35 @@ void cumsum(absl::Span input, absl::Span output) noexcept template <> void cumsum(absl::Span input, absl::Span output) noexcept; + +template +void snippetSFZInterpolationCast(const T*& floatJump, int*& jump, T*& leftCoeff, T*& rightCoeff) +{ + *jump = static_cast(*floatJump); + *rightCoeff = *floatJump - static_cast(*jump); + *leftCoeff = static_cast(1.0) - *rightCoeff; + leftCoeff++; + jump++; + rightCoeff++; + floatJump++; +} + +template +void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs) noexcept +{ + ASSERT(jumps.size() >= floatJumps.size()); + ASSERT(jumps.size() == leftCoeffs.size()); + ASSERT(jumps.size() == rightCoeffs.size()); + + auto floatJump = floatJumps.data(); + auto jump = jumps.data(); + auto leftCoeff = leftCoeffs.data(); + auto rightCoeff = rightCoeffs.data(); + const auto sentinel = floatJump + min(floatJumps.size(), jumps.size(), leftCoeffs.size(), rightCoeffs.size()); + + while (floatJump < sentinel) + snippetSFZInterpolationCast(floatJump, jump, leftCoeff, rightCoeff); +} + +template<> +void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs) noexcept; \ No newline at end of file diff --git a/sfizz/SIMDSSE.cpp b/sfizz/SIMDSSE.cpp index 8f702f3c..38ceae47 100644 --- a/sfizz/SIMDSSE.cpp +++ b/sfizz/SIMDSSE.cpp @@ -522,6 +522,26 @@ void add(absl::Span input, absl::Span output) n snippetAdd(in, out); } +template <> +void add(float value, absl::Span output) noexcept +{ + auto* out = output.begin(); + auto* sentinel = output.end(); + const auto* lastAligned = prevAligned(sentinel); + + while (unaligned(out) && out < lastAligned) + snippetAdd(value, out); + + auto mmValue = _mm_set_ps1(value); + while (out < lastAligned) { + _mm_store_ps(out, _mm_add_ps(mmValue, _mm_load_ps(out))); + out += TypeAlignment; + } + + while (out < sentinel) + snippetAdd(value, out); +} + template <> void subtract(absl::Span input, absl::Span output) noexcept { @@ -582,8 +602,8 @@ void pan(absl::Span panEnvelope, absl::Span lef const auto mmOne = _mm_set_ps1(1.0f); const auto mmPiFour = _mm_set_ps1(piFour); - __m128 mmCos; - __m128 mmSin; + __m128 mmCos; + __m128 mmSin; while (pan < lastAligned) { auto mmPan = _mm_load_ps(pan); mmPan = _mm_add_ps(mmOne, mmPan); @@ -615,9 +635,9 @@ float mean(absl::Span vector) noexcept while (unaligned(value) && value < lastAligned) result += *value++; - + auto mmSums = _mm_setzero_ps(); - while(value < lastAligned) { + while (value < lastAligned) { mmSums = _mm_add_ps(mmSums, _mm_load_ps(value)); value += TypeAlignment; } @@ -625,7 +645,7 @@ float mean(absl::Span vector) noexcept std::array sseResult; _mm_store_ps(sseResult.data(), mmSums); - for (auto sseValue: sseResult) + for (auto sseValue : sseResult) result += sseValue; while (value < sentinel) @@ -649,9 +669,9 @@ float meanSquared(absl::Span vector) noexcept result += (*value) * (*value); value++; } - + auto mmSums = _mm_setzero_ps(); - while(value < lastAligned) { + while (value < lastAligned) { const auto mmValues = _mm_load_ps(value); mmSums = _mm_add_ps(mmSums, _mm_mul_ps(mmValues, mmValues)); value += TypeAlignment; @@ -660,7 +680,7 @@ float meanSquared(absl::Span vector) noexcept std::array sseResult; _mm_store_ps(sseResult.data(), mmSums); - for (auto sseValue: sseResult) + for (auto sseValue : sseResult) result += sseValue; while (value < sentinel) { @@ -702,3 +722,39 @@ void cumsum(absl::Span input, absl::Span output while (in < sentinel) snippetCumsum(in, out); } + +template <> +void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs) noexcept +{ + ASSERT(jumps.size() >= floatJumps.size()); + ASSERT(jumps.size() == leftCoeffs.size()); + ASSERT(jumps.size() == rightCoeffs.size()); + + auto floatJump = floatJumps.data(); + auto jump = jumps.data(); + auto leftCoeff = leftCoeffs.data(); + auto rightCoeff = rightCoeffs.data(); + const auto sentinel = floatJump + min(floatJumps.size(), jumps.size(), leftCoeffs.size(), rightCoeffs.size()); + const auto lastAligned = prevAligned(sentinel); + + while (unaligned(floatJump, reinterpret_cast(jump), leftCoeff, rightCoeff) && floatJump < lastAligned) + snippetSFZInterpolationCast(floatJump, jump, leftCoeff, rightCoeff); + + while (floatJump < lastAligned) { + auto mmFloatJumps = _mm_load_ps(floatJump); + auto mmIndices = _mm_cvtps_epi32(_mm_sub_ps(mmFloatJumps, _mm_set_ps1(0.4999999552965164184570312f))); + _mm_store_si128(reinterpret_cast<__m128i*>(jump), mmIndices); + + auto mmRight = _mm_sub_ps(mmFloatJumps, _mm_cvtepi32_ps(mmIndices)); + auto mmLeft = _mm_sub_ps(_mm_set_ps1(1.0f), mmRight); + _mm_store_ps(leftCoeff, mmLeft); + _mm_store_ps(rightCoeff, mmRight); + floatJump += TypeAlignment; + jump += TypeAlignment; + leftCoeff += TypeAlignment; + rightCoeff += TypeAlignment; + } + + while(floatJump < sentinel) + snippetSFZInterpolationCast(floatJump, jump, leftCoeff, rightCoeff); +} \ No newline at end of file