diff --git a/CMakeLists.txt b/CMakeLists.txt index 01ce6933..ab14d4c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ if (UNIX) add_compile_options(-Wextra) # add_compile_options(-fno-exceptions) add_compile_options(-ffast-math) - # add_compile_options(-fno-omit-frame-pointer) + add_compile_options(-fno-omit-frame-pointer) endif() if (WIN32) @@ -140,7 +140,7 @@ add_executable(sfizz_tests ${TEST_SOURCES} ${COMMON_SOURCES}) if(UNIX) target_link_libraries(sfizz_tests stdc++fs) endif(UNIX) -target_link_libraries(sfizz_tests Catch2::Catch2 absl::strings absl::flat_hash_map sndfile readerwriterqueue cnpy-static absl::span) +target_link_libraries(sfizz_tests Catch2::Catch2 absl::strings absl::flat_hash_map sndfile readerwriterqueue cnpy-static absl::span absl::algorithm) target_include_directories(sfizz_tests SYSTEM PRIVATE sources) file(COPY "tests" DESTINATION ${CMAKE_BINARY_DIR}) @@ -153,12 +153,6 @@ add_executable(bench_opf_high_vs_low benchmarks/OPF_high_vs_low.cpp) target_link_libraries(bench_opf_high_vs_low benchmark absl::span) ############################### -add_executable(bench_looping_index benchmarks/Looping_index.cpp) -target_link_libraries(bench_looping_index benchmark) - -add_executable(bench_looping_index_2 benchmarks/Looping_index_2.cpp) -target_link_libraries(bench_looping_index_2 benchmark) - add_executable(bm_cum_prod benchmarks/Cum_Prod.cpp) target_link_libraries(bm_cum_prod benchmark) @@ -175,10 +169,13 @@ add_executable(bm_fill benchmarks/BM_fill.cpp sources/SIMDSSE.cpp) target_link_libraries(bm_fill benchmark absl::span) add_executable(bm_mathfuns benchmarks/BM_mathfuns.cpp sources/SIMDSSE.cpp) -target_link_libraries(bm_mathfuns benchmark absl::span absl::span) +target_link_libraries(bm_mathfuns benchmark absl::span) if (UNIX) # target_compile_options(bm_math_loops PRIVATE -fopenmp) 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 +target_link_libraries(bm_gain benchmark absl::span) + +add_executable(bm_looping benchmarks/BM_looping.cpp sources/SIMDSSE.cpp) +target_link_libraries(bm_looping benchmark absl::span absl::algorithm) \ No newline at end of file diff --git a/benchmarks/BM_looping.cpp b/benchmarks/BM_looping.cpp new file mode 100644 index 00000000..0e4d7da9 --- /dev/null +++ b/benchmarks/BM_looping.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include "../sources/SIMDHelpers.h" + +// In this one we have an array of indices + +constexpr int loopStart { 5 }; +constexpr int loopEnd { 1076 }; +constexpr float maxJump { 4 }; + +class LoopingFixture : 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 }; + indices = std::vector(state.range(0)); + leftCoeffs = std::vector(state.range(0)); + rightCoeffs = std::vector(state.range(0)); + jumps = std::vector(state.range(0)); + absl::c_generate(jumps, [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + + } + + std::vector indices; + std::vector leftCoeffs; + std::vector rightCoeffs; + std::vector jumps; +}; + + +BENCHMARK_DEFINE_F(LoopingFixture, Scalar)(benchmark::State& state) { + for (auto _ : state) + { + loopingSFZIndex(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd, loopStart); + } +} + +BENCHMARK_DEFINE_F(LoopingFixture, SIMD)(benchmark::State& state) { + for (auto _ : state) + { + loopingSFZIndex(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd, loopStart); + } +} + +BENCHMARK_DEFINE_F(LoopingFixture, Scalar_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + loopingSFZIndex(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd, loopStart); + } +} + +BENCHMARK_DEFINE_F(LoopingFixture, SIMD_Unaligned)(benchmark::State& state) { + for (auto _ : state) + { + loopingSFZIndex(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd, loopStart); + } +} + + +// Register the function as a benchmark +BENCHMARK_REGISTER_F(LoopingFixture, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_REGISTER_F(LoopingFixture, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_REGISTER_F(LoopingFixture, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_REGISTER_F(LoopingFixture, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12)); +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Looping_index.cpp b/benchmarks/Looping_index.cpp deleted file mode 100644 index 9c02080a..00000000 --- a/benchmarks/Looping_index.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include -#include "../sources/Intrinsics.h" -#include "../sources/Buffer.h" -constexpr int loopOffset { 5 }; -constexpr int loopPoint { 51 }; -constexpr int loopBack { loopPoint - loopOffset }; -constexpr float maxJump { 4 }; - -static void Straight(benchmark::State& state) { - Buffer indices(state.range(0)); - Buffer leftCoeffs(state.range(0)); - Buffer rightCoeffs(state.range(0)); - std::random_device rd { }; - std::mt19937 gen { rd() }; - std::uniform_real_distribution dist { 0, maxJump }; - for (auto _ : state) - { - auto offset = dist(gen); - float floatingIndex = 0.0f; - auto index = indices.data(); - auto leftCoeff = leftCoeffs.data(); - auto rightCoeff = rightCoeffs.data(); - while (index < indices.end()) - { - *index = static_cast(floatingIndex); - *rightCoeff = floatingIndex - *index; - *leftCoeff = 1.0f - *rightCoeff; - floatingIndex += offset; - if (floatingIndex > loopPoint) - floatingIndex -= loopBack; - index++; - leftCoeff++; - rightCoeff++; - } - } -} - -static void SIMD(benchmark::State& state) { - Buffer indices(state.range(0)); - Buffer leftCoeffs(state.range(0)); - Buffer rightCoeffs(state.range(0)); - std::random_device rd { }; - std::mt19937 gen { rd() }; - std::uniform_real_distribution dist { 0, maxJump }; - for (auto _ : state) - { - auto index = indices.begin(); - auto leftCoeff = leftCoeffs.begin(); - auto rightCoeff = rightCoeffs.begin(); - const auto alignedEnd = state.range(0) - (state.range(0) & 3); - float floatingIndex = 48.0f; - auto offset = dist(gen); - const auto wrappingRegister = _mm_set1_ps(loopBack); - const auto upperBoundRegister = _mm_set1_ps(loopPoint); - const auto offsetRegister = _mm_set1_ps(offset); - const auto incrementRegister = _mm_set_ps(4.0f, 3.0f, 2.0f, 1.0f); - while (index < indices.data() + alignedEnd) - { - auto floatingIndexRegister = _mm_set_ps1(floatingIndex); - floatingIndexRegister = _mm_add_ps(floatingIndexRegister, _mm_mul_ps(incrementRegister, offsetRegister)); - auto rightCoefficientsRegister = _mm_sub_ps(floatingIndexRegister, - _mm_cvtepi32_ps(_mm_cvtps_epi32(_mm_sub_ps(floatingIndexRegister, _mm_set_ps1(0.5f)))) - ); - auto leftCoefficientsRegister = _mm_sub_ps(_mm_set_ps1(1.0f), rightCoefficientsRegister); - const auto comparisonRegister = _mm_cmpge_ps(floatingIndexRegister, upperBoundRegister); - auto loopbackRegister = _mm_sub_ps(floatingIndexRegister, wrappingRegister); - loopbackRegister = _mm_and_ps(comparisonRegister, loopbackRegister); - floatingIndexRegister = _mm_andnot_ps(comparisonRegister, floatingIndexRegister); - floatingIndexRegister = _mm_add_ps(floatingIndexRegister, loopbackRegister); - auto indexRegister = _mm_cvtps_epi32(floatingIndexRegister); - _mm_store_si128(reinterpret_cast<__m128i*>(index), indexRegister); - _mm_store_ps(leftCoeff, leftCoefficientsRegister); - _mm_store_ps(rightCoeff, rightCoefficientsRegister); - floatingIndex = _mm_cvtss_f32(_mm_shuffle_ps(floatingIndexRegister, floatingIndexRegister, _MM_SHUFFLE(0, 0, 0, 3)));; - // floatingIndex = *(index + 3) + *(rightCoeff + 3); - index += 4; - leftCoeff += 4; - rightCoeff += 4; - } - while (index < indices.end()) - { - *index = static_cast(floatingIndex); - *rightCoeff = floatingIndex - *index; - *leftCoeff = 1.0f - *rightCoeff; - floatingIndex += offset; - if (floatingIndex > loopPoint) - floatingIndex -= loopBack; - index++; - leftCoeff++; - rightCoeff++; - } - } -} - -// Register the function as a benchmark -BENCHMARK(Straight)->RangeMultiplier(2)->Range((2<<6), (2<<12)); -BENCHMARK(SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12)); -BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Looping_index_2.cpp b/benchmarks/Looping_index_2.cpp deleted file mode 100644 index 76883ec1..00000000 --- a/benchmarks/Looping_index_2.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include -#include -#include "../sources/Intrinsics.h" -#include "../sources/Buffer.h" - -// In this one we have an array of indices - -constexpr int loopOffset { 5 }; -constexpr int loopPoint { 1076 }; -constexpr int loopBack { loopPoint - loopOffset }; -constexpr float maxJump { 4 }; - -static void Straight(benchmark::State& state) { - Buffer indices(state.range(0)); - Buffer leftCoeffs(state.range(0)); - Buffer rightCoeffs(state.range(0)); - std::random_device rd { }; - std::mt19937 gen { rd() }; - std::uniform_real_distribution dist { 0, maxJump }; - Buffer jumps(state.range(0)); - std::generate(jumps.begin(), jumps.end(), [&]() { return dist(gen); }); - - for (auto _ : state) - { - float floatingIndex = 0.0f; - auto index = indices.data(); - auto leftCoeff = leftCoeffs.data(); - auto rightCoeff = rightCoeffs.data(); - auto jump = jumps.data(); - - while (index < indices.end()) - { - *index = static_cast(floatingIndex); - *rightCoeff = floatingIndex - *index; - *leftCoeff = 1.0f - *rightCoeff; - floatingIndex += *jump; - if (floatingIndex > loopPoint) - floatingIndex -= loopBack; - index++; - leftCoeff++; - rightCoeff++; - jump++; - } - } -} - -static void SIMD(benchmark::State& state) { - Buffer indices(state.range(0)); - Buffer leftCoeffs(state.range(0)); - Buffer rightCoeffs(state.range(0)); - - std::random_device rd { }; - std::mt19937 gen { rd() }; - std::uniform_real_distribution dist { 0, maxJump }; - Buffer jumps(state.range(0)); - std::generate(jumps.begin(), jumps.end(), [&]() { return dist(gen); }); - - for (auto _ : state) - { - auto index = indices.data(); - auto leftCoeff = leftCoeffs.data(); - auto rightCoeff = rightCoeffs.data(); - auto jump = jumps.data(); - - const auto alignedEnd = state.range(0) - (state.range(0) & 3); - - auto floatingIndexReg = _mm_set_ps1(48.0f); - const auto wrappingReg = _mm_set1_ps(loopBack); - const auto upperBoundReg = _mm_set1_ps(loopPoint); - - while (index < indices.data() + alignedEnd) - { - auto offsetReg = _mm_load_ps(jump); - offsetReg = _mm_add_ps(offsetReg, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(offsetReg), 4))); - offsetReg = _mm_add_ps(offsetReg, _mm_shuffle_ps(_mm_setzero_ps(), offsetReg, 0x40)); - - floatingIndexReg = _mm_add_ps(floatingIndexReg, offsetReg); - const auto comparisonReg = _mm_cmpge_ps(floatingIndexReg, upperBoundReg); - auto loopbackReg = _mm_sub_ps(floatingIndexReg, wrappingReg); - loopbackReg = _mm_and_ps(comparisonReg, loopbackReg); - floatingIndexReg = _mm_andnot_ps(comparisonReg, floatingIndexReg); - floatingIndexReg = _mm_add_ps(floatingIndexReg, loopbackReg); - - auto indexReg = _mm_cvtps_epi32(_mm_sub_ps(floatingIndexReg, _mm_set_ps1(0.5f))); - _mm_store_si128(reinterpret_cast<__m128i*>(index), indexReg); - - auto rightCoefficientsReg = _mm_sub_ps(floatingIndexReg, _mm_cvtepi32_ps(indexReg)); - auto leftCoefficientsReg = _mm_sub_ps(_mm_set_ps1(1.0f), rightCoefficientsReg); - _mm_store_ps(leftCoeff, leftCoefficientsReg); - _mm_store_ps(rightCoeff, rightCoefficientsReg); - - floatingIndexReg = _mm_shuffle_ps(floatingIndexReg, floatingIndexReg, _MM_SHUFFLE(3, 3, 3, 3)); - // floatingIndex = _mm_cvtss_f32(_mm_shuffle_ps(floatingIndexReg, floatingIndexReg, _MM_SHUFFLE(0, 0, 0, 3)));; - // floatingIndex = *(index + 3) + *(rightCoeff + 3); - index += 4; - jump += 4; - leftCoeff += 4; - rightCoeff += 4; - } - - float floatingIndex = _mm_cvtss_f32(floatingIndexReg); - while (index < indices.end()) - { - *index = static_cast(floatingIndex); - *rightCoeff = floatingIndex - *index; - *leftCoeff = 1.0f - *rightCoeff; - floatingIndex += *jump; - if (floatingIndex > loopPoint) - floatingIndex -= loopBack; - index++; - leftCoeff++; - rightCoeff++; - jump++; - } - } -} - -// Register the function as a benchmark -BENCHMARK(Straight)->RangeMultiplier(2)->Range((2<<6), (2<<12)); -BENCHMARK(SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12)); -BENCHMARK_MAIN(); \ No newline at end of file diff --git a/benchmarks/Spans.cpp b/benchmarks/Spans.cpp deleted file mode 100644 index f537462c..00000000 --- a/benchmarks/Spans.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include -#include "gsl/gsl-lite.hpp" -#include "absl/types/span.h" -#include -#include - -constexpr float filterGain { 0.25f }; - -void processRaw(float* input, float* lowpass, float gain, int numSamples) -{ - const auto end = input + numSamples; - float state = 0.0f; - float intermediate; - const auto G = gain / (1 - gain); - while (input < end) - { - intermediate = G * (*input++ - state); - *lowpass = intermediate + state; - state = *lowpass++ + intermediate; - } -} - -void processGSLSpan(gsl::span input, gsl::span lowpass, float gain) -{ - - float state = 0.0f; - float intermediate; - const auto G = gain / (1 - gain); - for (auto [in, out] = std::make_pair(input.begin(), lowpass.begin()); - in < input.end() && out < lowpass.end(); - in++, out++) - { - intermediate = G * (*in - state); - *out = intermediate + state; - state = *out + intermediate; - } -} - -void processABSLSpan(absl::Span input, absl::Span lowpass, float gain) -{ - - float state = 0.0f; - float intermediate; - const auto G = gain / (1 - gain); - for (auto [in, out] = std::make_pair(input.begin(), lowpass.begin()); - in < input.end() && out < lowpass.end(); - in++, out++) - { - intermediate = G * (*in - state); - *out = intermediate + state; - state = *out + intermediate; - } -} - -static void Raw(benchmark::State& state) { - std::vector input(state.range(0)); - std::vector output(state.range(0)); - std::random_device rd { }; - std::mt19937 gen { rd() }; - - std::normal_distribution dist { }; - std::generate(input.begin(), input.end(), [&]() { - return dist(gen); - }); - - for (auto _ : state) - { - processRaw(input.data(), output.data(), filterGain, state.range(0)); - } -} - -static void GSLSpan(benchmark::State& state) { - std::vector input(state.range(0)); - std::vector output(state.range(0)); - std::random_device rd { }; - std::mt19937 gen { rd() }; - - std::normal_distribution dist { }; - std::generate(input.begin(), input.end(), [&]() { - return dist(gen); - }); - - for (auto _ : state) - { - processGSLSpan(input, output, filterGain); - } -} - -static void ABSLSpan(benchmark::State& state) { - std::vector input(state.range(0)); - std::vector output(state.range(0)); - std::random_device rd { }; - std::mt19937 gen { rd() }; - - std::normal_distribution dist { }; - std::generate(input.begin(), input.end(), [&]() { - return dist(gen); - }); - - for (auto _ : state) - { - processABSLSpan(input, absl::MakeSpan(output), filterGain); - } -} - -// Register the function as a benchmark - -BENCHMARK(Raw)->RangeMultiplier(2)->Range((2<<6), (2<<12)); -BENCHMARK(GSLSpan)->RangeMultiplier(2)->Range((2<<6), (2<<12)); -BENCHMARK(ABSLSpan)->RangeMultiplier(2)->Range((2<<6), (2<<12)); -BENCHMARK_MAIN(); \ No newline at end of file diff --git a/sources/Helpers.h b/sources/Helpers.h index 1ae98d52..cc8e2cfd 100644 --- a/sources/Helpers.h +++ b/sources/Helpers.h @@ -48,6 +48,13 @@ inline unsigned int hash(std::string_view s, unsigned int h = Fnv1aBasis) return h; } +template +inline constexpr T min(T op1, T op2) { return std::min(op1, op2); } +template +inline constexpr T min(T op1, T op2, T op3) { return std::min(op1, std::min(op2, op3)); } +template +inline constexpr T min(T op1, T op2, T op3, T op4) { return std::min(op1, std::min(op2, std::min(op3, op4))); } + #ifndef NDEBUG #if __linux__ || __unix__ // These trap into the signal library rather than your own sourcecode diff --git a/sources/SIMDDummy.cpp b/sources/SIMDDummy.cpp index bdc400da..59cb624b 100644 --- a/sources/SIMDDummy.cpp +++ b/sources/SIMDDummy.cpp @@ -68,4 +68,10 @@ template<> void applyGain(absl::Span gain, absl::Span input, absl::Span output) noexcept { applyGain(gain, input, output); +} + +template<> +void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept +{ + loopingSFZIndex( jumps, leftCoeff, rightCoeff, indices); } \ No newline at end of file diff --git a/sources/SIMDHelpers.h b/sources/SIMDHelpers.h index 5d6a4c5c..b41a3cb0 100644 --- a/sources/SIMDHelpers.h +++ b/sources/SIMDHelpers.h @@ -100,7 +100,36 @@ template<> void cos(absl::Span input, absl::Span output) noexcept; template -void loopingSFZIndex(absl::Span inputLeft, absl::Span inputRight, absl::Span output); +void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, T floatIndex, T loopEnd, T loopStart) noexcept +{ + ASSERT(indices.size() >= jumps.size()); + ASSERT(indices.size() == leftCoeffs.size()); + ASSERT(indices.size() == rightCoeffs.size()); + + auto* index = indices.begin(); + auto* leftCoeff = leftCoeffs.begin(); + auto* rightCoeff = rightCoeffs.begin(); + auto* jump = jumps.begin(); + const auto size = min(jumps.size(), indices.size(), leftCoeffs.size(), rightCoeffs.size()); + auto* sentinel = jumps.begin() + size; + + while (jump < sentinel) + { + floatIndex += *jump; + if (floatIndex >= loopEnd) + floatIndex -= loopEnd - loopStart; + *index = static_cast(floatIndex); + *rightCoeff = floatIndex - *index; + *leftCoeff = 1.0f - *rightCoeff; + index++; + leftCoeff++; + rightCoeff++; + jump++; + } +} + +template<> +void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept; template void linearRamp(absl::Span output, T start, T end); diff --git a/sources/SIMDSSE.cpp b/sources/SIMDSSE.cpp index 001b7dfb..806496d8 100644 --- a/sources/SIMDSSE.cpp +++ b/sources/SIMDSSE.cpp @@ -37,6 +37,11 @@ bool unaligned(const float* ptr1, const float* ptr2, const float* ptr3) return unaligned(ptr1) || unaligned(ptr2) || unaligned(ptr3); } +bool unaligned(const float* ptr1, const float* ptr2, const float* ptr3, const float* ptr4) +{ + return unaligned(ptr1) || unaligned(ptr2) || unaligned(ptr3) || unaligned(ptr4); +} + template<> void readInterleaved(absl::Span input, absl::Span outputLeft, absl::Span outputRight) noexcept { @@ -194,8 +199,6 @@ void log(absl::Span input, absl::Span output) n } } - - template<> void sin(absl::Span input, absl::Span output) noexcept { @@ -256,4 +259,82 @@ void applyGain(absl::Span gain, absl::Span +void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept +{ + ASSERT(indices.size() >= jumps.size()); + ASSERT(indices.size() == leftCoeffs.size()); + ASSERT(indices.size() == rightCoeffs.size()); + + auto index = indices.data(); + auto leftCoeff = leftCoeffs.data(); + auto rightCoeff = rightCoeffs.data(); + auto jump = jumps.data(); + const auto size = min(jumps.size(), indices.size(), leftCoeffs.size(), rightCoeffs.size()); + const auto* sentinel = jumps.begin() + size; + const auto* alignedEnd = prevAligned(sentinel); + + while (unaligned(reinterpret_cast(index), leftCoeff, rightCoeff, jump) && jump < alignedEnd) + { + floatIndex += *jump; + if (floatIndex >= loopEnd) + floatIndex -= loopEnd - loopStart; + *index = static_cast(floatIndex); + *rightCoeff = floatIndex - *index; + *leftCoeff = 1.0f - *rightCoeff; + index++; + leftCoeff++; + rightCoeff++; + jump++; + } + + auto mmFloatIndex = _mm_set_ps1(floatIndex); + const auto mmJumpBack = _mm_set1_ps(loopEnd - loopStart); + const auto mmLoopEnd = _mm_set1_ps(loopEnd); + while (jump < alignedEnd) + { + auto mmOffset = _mm_load_ps(jump); + 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)); + + mmFloatIndex = _mm_add_ps(mmFloatIndex, mmOffset); + const auto mmCompared = _mm_cmpge_ps(mmFloatIndex, mmLoopEnd); + auto mmLoopBack = _mm_sub_ps(mmFloatIndex, mmJumpBack); + mmLoopBack = _mm_and_ps(mmCompared, mmLoopBack); + mmFloatIndex = _mm_andnot_ps(mmCompared, mmFloatIndex); + mmFloatIndex = _mm_add_ps(mmFloatIndex, mmLoopBack); + + auto mmIndices = _mm_cvtps_epi32(_mm_sub_ps(mmFloatIndex, _mm_set_ps1(0.4999999552965164184570312f))); + _mm_store_si128(reinterpret_cast<__m128i*>(index), mmIndices); + + auto mmRight = _mm_sub_ps(mmFloatIndex, _mm_cvtepi32_ps(mmIndices)); + auto mmLeft = _mm_sub_ps(_mm_set_ps1(1.0f), mmRight); + _mm_store_ps(leftCoeff, mmLeft); + _mm_store_ps(rightCoeff, mmRight); + + mmFloatIndex = _mm_shuffle_ps(mmFloatIndex, mmFloatIndex, _MM_SHUFFLE(3, 3, 3, 3)); + // floatingIndex = _mm_cvtss_f32(_mm_shuffle_ps(mmFloatIndex, mmFloatIndex, _MM_SHUFFLE(0, 0, 0, 3)));; + // floatingIndex = *(index + 3) + *(rightCoeff + 3); + index += TypeAlignment; + jump += TypeAlignment; + leftCoeff += TypeAlignment; + rightCoeff += TypeAlignment; + } + + floatIndex = _mm_cvtss_f32(mmFloatIndex); + while (jump < sentinel) + { + floatIndex += *jump; + if (floatIndex >= loopEnd) + floatIndex -= loopEnd - loopStart; + *index = static_cast(floatIndex); + *rightCoeff = floatIndex - *index; + *leftCoeff = 1.0f - *rightCoeff; + index++; + leftCoeff++; + rightCoeff++; + jump++; + } } \ No newline at end of file diff --git a/sources/StereoBuffer.h b/sources/StereoBuffer.h index cc0572c1..792ceab9 100644 --- a/sources/StereoBuffer.h +++ b/sources/StereoBuffer.h @@ -56,13 +56,13 @@ public: void readInterleaved(absl::Span input) noexcept { - ASSERT(input.size() <= numChannels * numFrames); + ASSERT(input.size() <= static_cast(numChannels * numFrames)); ::readInterleaved(input, absl::MakeSpan(leftBuffer), absl::MakeSpan(rightBuffer)); } void writeInterleaved(absl::Span output) noexcept { - ASSERT(output.size() >= numChannels * numFrames); + ASSERT(output.size() >= static_cast(numChannels * numFrames)); ::writeInterleaved(leftBuffer, rightBuffer, output); } diff --git a/tests/SIMDHelpersT.cpp b/tests/SIMDHelpersT.cpp index a82fc2ab..a9703189 100644 --- a/tests/SIMDHelpersT.cpp +++ b/tests/SIMDHelpersT.cpp @@ -2,6 +2,8 @@ #include "../sources/SIMDHelpers.h" #include #include +#include +#include using namespace Catch::literals; constexpr int smallBufferSize { 3 }; @@ -9,6 +11,22 @@ constexpr int bigBufferSize { 4095 }; constexpr int medBufferSize { 127 }; constexpr double fillValue { 1.3 }; +template +inline bool approxEqual(absl::Span lhs, absl::Span rhs, Type eps=1e-3) +{ + if (lhs.size() != rhs.size()) + return false; + + for (size_t i = 0; i < rhs.size(); ++i) + if (rhs[i] != Approx(lhs[i]).epsilon(eps)) + { + std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; + return false; + } + + return true; +} + TEST_CASE("[Helpers] fill() - Manual buffer") { std::vector buffer (5); @@ -337,4 +355,54 @@ TEST_CASE("[Helpers] Gain, spans and inplace (SIMD)") 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 +} + +TEST_CASE("[Helpers] SFZ looping index") +{ + std::array jumps { 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 indices; + std::array leftCoeffs; + std::array rightCoeffs; + std::array expectedIndices { 2, 3, 4, 1, 2, 4 }; + std::array expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f }; + std::array expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f }; + loopingSFZIndex(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1); + REQUIRE( indices == expectedIndices ); + REQUIRE( approxEqual(leftCoeffs, expectedLeft) ); + REQUIRE( approxEqual(rightCoeffs, expectedRight) ); +} + +TEST_CASE("[Helpers] SFZ looping index (SIMD)") +{ + std::array jumps { 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 indices; + std::array leftCoeffs; + std::array rightCoeffs; + std::array expectedIndices { 2, 3, 4, 1, 2, 4 }; + std::array expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f }; + std::array expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f }; + loopingSFZIndex(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1); + REQUIRE( indices == expectedIndices ); + REQUIRE( approxEqual(leftCoeffs, expectedLeft) ); + REQUIRE( approxEqual(rightCoeffs, expectedRight) ); +} + +// TEST_CASE("[Helpers] SFZ looping index (SIMD vs Scalar)") +// { + +// std::vector jumps(bigBufferSize); +// absl::c_fill(jumps, fillValue); + +// std::vector indices(bigBufferSize); +// std::vector leftCoeffs(bigBufferSize); +// std::vector rightCoeffs(bigBufferSize); + +// std::vector indicesSIMD(bigBufferSize); +// std::vector leftCoeffsSIMD(bigBufferSize); +// std::vector rightCoeffsSIMD(bigBufferSize); +// loopingSFZIndex(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, medBufferSize, 1); +// loopingSFZIndex(jumps, absl::MakeSpan(leftCoeffsSIMD), absl::MakeSpan(rightCoeffsSIMD), absl::MakeSpan(indicesSIMD), 1.0f, medBufferSize, 1); +// REQUIRE( approxEqual(indices, indicesSIMD, 1) ); +// REQUIRE( approxEqual(leftCoeffs, leftCoeffsSIMD, 1e-2) ); +// REQUIRE( approxEqual(rightCoeffs, rightCoeffsSIMD, 1e-2) ); +// } \ No newline at end of file