diff --git a/benchmarks/BM_multiplyAddFixedGain.cpp b/benchmarks/BM_multiplyAddFixedGain.cpp new file mode 100644 index 00000000..b484389d --- /dev/null +++ b/benchmarks/BM_multiplyAddFixedGain.cpp @@ -0,0 +1,78 @@ +// 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 +#include +#include +#include +#include +#include + +class MultiplyAddFixedGain : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) { + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0, 1 }; + input = std::vector(state.range(0)); + output = std::vector(state.range(0)); + gain = dist(gen); + std::fill(output.begin(), output.end(), 1.0f ); + std::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + float gain = {}; + std::vector input; + std::vector output; +}; + +BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Straight)(benchmark::State& state) { + for (auto _ : state) + { + for (int i = 0; i < state.range(0); ++i) + output[i] += gain * input[i]; + } +} + +BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + sfz::multiplyAdd(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + sfz::multiplyAdd(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + sfz::multiplyAdd(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + sfz::multiplyAdd(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_REGISTER_F(MultiplyAddFixedGain, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAddFixedGain, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAddFixedGain, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAddFixedGain, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAddFixedGain, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index dd7bfb65..4f27ca1a 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -46,6 +46,7 @@ target_link_libraries(bm_ADSR PRIVATE sfizz::sfizz) sfizz_add_benchmark(bm_add BM_add.cpp) sfizz_add_benchmark(bm_multiplyAdd BM_multiplyAdd.cpp) +sfizz_add_benchmark(bm_multiplyAddFixedGain BM_multiplyAddFixedGain.cpp) sfizz_add_benchmark(bm_subtract BM_subtract.cpp) sfizz_add_benchmark(bm_copy BM_copy.cpp) sfizz_add_benchmark(bm_pan BM_pan.cpp) diff --git a/src/sfizz/SIMDDummy.cpp b/src/sfizz/SIMDDummy.cpp index 22a1140b..2a0ca34d 100644 --- a/src/sfizz/SIMDDummy.cpp +++ b/src/sfizz/SIMDDummy.cpp @@ -76,6 +76,12 @@ void sfz::multiplyAdd(absl::Span gain, absl::Span(gain, input, output); } +template <> +void sfz::multiplyAdd(const float gain, absl::Span input, absl::Span output) noexcept +{ + multiplyAdd(gain, input, output); +} + template <> float sfz::loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept { diff --git a/src/sfizz/SIMDHelpers.h b/src/sfizz/SIMDHelpers.h index f4c5a2d7..b3725129 100644 --- a/src/sfizz/SIMDHelpers.h +++ b/src/sfizz/SIMDHelpers.h @@ -491,6 +491,12 @@ namespace _internals { { *output++ += (*gain++) * (*input++); } + + template + inline void snippetMultiplyAdd(const T gain, const T*& input, T*& output) + { + *output++ += gain * (*input++); + } } /** @@ -520,6 +526,20 @@ void multiplyAdd(absl::Span gain, absl::Span input, absl::Span template <> void multiplyAdd(absl::Span gain, absl::Span input, absl::Span output) noexcept; +template +void multiplyAdd(const T gain, absl::Span input, absl::Span output) noexcept +{ + ASSERT(input.size() <= output.size()); + auto* in = input.begin(); + auto* out = output.begin(); + auto* sentinel = out + std::min(output.size(), input.size()); + while (out < sentinel) + _internals::snippetMultiplyAdd(gain, in, out); +} + +template <> +void multiplyAdd(const float gain, absl::Span input, absl::Span output) noexcept; + namespace _internals { template inline void snippetRampLinear(T*& output, T& value, T step) diff --git a/src/sfizz/SIMDSSE.cpp b/src/sfizz/SIMDSSE.cpp index ba98c12d..74adfb66 100644 --- a/src/sfizz/SIMDSSE.cpp +++ b/src/sfizz/SIMDSSE.cpp @@ -316,6 +316,29 @@ void sfz::multiplyAdd(absl::Span gain, absl::Span(g, in, out); } +template <> +void sfz::multiplyAdd(const float gain, absl::Span input, absl::Span output) noexcept +{ + auto* in = input.begin(); + auto* out = output.begin(); + const auto size = std::min(output.size(), input.size()); + const auto* lastAligned = prevAligned(output.begin() + size); + + while (unaligned(out, in) && out < lastAligned) + _internals::snippetMultiplyAdd(gain, in, out); + + auto mmGain = _mm_set1_ps(gain); + while (out < lastAligned) { + auto mmOut = _mm_load_ps(out); + mmOut = _mm_add_ps(_mm_mul_ps(mmGain, _mm_load_ps(in)), mmOut); + _mm_store_ps(out, mmOut); + incrementAll(in, out); + } + + while (out < output.end()) + _internals::snippetMultiplyAdd(gain, in, out); +} + template <> float sfz::loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs,