From 9afa1918a0652e3a2b9135933f41c0763f9a1d8b Mon Sep 17 00:00:00 2001 From: paulfd Date: Sun, 15 Sep 2019 21:14:02 +0200 Subject: [PATCH] Added a "diff" SIMD helper, and some more benchmarking... --- benchmarks/BM_diff.cpp | 87 +++++++++++++++++++++ benchmarks/BM_pointerIterationOrOffsets.cpp | 86 ++++++++++++++++++++ benchmarks/CMakeLists.txt | 8 ++ sfizz/Config.h | 1 + sfizz/SIMDDummy.cpp | 6 ++ sfizz/SIMDHelpers.h | 29 ++++++- sfizz/SIMDSSE.cpp | 32 ++++++++ tests/SIMDHelpersT.cpp | 20 +++++ 8 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 benchmarks/BM_diff.cpp create mode 100644 benchmarks/BM_pointerIterationOrOffsets.cpp diff --git a/benchmarks/BM_diff.cpp b/benchmarks/BM_diff.cpp new file mode 100644 index 00000000..ea7e6fdf --- /dev/null +++ b/benchmarks/BM_diff.cpp @@ -0,0 +1,87 @@ +// 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 "absl/types/span.h" + +class DiffArray : 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, 1 }; + input = std::vector(state.range(0)); + output = std::vector(state.range(0)); + std::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + cumsum(input, absl::MakeSpan(input)); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + std::vector input; + std::vector output; +}; + + +BENCHMARK_DEFINE_F(DiffArray, Diff_Scalar)(benchmark::State& state) { + for (auto _ : state) + { + diff(input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(DiffArray, Diff_SIMD)(benchmark::State& state) { + for (auto _ : state) + { + diff(input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(DiffArray, Diff_Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + diff(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_DEFINE_F(DiffArray, Diff_SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + diff(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + + +BENCHMARK_REGISTER_F(DiffArray, Diff_Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(DiffArray, Diff_SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(DiffArray, Diff_Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(DiffArray, Diff_SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/BM_pointerIterationOrOffsets.cpp b/benchmarks/BM_pointerIterationOrOffsets.cpp new file mode 100644 index 00000000..be2ce371 --- /dev/null +++ b/benchmarks/BM_pointerIterationOrOffsets.cpp @@ -0,0 +1,86 @@ +// 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 "../sfizz/SIMDHelpers.h" +#include "absl/types/span.h" + +inline constexpr int bigNumber { 2399132 }; + +class IterOffset : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state [[maybe_unused]]) { + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0.001, 1 }; + std::uniform_int_distribution jumpDist { 0, 3 }; + source = std::vector(bigNumber); + result.resize(state.range(0)); + absl::c_generate(source, [&]() { return dist(gen); }); + jumps.resize(state.range(0)); + offsets.resize(state.range(0)); + absl::c_generate(jumps, [&]() { return jumpDist(gen); }); + cumsum(jumps, absl::MakeSpan(offsets)); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + std::vector source; + std::vector result; + std::vector offsets; + std::vector jumps; +}; + +BENCHMARK_DEFINE_F(IterOffset, Pointers)(benchmark::State& state) { + for (auto _ : state) + { + diff(offsets, absl::MakeSpan(jumps)); + auto jump = jumps.begin(); + auto in = source.begin(); + auto out = result.begin(); + while (out < result.end() && in < source.end()) { + *out++ = *in; + in += *jump++; + } + } +} + +BENCHMARK_DEFINE_F(IterOffset, Offsets)(benchmark::State& state) { + for (auto _ : state) + { + auto offset = offsets.begin(); + auto out = result.begin(); + while (offset < offsets.end()) + *out++ = source[*offset++]; + } +} + + +// Register the function as a benchmark +BENCHMARK_REGISTER_F(IterOffset, Pointers)->RangeMultiplier(4)->Range((1 << 2), (1 << 12)); +BENCHMARK_REGISTER_F(IterOffset, Offsets)->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 ac2278a7..2a4e0342 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -78,18 +78,26 @@ 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_diff BM_diff.cpp ${SFIZZ_SIMD_SOURCES}) +target_link_libraries(bm_diff 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_executable(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp ${SFIZZ_SIMD_SOURCES}) +target_link_libraries(bm_pointerIterationOrOffsets benchmark absl::span absl::algorithm) + add_custom_target(sfizz_benchmarks) add_dependencies(sfizz_benchmarks bm_opf_high_vs_low bm_write + bm_pointerIterationOrOffsets bm_read bm_mean bm_meanSquared bm_fill bm_cumsum + bm_diff bm_interpolationCast bm_mathfuns bm_gain diff --git a/sfizz/Config.h b/sfizz/Config.h index 78f3141a..73fcd757 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 diff { false }; constexpr bool sfzInterpolationCast { true }; constexpr bool mean { false }; constexpr bool meanSquared { false }; diff --git a/sfizz/SIMDDummy.cpp b/sfizz/SIMDDummy.cpp index a56c5949..507c15d5 100644 --- a/sfizz/SIMDDummy.cpp +++ b/sfizz/SIMDDummy.cpp @@ -160,4 +160,10 @@ template<> void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs) noexcept { sfzInterpolationCast(floatJumps, jumps, leftCoeffs, rightCoeffs); +} + +template <> +void diff(absl::Span input, absl::Span output) noexcept +{ + diff(input, output); } \ No newline at end of file diff --git a/sfizz/SIMDHelpers.h b/sfizz/SIMDHelpers.h index 6e0a8625..db4de29e 100644 --- a/sfizz/SIMDHelpers.h +++ b/sfizz/SIMDHelpers.h @@ -525,4 +525,31 @@ void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, } template<> -void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs) noexcept; \ No newline at end of file +void sfzInterpolationCast(absl::Span floatJumps, absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs) noexcept; + +template +inline void snippetDiff(const T*& input, T*& output) +{ + *output = *input - *(input - 1); + output++; + input++; +} + +template +void diff(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) + snippetDiff(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 38ceae47..63a11fa4 100644 --- a/sfizz/SIMDSSE.cpp +++ b/sfizz/SIMDSSE.cpp @@ -757,4 +757,36 @@ void sfzInterpolationCast(absl::Span floatJumps, absl: while(floatJump < sentinel) snippetSFZInterpolationCast(floatJump, jump, leftCoeff, rightCoeff); +} + +template <> +void diff(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) + snippetDiff(in, out); + + auto mmBase = _mm_set_ps1(*(out - 1)); + while (in < lastAligned) { + auto mmOutput = _mm_load_ps(in); + auto mmNextBase = _mm_shuffle_ps(mmOutput, mmOutput, _MM_SHUFFLE(3, 3, 3, 3)); + mmOutput = _mm_sub_ps(mmOutput, mmBase); + mmBase = mmNextBase; + mmOutput = _mm_sub_ps(mmOutput, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOutput), 4))); + _mm_store_ps(out, mmOutput); + in += TypeAlignment; + out += TypeAlignment; + } + + while (in < sentinel) + snippetDiff(in, out); } \ No newline at end of file diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp index 7a34bcca..eb611487 100644 --- a/tests/SIMDHelpersT.cpp +++ b/tests/SIMDHelpersT.cpp @@ -731,4 +731,24 @@ TEST_CASE("[Helpers] Cumulative sum (SIMD vs Scalar)") cumsum(input, absl::MakeSpan(outputScalar)); cumsum(input, absl::MakeSpan(outputSIMD)); REQUIRE(approxEqual(outputScalar, outputSIMD)); +} + +TEST_CASE("[Helpers] Diff ") +{ + std::array input { 1.1f, 2.3f, 3.6f, 5.0f, 6.5f, 8.1f }; + std::array output; + std::array expected { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; + diff(input, absl::MakeSpan(output)); + REQUIRE(approxEqual(output, expected)); +} + +TEST_CASE("[Helpers] Diff (SIMD vs Scalar)") +{ + std::vector input(bigBufferSize); + std::vector outputScalar(bigBufferSize); + std::vector outputSIMD(bigBufferSize); + linearRamp(absl::MakeSpan(input), 0.0, 0.1); + diff(input, absl::MakeSpan(outputScalar)); + diff(input, absl::MakeSpan(outputSIMD)); + REQUIRE(approxEqual(outputScalar, outputSIMD)); } \ No newline at end of file