From dd08115dc437d91dc251a1a7afc016a41cd37146 Mon Sep 17 00:00:00 2001 From: paulfd Date: Sat, 7 Sep 2019 16:28:33 +0200 Subject: [PATCH] Added a panning SIMD helper and a benchmark against block operations and a scalar computation --- benchmarks/BM_pan.cpp | 96 +++++++++++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 4 ++ sfizz/Config.h | 1 + sfizz/SIMDDummy.cpp | 6 +++ sfizz/SIMDHelpers.h | 26 ++++++++++- sfizz/SIMDSSE.cpp | 36 +++++++++++++++ sfizz/Voice.cpp | 1 + 7 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 benchmarks/BM_pan.cpp diff --git a/benchmarks/BM_pan.cpp b/benchmarks/BM_pan.cpp new file mode 100644 index 00000000..4530d4e7 --- /dev/null +++ b/benchmarks/BM_pan.cpp @@ -0,0 +1,96 @@ +// 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 +#include "../sfizz/SIMDHelpers.h" +#include "../sfizz/Config.h" +#include "absl/types/span.h" + +class PanArray : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) { + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0.001, 1 }; + pan = std::vector(state.range(0)); + left = std::vector(state.range(0)); + right = std::vector(state.range(0)); + std::generate(pan.begin(), pan.end(), [&]() { return dist(gen); }); + std::generate(right.begin(), right.end(), [&]() { return dist(gen); }); + std::generate(left.begin(), left.end(), [&]() { return dist(gen); }); + temp1 = std::vector(state.range(0)); + temp2 = std::vector(state.range(0)); + span1 = absl::MakeSpan(temp1); + span2 = absl::MakeSpan(temp2); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + std::vector pan; + std::vector left; + std::vector right; + std::vector temp1; + std::vector temp2; + absl::Span span1; + absl::Span span2; +}; + + +BENCHMARK_DEFINE_F(PanArray, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + ::pan(pan, absl::MakeSpan(left), absl::MakeSpan(right)); + } +} + +BENCHMARK_DEFINE_F(PanArray, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + ::pan(pan, absl::MakeSpan(left), absl::MakeSpan(right)); + } +} + +BENCHMARK_DEFINE_F(PanArray, BlockOps)(benchmark::State& state) { + for (auto _ : state) + { + ::fill(span2, 1.0f); + ::add(span1, span2); + ::applyGain(piFour, span2); + ::cos(span2, span1); + ::sin(span2, span2); + ::applyGain(span1, absl::MakeSpan(left)); + ::applyGain(span2, absl::MakeSpan(right)); + } +} + +BENCHMARK_REGISTER_F(PanArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(PanArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(PanArray, BlockOps)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 98502cf4..c480f66e 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -66,6 +66,9 @@ target_link_libraries(bm_subtract benchmark absl::span absl::algorithm) add_executable(bm_copy BM_copy.cpp ${SFIZZ_SIMD_SOURCES}) target_link_libraries(bm_copy benchmark absl::span absl::algorithm) +add_executable(bm_pan BM_pan.cpp ${SFIZZ_SIMD_SOURCES}) +target_link_libraries(bm_pan benchmark absl::span absl::algorithm) + add_custom_target(sfizz_benchmarks) add_dependencies(sfizz_benchmarks bm_opf_high_vs_low @@ -79,6 +82,7 @@ add_dependencies(sfizz_benchmarks bm_ramp bm_ADSR bm_add + bm_pan bm_subtract bm_multiplyAdd ) \ No newline at end of file diff --git a/sfizz/Config.h b/sfizz/Config.h index b313e441..5875e967 100644 --- a/sfizz/Config.h +++ b/sfizz/Config.h @@ -57,4 +57,5 @@ namespace SIMDConfig { constexpr bool subtract { false }; constexpr bool multiplyAdd { false }; constexpr bool copy { false }; + constexpr bool pan { true }; } \ No newline at end of file diff --git a/sfizz/SIMDDummy.cpp b/sfizz/SIMDDummy.cpp index cac268e6..ab1770f7 100644 --- a/sfizz/SIMDDummy.cpp +++ b/sfizz/SIMDDummy.cpp @@ -124,4 +124,10 @@ template <> void copy(absl::Span input, absl::Span output) noexcept { copy(input, output); +} + +template <> +void pan(absl::Span panEnvelope, absl::Span leftBuffer, absl::Span rightBuffer) noexcept +{ + pan(panEnvelope, leftBuffer, rightBuffer); } \ No newline at end of file diff --git a/sfizz/SIMDHelpers.h b/sfizz/SIMDHelpers.h index 0b84b65c..ff728a35 100644 --- a/sfizz/SIMDHelpers.h +++ b/sfizz/SIMDHelpers.h @@ -388,4 +388,28 @@ void copy(absl::Span input, absl::Span output) noexcept } template <> -void copy(absl::Span input, absl::Span output) noexcept; \ No newline at end of file +void copy(absl::Span input, absl::Span output) noexcept; + +template +inline void snippetPan(const T*& pan, T*& left, T*& right) +{ + const auto circlePan = piFour * (static_cast(1.0) + *pan++); + *left++ *= std::cos(circlePan); + *right++ *= std::sin(circlePan); +} + +template +void pan(absl::Span panEnvelope, absl::Span leftBuffer, absl::Span rightBuffer) noexcept +{ + ASSERT(leftBuffer.size() >= panEnvelope.size()); + ASSERT(rightBuffer.size() >= panEnvelope.size()); + auto* pan = panEnvelope.begin(); + auto* left = leftBuffer.begin(); + auto* right = rightBuffer.begin(); + auto* sentinel = pan + min(panEnvelope.size(), leftBuffer.size(), rightBuffer.size()); + while (pan < sentinel) + snippetPan(pan, left, right); +} + +template <> +void pan(absl::Span panEnvelope, absl::Span leftBuffer, absl::Span rightBuffer) noexcept; \ No newline at end of file diff --git a/sfizz/SIMDSSE.cpp b/sfizz/SIMDSSE.cpp index b13f4081..7684370b 100644 --- a/sfizz/SIMDSSE.cpp +++ b/sfizz/SIMDSSE.cpp @@ -564,4 +564,40 @@ void copy(absl::Span input, absl::Span output) while (out < sentinel) snippetCopy(in, out); +} + +template <> +void pan(absl::Span panEnvelope, absl::Span leftBuffer, absl::Span rightBuffer) noexcept +{ + ASSERT(leftBuffer.size() >= panEnvelope.size()); + ASSERT(rightBuffer.size() >= panEnvelope.size()); + auto* pan = panEnvelope.begin(); + auto* left = leftBuffer.begin(); + auto* right = rightBuffer.begin(); + auto* sentinel = pan + min(panEnvelope.size(), leftBuffer.size(), rightBuffer.size()); + const auto* lastAligned = prevAligned(sentinel); + + while (unaligned(pan, left, right) && pan < lastAligned) + snippetPan(pan, left, right); + + const auto mmOne = _mm_set_ps1(1.0f); + const auto mmPiFour = _mm_set_ps1(piFour); + __m128 mmCos; + __m128 mmSin; + while (pan < lastAligned) { + auto mmPan = _mm_load_ps(pan); + mmPan = _mm_add_ps(mmOne, mmPan); + mmPan = _mm_mul_ps(mmOne, mmPiFour); + sincos_ps(mmPan, &mmSin, &mmCos); + auto mmLeft = _mm_mul_ps(mmCos, _mm_load_ps(left)); + auto mmRight = _mm_mul_ps(mmCos, _mm_load_ps(left)); + _mm_store_ps(left, mmLeft); + _mm_store_ps(right, mmRight); + left += TypeAlignment; + right += TypeAlignment; + pan += TypeAlignment; + } + + while (pan < sentinel) + snippetPan(pan, left, right); } \ No newline at end of file diff --git a/sfizz/Voice.cpp b/sfizz/Voice.cpp index cd9b9054..d8e40f99 100644 --- a/sfizz/Voice.cpp +++ b/sfizz/Voice.cpp @@ -234,6 +234,7 @@ void sfz::Voice::processMono(AudioSpan buffer) noexcept panEnvelope.getBlock(span1); // We assume that the pan envelope is already normalized between -1 and 1 + // Check bm_pan for your architecture to check if it's interesting to use the pan helper instead ::fill(span2, 1.0f); ::add(span1, span2); ::applyGain(piFour, span2);