From 617e2375b3cc3e36ba9c5d4c2af1d9e50c79357f Mon Sep 17 00:00:00 2001 From: paulfd Date: Sat, 14 Sep 2019 09:17:02 +0200 Subject: [PATCH] Added a cumsum helper --- benchmarks/BM_cumsum.cpp | 84 +++++++++++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 4 ++ sfizz/Config.h | 1 + sfizz/SIMDDummy.cpp | 6 +++ sfizz/SIMDHelpers.h | 28 ++++++++++++- sfizz/SIMDSSE.cpp | 36 ++++++++++++++++- tests/SIMDHelpersT.cpp | 20 ++++++++++ 7 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 benchmarks/BM_cumsum.cpp diff --git a/benchmarks/BM_cumsum.cpp b/benchmarks/BM_cumsum.cpp new file mode 100644 index 00000000..08a49859 --- /dev/null +++ b/benchmarks/BM_cumsum.cpp @@ -0,0 +1,84 @@ +// 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" + +class CumsumArray : 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)); + std::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + std::vector input; + std::vector output; +}; + + +BENCHMARK_DEFINE_F(CumsumArray, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + cumsum(input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(CumsumArray, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + cumsum(input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(CumsumArray, Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + cumsum(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_DEFINE_F(CumsumArray, SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + cumsum(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_REGISTER_F(CumsumArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(CumsumArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(CumsumArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(CumsumArray, SIMD_Unaligned)->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 618b942b..c16b203a 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -75,6 +75,9 @@ target_link_libraries(bm_mean benchmark absl::span absl::algorithm) add_executable(bm_meanSquared BM_meanSquared.cpp ${SFIZZ_SIMD_SOURCES}) 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_custom_target(sfizz_benchmarks) add_dependencies(sfizz_benchmarks bm_opf_high_vs_low @@ -83,6 +86,7 @@ add_dependencies(sfizz_benchmarks bm_mean bm_meanSquared bm_fill + bm_cumsum bm_mathfuns bm_gain bm_looping diff --git a/sfizz/Config.h b/sfizz/Config.h index 7ac7bb06..a2da6ec8 100644 --- a/sfizz/Config.h +++ b/sfizz/Config.h @@ -61,6 +61,7 @@ namespace SIMDConfig { constexpr bool multiplyAdd { false }; constexpr bool copy { false }; constexpr bool pan { true }; + constexpr bool cumsum { 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 f00e587c..f91549de 100644 --- a/sfizz/SIMDDummy.cpp +++ b/sfizz/SIMDDummy.cpp @@ -142,4 +142,10 @@ template <> float meanSquared(absl::Span vector) noexcept { return meanSquared(vector); +} + +template <> +void cumsum(absl::Span input, absl::Span output) noexcept +{ + cumsum(input, output); } \ No newline at end of file diff --git a/sfizz/SIMDHelpers.h b/sfizz/SIMDHelpers.h index 0196922c..c06ca20b 100644 --- a/sfizz/SIMDHelpers.h +++ b/sfizz/SIMDHelpers.h @@ -448,4 +448,30 @@ T meanSquared(absl::Span vector) noexcept } template <> -float meanSquared(absl::Span vector) noexcept; \ No newline at end of file +float meanSquared(absl::Span vector) noexcept; + +template +inline void snippetCumsum(const T*& input, T*& output) +{ + *output = *(output - 1) + *input++; + output++; +} + +template +void cumsum(absl::Span input, absl::Span output) noexcept +{ + ASSERT(output.size() >= input.size()); + if (input.size() == 0) + return; + + auto out = output.data(); + auto in = input.data(); + const auto sentinel = in + std::min(input.size(), output.size()); + + *out++ = *in++; + while (in < sentinel) + snippetCumsum(in, out); +} + +template <> +void cumsum(absl::Span input, absl::Span output) noexcept; \ No newline at end of file diff --git a/sfizz/SIMDSSE.cpp b/sfizz/SIMDSSE.cpp index 46733213..f7900a81 100644 --- a/sfizz/SIMDSSE.cpp +++ b/sfizz/SIMDSSE.cpp @@ -613,7 +613,7 @@ float mean(absl::Span vector) noexcept auto* sentinel = vector.end(); const auto* lastAligned = prevAligned(sentinel); - while (unaligned(value)) + while (unaligned(value) && value < lastAligned) result += *value++; auto mmSums = _mm_setzero_ps(); @@ -645,7 +645,7 @@ float meanSquared(absl::Span vector) noexcept auto* sentinel = vector.end(); const auto* lastAligned = prevAligned(sentinel); - while (unaligned(value)) { + while (unaligned(value) && value < lastAligned) { result += (*value) * (*value); value++; } @@ -669,4 +669,36 @@ float meanSquared(absl::Span vector) noexcept } return result / static_cast(vector.size()); +} + +template <> +void cumsum(absl::Span input, absl::Span output) noexcept +{ + ASSERT(output.size() >= input.size()); + if (input.size() == 0) + return; + + auto out = output.data(); + auto in = input.data(); + const auto sentinel = in + std::min(input.size(), output.size()); + const auto lastAligned = prevAligned(sentinel); + + *out++ = *in++; + while (unaligned(in, out) && in < lastAligned) + snippetCumsum(in, out); + + auto mmOutput = _mm_set_ps1(*(out - 1)); + while (in < lastAligned) { + auto mmOffset = _mm_load_ps(in); + mmOffset = _mm_add_ps(mmOffset, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOffset), 4))); + mmOffset = _mm_add_ps(mmOffset, _mm_shuffle_ps(_mm_setzero_ps(), mmOffset, 0x40)); + mmOutput = _mm_add_ps(mmOutput, mmOffset); + _mm_store_ps(out, mmOutput); + mmOutput = _mm_shuffle_ps(mmOutput, mmOutput, _MM_SHUFFLE(3, 3, 3, 3)); + in += TypeAlignment; + out += TypeAlignment; + } + + while (in < sentinel) + snippetCumsum(in, out); } \ No newline at end of file diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp index c991c58d..7a34bcca 100644 --- a/tests/SIMDHelpersT.cpp +++ b/tests/SIMDHelpersT.cpp @@ -711,4 +711,24 @@ TEST_CASE("[Helpers] Mean Squared (SIMD vs scalar)") std::vector input(medBufferSize); absl::c_iota(input, 0.0); REQUIRE(meanSquared(input) == meanSquared(input)); +} + +TEST_CASE("[Helpers] Cumulative sum ") +{ + std::array input { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0 6.5 8.1 + std::array output; + std::array expected { 1.1f, 2.3f, 3.6f, 5.0f, 6.5f, 8.1f }; + cumsum(input, absl::MakeSpan(output)); + REQUIRE(approxEqual(output, expected)); +} + +TEST_CASE("[Helpers] Cumulative sum (SIMD vs Scalar)") +{ + std::vector input(bigBufferSize); + std::vector outputScalar(bigBufferSize); + std::vector outputSIMD(bigBufferSize); + linearRamp(absl::MakeSpan(input), 0.0, 0.1); + cumsum(input, absl::MakeSpan(outputScalar)); + cumsum(input, absl::MakeSpan(outputSIMD)); + REQUIRE(approxEqual(outputScalar, outputSIMD)); } \ No newline at end of file