diff --git a/CMakeLists.txt b/CMakeLists.txt index fe4f39c9..01ce6933 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,4 +178,7 @@ add_executable(bm_mathfuns benchmarks/BM_mathfuns.cpp sources/SIMDSSE.cpp) target_link_libraries(bm_mathfuns benchmark absl::span absl::span) if (UNIX) # target_compile_options(bm_math_loops PRIVATE -fopenmp) -endif() \ No newline at end of file +endif() + +add_executable(bm_gain benchmarks/BM_gain.cpp sources/SIMDSSE.cpp) +target_link_libraries(bm_gain benchmark absl::span) \ No newline at end of file diff --git a/benchmarks/BM_fill.cpp b/benchmarks/BM_fill.cpp index 0b427086..72c5c1ea 100644 --- a/benchmarks/BM_fill.cpp +++ b/benchmarks/BM_fill.cpp @@ -22,7 +22,17 @@ static void Fill_float(benchmark::State& state) { std::mt19937 gen { rd() }; std::uniform_real_distribution dist { 1, 2 }; for (auto _ : state) { - fill(buffer, dist(gen)); + fill(absl::MakeSpan(buffer), dist(gen)); + } +} + +static void Fill_float_unaligned(benchmark::State& state) { + Buffer buffer (state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) { + fill(absl::MakeSpan(buffer).subspan(1), dist(gen)); } } @@ -32,7 +42,17 @@ static void Fill_float_SSE(benchmark::State& state) { std::mt19937 gen { rd() }; std::uniform_real_distribution dist { 1, 2 }; for (auto _ : state) { - fill(buffer, dist(gen)); + fill(absl::MakeSpan(buffer), dist(gen)); + } +} + +static void Fill_float_SSE_unaligned(benchmark::State& state) { + Buffer buffer (state.range(0)); + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 1, 2 }; + for (auto _ : state) { + fill(absl::MakeSpan(buffer).subspan(1), dist(gen)); } } @@ -42,12 +62,14 @@ static void Fill_double(benchmark::State& state) { std::mt19937 gen { rd() }; std::uniform_real_distribution dist { 1, 2 }; for (auto _ : state) { - fill(buffer, dist(gen)); + fill(absl::MakeSpan(buffer), dist(gen)); } } BENCHMARK(Dummy)->Range((2<<6), (2<<16)); BENCHMARK(Fill_float)->Range((2<<6), (2<<16)); BENCHMARK(Fill_float_SSE)->Range((2<<6), (2<<16)); +BENCHMARK(Fill_float_unaligned)->Range((2<<6), (2<<16)); +BENCHMARK(Fill_float_SSE_unaligned)->Range((2<<6), (2<<16)); BENCHMARK(Fill_double)->Range((2<<6), (2<<16)); BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/BM_gain.cpp b/benchmarks/BM_gain.cpp new file mode 100644 index 00000000..702ecf8d --- /dev/null +++ b/benchmarks/BM_gain.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include "../sources/SIMDHelpers.h" + +class GainSingle : 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::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + float gain; + std::vector input; + std::vector output; +}; + +class GainArray : 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::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(GainSingle, Straight)(benchmark::State& state) { + for (auto _ : state) + { + for (int i = 0; i < state.range(0); ++i) + output[i] = gain * input[i]; + } +} + +BENCHMARK_DEFINE_F(GainSingle, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + applyGain(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(GainSingle, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + applyGain(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(GainArray, 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(GainArray, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + applyGain(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(GainArray, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + applyGain(gain, input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(GainArray, Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + applyGain(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + +BENCHMARK_DEFINE_F(GainArray, SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + applyGain(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); + } +} + + + +BENCHMARK_REGISTER_F(GainSingle, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(GainSingle, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(GainSingle, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(GainArray, Straight)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(GainArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(GainArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(GainArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_REGISTER_F(GainArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/BM_readInterleaved.cpp b/benchmarks/BM_readInterleaved.cpp index 6d39d7ae..ad639e63 100644 --- a/benchmarks/BM_readInterleaved.cpp +++ b/benchmarks/BM_readInterleaved.cpp @@ -33,7 +33,7 @@ static void Scalar_Unaligned(benchmark::State& state) { Buffer outputRight (state.range(0)); std::iota(input.begin(), input.end(), 1.0f); for (auto _ : state) { - readInterleaved(absl::MakeSpan(input).subspan(1), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(1)); + readInterleaved(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight)); } } @@ -43,7 +43,27 @@ static void SSE_Unaligned(benchmark::State& state) { Buffer outputRight (state.range(0)); std::iota(input.begin(), input.end(), 1.0f); for (auto _ : state) { - readInterleaved(absl::MakeSpan(input).subspan(1), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(1)); + readInterleaved(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight)); + } +} + +static void Scalar_Unaligned_2(benchmark::State& state) { + Buffer input (state.range(0) * 2); + Buffer outputLeft (state.range(0)); + Buffer outputRight (state.range(0)); + std::iota(input.begin(), input.end(), 1.0f); + for (auto _ : state) { + readInterleaved(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(3)); + } +} + +static void SSE_Unaligned_2(benchmark::State& state) { + Buffer input (state.range(0) * 2); + Buffer outputLeft (state.range(0)); + Buffer outputRight (state.range(0)); + std::iota(input.begin(), input.end(), 1.0f); + for (auto _ : state) { + readInterleaved(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(3)); } } @@ -51,4 +71,6 @@ BENCHMARK(Scalar)->Range((8<<10), (8<<20)); BENCHMARK(SSE)->Range((8<<10), (8<<20)); BENCHMARK(Scalar_Unaligned)->Range((8<<10), (8<<20)); BENCHMARK(SSE_Unaligned)->Range((8<<10), (8<<20)); +BENCHMARK(Scalar_Unaligned_2)->Range((8<<10), (8<<20)); +BENCHMARK(SSE_Unaligned_2)->Range((8<<10), (8<<20)); BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/BM_writeInterleaved.cpp b/benchmarks/BM_writeInterleaved.cpp index d80cca04..d64e5517 100644 --- a/benchmarks/BM_writeInterleaved.cpp +++ b/benchmarks/BM_writeInterleaved.cpp @@ -36,7 +36,7 @@ static void Unaligned_Interleaved_Write(benchmark::State& state) { std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); std::iota(inputRight.begin(), inputRight.end(), 1.0f); for (auto _ : state) { - writeInterleaved(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(1)); + writeInterleaved(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2)); benchmark::DoNotOptimize(output); } } @@ -48,7 +48,31 @@ static void Unaligned_Interleaved_Write_SSE(benchmark::State& state) { std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); std::iota(inputRight.begin(), inputRight.end(), 1.0f); for (auto _ : state) { - writeInterleaved(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(1)); + writeInterleaved(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2)); + benchmark::DoNotOptimize(output); + } +} + +static void Unaligned_Interleaved_Write_2(benchmark::State& state) { + Buffer inputLeft (state.range(0)); + Buffer inputRight (state.range(0)); + Buffer output (state.range(0) * 2); + std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); + std::iota(inputRight.begin(), inputRight.end(), 1.0f); + for (auto _ : state) { + writeInterleaved(absl::MakeSpan(inputLeft) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2)); + benchmark::DoNotOptimize(output); + } +} + +static void Unaligned_Interleaved_Write_SSE_2(benchmark::State& state) { + Buffer inputLeft (state.range(0)); + Buffer inputRight (state.range(0)); + Buffer output (state.range(0) * 2); + std::iota(inputLeft.begin(), inputLeft.end(), 1.0f); + std::iota(inputRight.begin(), inputRight.end(), 1.0f); + for (auto _ : state) { + writeInterleaved(absl::MakeSpan(inputLeft) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2)); benchmark::DoNotOptimize(output); } } @@ -57,4 +81,6 @@ BENCHMARK(Interleaved_Write)->Range((8<<10), (8<<20)); BENCHMARK(Interleaved_Write_SSE)->Range((8<<10), (8<<20)); BENCHMARK(Unaligned_Interleaved_Write)->Range((8<<10), (8<<20)); BENCHMARK(Unaligned_Interleaved_Write_SSE)->Range((8<<10), (8<<20)); +BENCHMARK(Unaligned_Interleaved_Write_2)->Range((8<<10), (8<<20)); +BENCHMARK(Unaligned_Interleaved_Write_SSE_2)->Range((8<<10), (8<<20)); BENCHMARK_MAIN(); \ No newline at end of file diff --git a/sources/Globals.h b/sources/Globals.h index 8588bae2..50dde3b7 100644 --- a/sources/Globals.h +++ b/sources/Globals.h @@ -26,6 +26,8 @@ namespace SIMDConfig constexpr bool writeInterleaved { true }; constexpr bool readInterleaved { true }; constexpr bool fill { false }; + constexpr bool gain { false }; + constexpr bool mathfuns { false }; #if USE_SIMD constexpr bool useSIMD { true }; #else diff --git a/sources/SIMDDummy.cpp b/sources/SIMDDummy.cpp index b63aef45..bdc400da 100644 --- a/sources/SIMDDummy.cpp +++ b/sources/SIMDDummy.cpp @@ -56,4 +56,16 @@ template<> void cos(absl::Span input, absl::Span output) noexcept { cos(input, output); +} + +template<> +void applyGain(float gain, absl::Span input, absl::Span output) noexcept +{ + applyGain(gain, input, output); +} + +template<> +void applyGain(absl::Span gain, absl::Span input, absl::Span output) noexcept +{ + applyGain(gain, input, output); } \ No newline at end of file diff --git a/sources/SIMDHelpers.h b/sources/SIMDHelpers.h index 16c6505f..5d6a4c5c 100644 --- a/sources/SIMDHelpers.h +++ b/sources/SIMDHelpers.h @@ -1,6 +1,5 @@ - -#include "absl/types/span.h" #include "Globals.h" +#include #include "Helpers.h" #include @@ -52,7 +51,7 @@ void fill(absl::Span output, T value) noexcept template<> void fill(absl::Span output, float value) noexcept; -template +template void exp(absl::Span input, absl::Span output) noexcept { ASSERT(output.size() >= input.size()); @@ -64,7 +63,7 @@ void exp(absl::Span input, absl::Span output) noexcept template<> void exp(absl::Span input, absl::Span output) noexcept; -template +template void log(absl::Span input, absl::Span output) noexcept { ASSERT(output.size() >= input.size()); @@ -76,7 +75,7 @@ void log(absl::Span input, absl::Span output) noexcept template<> void log(absl::Span input, absl::Span output) noexcept; -template +template void sin(absl::Span input, absl::Span output) noexcept { ASSERT(output.size() >= input.size()); @@ -88,7 +87,7 @@ void sin(absl::Span input, absl::Span output) noexcept template<> void sin(absl::Span input, absl::Span output) noexcept; -template +template void cos(absl::Span input, absl::Span output) noexcept { ASSERT(output.size() >= input.size()); @@ -109,9 +108,48 @@ void linearRamp(absl::Span output, T start, T end); template void exponentialRamp(absl::Span output, T start, T end); -template -void applyGain(T gain, absl::Span output); +template +void applyGain(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) + { + *out++ = gain * (*in++); + } +} -template -void applyGain(absl::Span gain, absl::Span output); +template +void applyGain(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) + { + *out++ = (*g++) * (*in++); + } +} +template +void applyGain(T gain, absl::Span output) noexcept +{ + applyGain(gain, output, output); +} + +template +void applyGain(absl::Span gain, absl::Span output) noexcept +{ + applyGain(gain, output, output); +} + +template<> +void applyGain(float gain, absl::Span input, absl::Span output) noexcept; + +template<> +void applyGain(absl::Span gain, absl::Span input, absl::Span output) noexcept; \ No newline at end of file diff --git a/sources/SIMDSSE.cpp b/sources/SIMDSSE.cpp index 850b2c57..001b7dfb 100644 --- a/sources/SIMDSSE.cpp +++ b/sources/SIMDSSE.cpp @@ -3,11 +3,42 @@ #include "x86intrin.h" #include "mathfuns/sse_mathfun.h" -constexpr int TypeAlignment { 4 }; +constexpr uintptr_t TypeAlignment { 4 }; +constexpr uintptr_t TypeAlignmentMask { TypeAlignment - 1 }; using Type = float; +constexpr uintptr_t ByteAlignment { TypeAlignment * sizeof(Type) }; +constexpr uintptr_t ByteAlignmentMask { ByteAlignment - 1 }; + + +struct AlignmentSentinels { float* nextAligned; float* lastAligned; }; + +float* nextAligned(const float* ptr) +{ + return reinterpret_cast( (reinterpret_cast(ptr) + ByteAlignmentMask) & (~ByteAlignmentMask) ); +} + +float* prevAligned(const float* ptr) +{ + return reinterpret_cast( reinterpret_cast(ptr) & (~ByteAlignmentMask) ); +} + +bool unaligned(const float* ptr) +{ + return (reinterpret_cast(ptr) & ByteAlignmentMask) != 0; +} + +bool unaligned(const float* ptr1, const float* ptr2) +{ + return unaligned(ptr1) || unaligned(ptr2); +} + +bool unaligned(const float* ptr1, const float* ptr2, const float* ptr3) +{ + return unaligned(ptr1) || unaligned(ptr2) || unaligned(ptr3); +} template<> -void readInterleaved(absl::Span input, absl::Span outputLeft, absl::Span outputRight) noexcept +void readInterleaved(absl::Span input, absl::Span outputLeft, absl::Span outputRight) noexcept { // The size of the outputs is not big enough for the input... ASSERT(outputLeft.size() >= input.size() / 2); @@ -18,14 +49,21 @@ void readInterleaved(absl::Span input, absl::Span auto* in = input.begin(); auto* lOut = outputLeft.begin(); auto* rOut = outputRight.begin(); - const int unalignedEnd = input.size() & (2 * TypeAlignment - 1); - const int lastAligned = input.size() - unalignedEnd; - auto* inputSentinel = in + lastAligned; - while (in < inputSentinel && lOut < outputLeft.end() && rOut < outputRight.end()) + + const auto size = std::min(input.size(), std::min(outputLeft.size() * 2, outputRight.size() * 2 )); + const auto* lastAligned = prevAligned(input.begin() + size - TypeAlignment); + + while (unaligned(in, lOut, rOut) && in < lastAligned) { - auto register0 = _mm_loadu_ps(in); + *lOut++ = *in++; + *rOut++ = *in++; + } + + while (in < lastAligned ) + { + auto register0 = _mm_load_ps(in); in += TypeAlignment; - auto register1 = _mm_loadu_ps(in); + auto register1 = _mm_load_ps(in); in += TypeAlignment; auto register2 = register0; // register 2 holds the copy of register 0 that is going to get erased by the first operation @@ -33,14 +71,13 @@ void readInterleaved(absl::Span input, absl::Span // "take 0 from a, take 2 from a, take 0 from b, take 2 from b" register0 = _mm_shuffle_ps(register0, register1, 0b10001000); register1 = _mm_shuffle_ps(register2, register1, 0b11011101); - _mm_storeu_ps(lOut, register0); - _mm_storeu_ps(rOut, register1); + _mm_store_ps(lOut, register0); + _mm_store_ps(rOut, register1); lOut += TypeAlignment; rOut += TypeAlignment; } - inputSentinel = input.end() - 1; - while (in < inputSentinel && lOut < outputLeft.end() && rOut < outputRight.end()) + while (in < input.end() - 1) { *lOut++ = *in++; *rOut++ = *in++; @@ -48,7 +85,7 @@ void readInterleaved(absl::Span input, absl::Span } template<> -void writeInterleaved(absl::Span inputLeft, absl::Span inputRight, absl::Span output) noexcept +void writeInterleaved(absl::Span inputLeft, absl::Span inputRight, absl::Span output) noexcept { // The size of the output is not big enough for the inputs... ASSERT(inputLeft.size() <= output.size() / 2); @@ -58,48 +95,53 @@ void writeInterleaved(absl::Span inputLeft, absl::Span void fill(absl::Span output, float value) noexcept { const auto mmValue = _mm_set_ps1(value); auto* out = output.begin(); - const int residual = output.size() & (TypeAlignment - 1); - const auto* sentinel = output.end() - residual; + const auto* lastAligned = prevAligned(output.end()); - while (out < sentinel) // we should only need to test a single channel + while (unaligned(out) && out < lastAligned) + *out++ = value; + + while (out < lastAligned) // we should only need to test a single channel { - _mm_storeu_ps(out, mmValue); + _mm_store_ps(out, mmValue); out += TypeAlignment; } @@ -117,8 +159,8 @@ void exp(absl::Span input, absl::Span output) n while (in < sentinel) { _mm_storeu_ps(out, exp_ps(_mm_loadu_ps(in))); - out += 4; - in += 4; + out += TypeAlignment; + in += TypeAlignment; } } @@ -132,8 +174,8 @@ void cos(absl::Span input, absl::Span output) n while (in < sentinel) { _mm_storeu_ps(out, cos_ps(_mm_loadu_ps(in))); - out += 4; - in += 4; + out += TypeAlignment; + in += TypeAlignment; } } @@ -147,11 +189,13 @@ void log(absl::Span input, absl::Span output) n while (in < sentinel) { _mm_storeu_ps(out, log_ps(_mm_loadu_ps(in))); - out += 4; - in += 4; + out += TypeAlignment; + in += TypeAlignment; } } + + template<> void sin(absl::Span input, absl::Span output) noexcept { @@ -162,7 +206,54 @@ void sin(absl::Span input, absl::Span output) n while (in < sentinel) { _mm_storeu_ps(out, sin_ps(_mm_loadu_ps(in))); - out += 4; - in += 4; + out += TypeAlignment; + in += TypeAlignment; } +} + +template<> +void applyGain(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); + const auto mmGain = _mm_set_ps1(gain); + + while (unaligned(out, in) && out < lastAligned) + *out++ = gain * (*in++); + + while (out < lastAligned) + { + _mm_store_ps(out, _mm_mul_ps(mmGain, _mm_load_ps(in))); + in += TypeAlignment; + out += TypeAlignment; + } + + while (out < output.end()) + *out++ = gain * (*in++); +} + +template<> +void applyGain(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) + *out++ = (*g++) * (*in++); + + while (out < lastAligned) + { + _mm_store_ps(out, _mm_mul_ps(_mm_load_ps(g), _mm_load_ps(in))); + g += TypeAlignment; + in += TypeAlignment; + out += TypeAlignment; + } + + while (out < output.end()) + *out++ = (*g++) * (*in++); } \ No newline at end of file diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp index 587c7090..a82fc2ab 100644 --- a/tests/SIMDHelpersT.cpp +++ b/tests/SIMDHelpersT.cpp @@ -265,4 +265,76 @@ TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar") writeInterleaved(leftInput, rightInput, absl::MakeSpan(outputScalar)); writeInterleaved(leftInput, rightInput, absl::MakeSpan(outputSIMD)); REQUIRE( outputScalar == outputSIMD ); +} + +TEST_CASE("[Helpers] Gain, single") +{ + std::array input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + std::array expected { fillValue, fillValue, fillValue, fillValue, fillValue }; + applyGain(fillValue, input, absl::MakeSpan(output)); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Gain, single and inplace") +{ + std::array buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array expected { fillValue, fillValue, fillValue, fillValue, fillValue }; + applyGain(fillValue, buffer, absl::MakeSpan(buffer)); + REQUIRE( buffer == expected ); +} + +TEST_CASE("[Helpers] Gain, spans") +{ + std::array input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + std::array output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + std::array expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + applyGain(gain, input, absl::MakeSpan(output)); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Gain, spans and inplace") +{ + std::array buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + std::array expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + applyGain(gain, buffer, absl::MakeSpan(buffer)); + REQUIRE( buffer == expected ); +} + +TEST_CASE("[Helpers] Gain, single (SIMD)") +{ + std::array input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + std::array expected { fillValue, fillValue, fillValue, fillValue, fillValue }; + applyGain(fillValue, input, absl::MakeSpan(output)); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Gain, single and inplace (SIMD)") +{ + std::array buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array expected { fillValue, fillValue, fillValue, fillValue, fillValue }; + applyGain(fillValue, buffer, absl::MakeSpan(buffer)); + REQUIRE( buffer == expected ); +} + +TEST_CASE("[Helpers] Gain, spans (SIMD)") +{ + std::array input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + std::array output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + std::array expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + applyGain(gain, input, absl::MakeSpan(output)); + REQUIRE( output == expected ); +} + +TEST_CASE("[Helpers] Gain, spans and inplace (SIMD)") +{ + std::array buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + std::array gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + std::array expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + applyGain(gain, buffer, absl::MakeSpan(buffer)); + REQUIRE( buffer == expected ); } \ No newline at end of file