From 3e0c5b2f7584684526d2f41247b14f31e1d9d949 Mon Sep 17 00:00:00 2001 From: paulfd Date: Sat, 7 Sep 2019 15:55:35 +0200 Subject: [PATCH] Add width and position envelopes --- benchmarks/BM_multiplyAdd.cpp | 96 +++++++++++++++++++++++++++++++++++ benchmarks/BM_subtract.cpp | 85 +++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 8 +++ external/Catch2 | 2 +- sfizz/Config.h | 2 + sfizz/MathHelpers.h | 6 ++- sfizz/SIMDDummy.cpp | 12 +++++ sfizz/SIMDHelpers.h | 42 +++++++++++++++ sfizz/SIMDSSE.cpp | 48 ++++++++++++++++++ sfizz/Voice.cpp | 64 ++++++++++++++++++++--- sfizz/Voice.h | 6 +++ tests/SIMDHelpersT.cpp | 32 ++++++++++++ 12 files changed, 393 insertions(+), 10 deletions(-) create mode 100644 benchmarks/BM_multiplyAdd.cpp create mode 100644 benchmarks/BM_subtract.cpp diff --git a/benchmarks/BM_multiplyAdd.cpp b/benchmarks/BM_multiplyAdd.cpp new file mode 100644 index 00000000..4fea543e --- /dev/null +++ b/benchmarks/BM_multiplyAdd.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" + +class MultiplyAdd : 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 = std::vector(state.range(0)); + std::fill(output.begin(), output.end(), 1.0f ); + std::generate(gain.begin(), gain.end(), [&]() { return dist(gen); }); + std::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + std::vector gain; + std::vector input; + std::vector output; +}; + +BENCHMARK_DEFINE_F(MultiplyAdd, Straight)(benchmark::State& state) { + for (auto _ : state) + { + for (int i = 0; i < state.range(0); ++i) + output[i] += gain[i] * input[i]; + } +} + +BENCHMARK_DEFINE_F(MultiplyAdd, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + multiplyAdd(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(MultiplyAdd, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + multiplyAdd(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(MultiplyAdd, Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + multiplyAdd(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_DEFINE_F(MultiplyAdd, SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + multiplyAdd(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_REGISTER_F(MultiplyAdd, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAdd, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAdd, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAdd, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(MultiplyAdd, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/BM_subtract.cpp b/benchmarks/BM_subtract.cpp new file mode 100644 index 00000000..5235f6ee --- /dev/null +++ b/benchmarks/BM_subtract.cpp @@ -0,0 +1,85 @@ +// 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 SubArray : 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(output.begin(), output.end(), [&]() { return dist(gen); }); + 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(SubArray, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + subtract(input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(SubArray, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + subtract(input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(SubArray, Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + subtract(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_DEFINE_F(SubArray, SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + subtract(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_REGISTER_F(SubArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(SubArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(SubArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(SubArray, 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 4619ee3f..98502cf4 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -57,6 +57,12 @@ target_link_libraries(bm_ADSR benchmark absl::span absl::algorithm) add_executable(bm_add BM_add.cpp ${SFIZZ_SIMD_SOURCES}) target_link_libraries(bm_add benchmark absl::span absl::algorithm) +add_executable(bm_multiplyAdd BM_multiplyAdd.cpp ${SFIZZ_SIMD_SOURCES}) +target_link_libraries(bm_multiplyAdd benchmark absl::span absl::algorithm) + +add_executable(bm_subtract BM_subtract.cpp ${SFIZZ_SIMD_SOURCES}) +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) @@ -73,4 +79,6 @@ add_dependencies(sfizz_benchmarks bm_ramp bm_ADSR bm_add + bm_subtract + bm_multiplyAdd ) \ No newline at end of file diff --git a/external/Catch2 b/external/Catch2 index b77ab74b..f2c2711b 160000 --- a/external/Catch2 +++ b/external/Catch2 @@ -1 +1 @@ -Subproject commit b77ab74b723cbd55ee12883562e22c7e467dbcb1 +Subproject commit f2c2711bdcc583938e444f2039b9ceba840defcf diff --git a/sfizz/Config.h b/sfizz/Config.h index b5854847..b313e441 100644 --- a/sfizz/Config.h +++ b/sfizz/Config.h @@ -54,5 +54,7 @@ namespace SIMDConfig { constexpr bool linearRamp { false }; constexpr bool multiplicativeRamp { true }; constexpr bool add { false }; + constexpr bool subtract { false }; + constexpr bool multiplyAdd { false }; constexpr bool copy { false }; } \ No newline at end of file diff --git a/sfizz/MathHelpers.h b/sfizz/MathHelpers.h index 553f4d2c..e67acb5b 100644 --- a/sfizz/MathHelpers.h +++ b/sfizz/MathHelpers.h @@ -75,4 +75,8 @@ constexpr Type twoPi { 2 * pi }; template constexpr Type piTwo { pi / 2 }; template -constexpr Type piFour { pi / 4 }; \ No newline at end of file +constexpr Type piFour { pi / 4 }; +template +constexpr Type sqrtTwo { 1.414213562373095048801688724209698078569671875376948073176 }; +template +constexpr Type sqrtTwoInv { 0.707106781186547524400844362104849039284835937688474036588 }; \ No newline at end of file diff --git a/sfizz/SIMDDummy.cpp b/sfizz/SIMDDummy.cpp index 1df98f6c..cac268e6 100644 --- a/sfizz/SIMDDummy.cpp +++ b/sfizz/SIMDDummy.cpp @@ -77,6 +77,12 @@ void applyGain(absl::Span gain, absl::Span(gain, input, output); } +template <> +void multiplyAdd(absl::Span gain, absl::Span input, absl::Span output) noexcept +{ + multiplyAdd(gain, input, output); +} + template <> float loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept { @@ -108,6 +114,12 @@ void add(absl::Span input, absl::Span output) n add(input, output); } +template <> +void subtract(absl::Span input, absl::Span output) noexcept +{ + subtract(input, output); +} + template <> void copy(absl::Span input, absl::Span output) noexcept { diff --git a/sfizz/SIMDHelpers.h b/sfizz/SIMDHelpers.h index 1e9cb4ab..0b84b65c 100644 --- a/sfizz/SIMDHelpers.h +++ b/sfizz/SIMDHelpers.h @@ -269,6 +269,28 @@ void applyGain(float gain, absl::Span input, absl::Spa template <> void applyGain(absl::Span gain, absl::Span input, absl::Span output) noexcept; +template +inline void snippetMultiplyAdd(const T*& gain, const T*& input, T*& output) +{ + *output++ += (*gain++) * (*input++); +} + +template +void multiplyAdd(absl::Span gain, absl::Span input, absl::Span output) noexcept +{ + ASSERT(gain.size() == input.size()); + ASSERT(input.size() <= output.size()); + auto* in = input.begin(); + auto* g = gain.begin(); + auto* out = output.begin(); + auto* sentinel = out + std::min(gain.size(), std::min(output.size(), input.size())); + while (out < sentinel) + snippetMultiplyAdd(g, in, out); +} + +template <> +void multiplyAdd(absl::Span gain, absl::Span input, absl::Span output) noexcept; + template inline void snippetRampLinear(T*& output, T& value, T step) { @@ -327,6 +349,26 @@ void add(absl::Span input, absl::Span output) noexcept template <> void add(absl::Span input, absl::Span output) noexcept; +template +inline void snippetSubtract(const T*& input, T*& output) +{ + *output++ -= *input++; +} + +template +void subtract(absl::Span input, absl::Span output) noexcept +{ + ASSERT(output.size() >= input.size()); + auto* in = input.begin(); + auto* out = output.begin(); + auto* sentinel = out + min(input.size(), output.size()); + while (out < sentinel) + snippetSubtract(in, out); +} + +template <> +void subtract(absl::Span input, absl::Span output) noexcept; + template void snippetCopy(const T*& input, T*& output) diff --git a/sfizz/SIMDSSE.cpp b/sfizz/SIMDSSE.cpp index 75124d24..b13f4081 100644 --- a/sfizz/SIMDSSE.cpp +++ b/sfizz/SIMDSSE.cpp @@ -22,6 +22,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SIMDHelpers.h" +#include #if HAVE_X86INTRIN_H #include @@ -304,6 +305,31 @@ void applyGain(absl::Span gain, absl::Span(g, in, out); } +template <> +void multiplyAdd(absl::Span gain, absl::Span input, absl::Span output) noexcept +{ + auto* in = input.begin(); + auto* out = output.begin(); + auto* g = gain.begin(); + const auto size = std::min(output.size(), std::min(input.size(), gain.size())); + const auto* lastAligned = prevAligned(output.begin() + size); + + while (unaligned(out, in, g) && out < lastAligned) + snippetMultiplyAdd(g, in, out); + + while (out < lastAligned) { + auto mmOut = _mm_load_ps(out); + mmOut = _mm_add_ps(_mm_mul_ps(_mm_load_ps(g), _mm_load_ps(in)), mmOut); + _mm_store_ps(out, mmOut); + g += TypeAlignment; + in += TypeAlignment; + out += TypeAlignment; + } + + while (out < output.end()) + snippetMultiplyAdd(g, in, out); +} + template <> float loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, @@ -496,6 +522,28 @@ void add(absl::Span input, absl::Span output) n snippetAdd(in, out); } +template <> +void subtract(absl::Span input, absl::Span output) noexcept +{ + ASSERT(output.size() >= input.size()); + auto* in = input.begin(); + auto* out = output.begin(); + auto* sentinel = out + min(input.size(), output.size()); + const auto* lastAligned = prevAligned(sentinel); + + while (unaligned(in, out) && out < lastAligned) + snippetSubtract(in, out); + + while (out < lastAligned) { + _mm_store_ps(out, _mm_sub_ps(_mm_load_ps(in), _mm_load_ps(out))); + out += TypeAlignment; + in += TypeAlignment; + } + + while (out < sentinel) + snippetSubtract(in, out); +} + template <> void copy(absl::Span input, absl::Span output) noexcept { diff --git a/sfizz/Voice.cpp b/sfizz/Voice.cpp index b91f4a0f..cd9b9054 100644 --- a/sfizz/Voice.cpp +++ b/sfizz/Voice.cpp @@ -63,10 +63,20 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, basePan = normalizeNegativePercents(region->pan); auto pan = basePan; if (region->panCC) - pan += normalizeCC(ccState[region->amplitudeCC->first]) * normalizeNegativePercents(region->amplitudeCC->second); pan += normalizeCC(ccState[region->panCC->first]) * normalizeNegativePercents(region->panCC->second); panEnvelope.reset(pan); - DBG("Base Panning: " << pan); + + basePosition = normalizeNegativePercents(region->position); + auto position = basePosition; + if (region->positionCC) + position += normalizeCC(ccState[region->positionCC->first]) * normalizeNegativePercents(region->positionCC->second); + positionEnvelope.reset(position); + + baseWidth = normalizeNegativePercents(region->width); + auto width = baseWidth; + if (region->widthCC) + width += normalizeCC(ccState[region->widthCC->first]) * normalizeNegativePercents(region->widthCC->second); + widthEnvelope.reset(width); sourcePosition = region->getOffset(); floatPosition = static_cast(sourcePosition); @@ -172,9 +182,11 @@ void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept this->samplesPerBlock = samplesPerBlock; tempBuffer1.resize(samplesPerBlock); tempBuffer2.resize(samplesPerBlock); + tempBuffer3.resize(samplesPerBlock); indexBuffer.resize(samplesPerBlock); tempSpan1 = absl::MakeSpan(tempBuffer1); tempSpan2 = absl::MakeSpan(tempBuffer2); + tempSpan3 = absl::MakeSpan(tempBuffer3); indexSpan = absl::MakeSpan(indexBuffer); } @@ -234,13 +246,49 @@ void sfz::Voice::processMono(AudioSpan buffer) noexcept void sfz::Voice::processStereo(AudioSpan buffer) noexcept { const auto numSamples = buffer.getNumFrames(); - auto envelopeSpan = tempSpan1.first(numSamples); - - amplitudeEnvelope.getBlock(envelopeSpan); - buffer.applyGain(envelopeSpan); + auto span1 = tempSpan1.first(numSamples); + auto span2 = tempSpan2.first(numSamples); + auto span3 = tempSpan3.first(numSamples); + auto leftBuffer = buffer.getSpan(0); + auto rightBuffer = buffer.getSpan(1); + + amplitudeEnvelope.getBlock(span1); + buffer.applyGain(span1); - egEnvelope.getBlock(envelopeSpan); - buffer.applyGain(envelopeSpan); + egEnvelope.getBlock(span1); + buffer.applyGain(span1); + + // Create mid/side from left/right in the output buffer + ::copy(rightBuffer, span1); + ::add(leftBuffer, rightBuffer); + ::subtract(span1, leftBuffer); + ::applyGain(sqrtTwoInv, leftBuffer); + ::applyGain(sqrtTwoInv, rightBuffer); + + // Apply the width process + widthEnvelope.getBlock(span1); + ::fill(span2, 1.0f); + ::add(span1, span2); + ::applyGain(piFour, span2); + ::cos(span2, span1); + ::sin(span2, span2); + ::applyGain(span1, leftBuffer); + ::applyGain(span2, rightBuffer); + + // Apply a position to the "left" channel which is supposed to be our mid channel + // TODO: add panning here too? + positionEnvelope.getBlock(span1); + ::fill(span2, 1.0f); + ::add(span1, span2); + ::applyGain(piFour, span2); + ::cos(span2, span1); + ::sin(span2, span2); + ::copy(leftBuffer, span3); + ::copy(rightBuffer, leftBuffer); + ::multiplyAdd(span1, span3, leftBuffer); + ::multiplyAdd(span2, span3, rightBuffer); + ::applyGain(sqrtTwoInv, leftBuffer); + ::applyGain(sqrtTwoInv, rightBuffer); } void sfz::Voice::fillWithData(AudioSpan buffer) noexcept diff --git a/sfizz/Voice.h b/sfizz/Voice.h index 7890a33b..61f6f135 100644 --- a/sfizz/Voice.h +++ b/sfizz/Voice.h @@ -91,6 +91,8 @@ private: float pitchRatio { 1.0 }; float baseGain { 1.0 }; float basePan { 0.0 }; + float basePosition { 0.0 }; + float baseWidth { 0.0 }; float baseFrequency { 440.0 }; float phase { 0.0f }; @@ -103,9 +105,11 @@ private: Buffer tempBuffer1; Buffer tempBuffer2; + Buffer tempBuffer3; Buffer indexBuffer; absl::Span tempSpan1 { absl::MakeSpan(tempBuffer1) }; absl::Span tempSpan2 { absl::MakeSpan(tempBuffer2) }; + absl::Span tempSpan3 { absl::MakeSpan(tempBuffer3) }; absl::Span indexSpan { absl::MakeSpan(indexBuffer) }; int samplesPerBlock { config::defaultSamplesPerBlock }; @@ -115,6 +119,8 @@ private: ADSREnvelope egEnvelope; LinearEnvelope amplitudeEnvelope; LinearEnvelope panEnvelope; + LinearEnvelope positionEnvelope; + LinearEnvelope widthEnvelope; LEAK_DETECTOR(Voice); }; diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp index 235ab117..0a58e7d5 100644 --- a/tests/SIMDHelpersT.cpp +++ b/tests/SIMDHelpersT.cpp @@ -623,6 +623,38 @@ TEST_CASE("[Helpers] Add (SIMD vs scalar)") REQUIRE(approxEqual(outputScalar, outputSIMD)); } +TEST_CASE("[Helpers] Subtract") +{ + std::array input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + std::array output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array expected { 0.0, -1.0, -2.0, -3.0, -4.0 }; + subtract(input, absl::MakeSpan(output)); + REQUIRE(output == expected); +} + +TEST_CASE("[Helpers] Subtract (SIMD)") +{ + std::array input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + std::array output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array expected { 0.0, -1.0, -2.0, -3.0, -4.0 }; + subtract(input, absl::MakeSpan(output)); + REQUIRE(output == expected); +} + +TEST_CASE("[Helpers] Subtract (SIMD vs scalar)") +{ + std::vector input(bigBufferSize); + std::vector outputScalar(bigBufferSize); + std::vector outputSIMD(bigBufferSize); + absl::c_iota(input, 0.0); + absl::c_fill(outputScalar, 0.0); + absl::c_fill(outputSIMD, 0.0); + + subtract(input, absl::MakeSpan(outputScalar)); + subtract(input, absl::MakeSpan(outputSIMD)); + REQUIRE(approxEqual(outputScalar, outputSIMD)); +} + TEST_CASE("[Helpers] copy") { std::array input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };