Moved applyGain to the new mode

This commit is contained in:
Paul Ferrand 2020-05-30 22:17:33 +02:00 committed by Jean Pierre Cimalando
parent 3c551d8f34
commit 1ab1ab802b
5 changed files with 140 additions and 130 deletions

View file

@ -66,14 +66,14 @@ BENCHMARK_DEFINE_F(GainSingle, Straight)(benchmark::State& state) {
BENCHMARK_DEFINE_F(GainSingle, Scalar)(benchmark::State& state) { BENCHMARK_DEFINE_F(GainSingle, Scalar)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output)); sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
} }
} }
BENCHMARK_DEFINE_F(GainSingle, SIMD)(benchmark::State& state) { BENCHMARK_DEFINE_F(GainSingle, SIMD)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output)); sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
} }
} }
@ -88,28 +88,28 @@ BENCHMARK_DEFINE_F(GainArray, Straight)(benchmark::State& state) {
BENCHMARK_DEFINE_F(GainArray, Scalar)(benchmark::State& state) { BENCHMARK_DEFINE_F(GainArray, Scalar)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output)); sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
} }
} }
BENCHMARK_DEFINE_F(GainArray, SIMD)(benchmark::State& state) { BENCHMARK_DEFINE_F(GainArray, SIMD)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output)); sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
} }
} }
BENCHMARK_DEFINE_F(GainArray, Scalar_Unaligned)(benchmark::State& state) { BENCHMARK_DEFINE_F(GainArray, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::applyGain<float, false>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); sfz::applyGain<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
} }
} }
BENCHMARK_DEFINE_F(GainArray, SIMD_Unaligned)(benchmark::State& state) { BENCHMARK_DEFINE_F(GainArray, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::applyGain<float, true>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1)); sfz::applyGain<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
} }
} }

View file

@ -13,6 +13,7 @@ namespace sfz {
static std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus; static std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus;
static bool simdStatusInitialized = false; static bool simdStatusInitialized = false;
static cpuid::cpuinfo cpuInfo;
void resetSIMDStatus() void resetSIMDStatus()
{ {
@ -58,7 +59,7 @@ bool getSIMDOpStatus(SIMDOps op)
constexpr uintptr_t TypeAlignment = 4; constexpr uintptr_t TypeAlignment = 4;
template<class T> template <class T>
inline void tickRead(const T*& input, T*& outputLeft, T*& outputRight) inline void tickRead(const T*& input, T*& outputLeft, T*& outputRight)
{ {
*outputLeft++ = *input++; *outputLeft++ = *input++;
@ -75,7 +76,7 @@ inline void tickWrite(T*& output, const T*& inputLeft, const T*& inputRight)
void readInterleaved(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept void readInterleaved(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept
{ {
const auto sentinel = input + inputSize - 1; const auto sentinel = input + inputSize - 1;
cpuid::cpuinfo cpuInfo;
if (getSIMDOpStatus(SIMDOps::readInterleaved)) { if (getSIMDOpStatus(SIMDOps::readInterleaved)) {
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 #if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
if (cpuInfo.has_sse()) { if (cpuInfo.has_sse()) {
@ -109,8 +110,7 @@ void writeInterleaved(const float* inputLeft, const float* inputRight, float* ou
{ {
const auto sentinel = output + outputSize - 1; const auto sentinel = output + outputSize - 1;
cpuid::cpuinfo cpuInfo; if (getSIMDOpStatus(SIMDOps::writeInterleaved)) {
if (getSIMDOpStatus(SIMDOps::readInterleaved)) {
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386 #if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
if (cpuInfo.has_sse()) { if (cpuInfo.has_sse()) {
const auto* lastAligned = prevAligned(output + outputSize - 4); const auto* lastAligned = prevAligned(output + outputSize - 4);
@ -136,5 +136,56 @@ void writeInterleaved(const float* inputLeft, const float* inputRight, float* ou
tickWrite(output, inputLeft, inputRight); tickWrite(output, inputLeft, inputRight);
} }
template<>
void applyGain<float>(float gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
if (getSIMDOpStatus(SIMDOps::gain)) {
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
if (cpuInfo.has_sse()) {
const auto* lastAligned = prevAligned(sentinel);
const auto mmGain = _mm_set_ps1(gain);
while (unaligned(input, output) && output < lastAligned)
*output++ = gain * (*input++);
while (output < lastAligned) {
_mm_store_ps(output, _mm_mul_ps(mmGain, _mm_load_ps(input)));
incrementAll<4>(input, output);
}
// fallthrough from lastAligned to sentinel
}
#endif
}
while (output < sentinel)
*output++ = gain * (*input++);
} }
template<>
void applyGain<float>(const float* gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
if (getSIMDOpStatus(SIMDOps::gain)) {
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
if (cpuInfo.has_sse()) {
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(input, output) && output < lastAligned)
*output++ = (*gain++) * (*input++);
while (output < lastAligned) {
_mm_store_ps(output, _mm_mul_ps(_mm_load_ps(gain), _mm_load_ps(input)));
incrementAll<4>(gain, input, output);
}
// fallthrough from lastAligned to sentinel
}
#endif
}
while (output < sentinel)
*output++ = (*gain++) * (*input++);
}
}

View file

@ -112,14 +112,13 @@ void readInterleaved(const float* input, float* outputLeft, float* outputRight,
inline void readInterleaved(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept inline void readInterleaved(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept
{ {
// The size of the output is not big enough for the input... // Something is fishy with the sizes
CHECK(outputLeft.size() >= input.size() / 2); CHECK(outputLeft.size() == input.size() / 2);
CHECK(outputRight.size() >= input.size() / 2); CHECK(outputRight.size() == input.size() / 2);
const auto size = min(input.size(), 2 * outputLeft.size(), 2 * outputRight.size()); const auto size = min(input.size(), 2 * outputLeft.size(), 2 * outputRight.size());
readInterleaved(input.data(), outputLeft.data(), outputRight.data(), size); readInterleaved(input.data(), outputLeft.data(), outputRight.data(), size);
} }
/** /**
* @brief Write a pair of left and right stereo input into a single buffer interleaved. * @brief Write a pair of left and right stereo input into a single buffer interleaved.
* *
@ -132,9 +131,9 @@ void writeInterleaved(const float* inputLeft, const float* inputRight, float* ou
inline void writeInterleaved(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept inline void writeInterleaved(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept
{ {
// Not enough data in the inputs // Something is fishy with the sizes
CHECK(inputLeft.size() >= output.size() / 2); CHECK(inputLeft.size() == output.size() / 2);
CHECK(inputRight.size() >= output.size() / 2); CHECK(inputRight.size() == output.size() / 2);
const auto size = min(output.size(), 2 * inputLeft.size(), 2 * inputRight.size()); const auto size = min(output.size(), 2 * inputLeft.size(), 2 * inputRight.size());
writeInterleaved(inputLeft.data(), inputRight.data(), output.data(), size); writeInterleaved(inputLeft.data(), inputRight.data(), output.data(), size);
} }
@ -152,103 +151,96 @@ void fill(absl::Span<T> output, T value) noexcept
absl::c_fill(output, value); absl::c_fill(output, value);
} }
namespace _internals {
template <class T>
inline void snippetGain(T gain, const T*& input, T*& output)
{
*output++ = gain * (*input++);
}
}
/** /**
* @brief Applies a scalar gain to the input * @brief Applies a scalar gain to the input
* *
* The output size will be the minimum of the input span and output span size.
*
* @tparam T the underlying type
* @tparam SIMD use the SIMD version or the scalar version
* @param gain the gain to apply * @param gain the gain to apply
* @param input * @param input
* @param output * @param output
* @param size
*/ */
template <class T, bool SIMD = SIMDConfig::gain> template<class T>
void applyGain(T gain, absl::Span<const T> input, absl::Span<T> output) noexcept void applyGain(T gain, const T* input, T* output, unsigned size) noexcept
{ {
CHECK(input.size() <= output.size()); const auto sentinel = output + size;
auto* in = input.begin(); while (output < sentinel)
auto* out = output.begin(); *output++ = gain * (*input++);
auto* sentinel = out + std::min(output.size(), input.size());
while (out < sentinel)
_internals::snippetGain<T>(gain, in, out);
} }
namespace _internals { template<>
template <class T> void applyGain<float>(float gain, const float* input, float* output, unsigned size) noexcept;
inline void snippetGainSpan(const T*& gain, const T*& input, T*& output)
{ template<class T>
*output++ = (*gain++) * (*input++); inline void applyGain(T gain, absl::Span<const T> input, absl::Span<T> output) noexcept
} {
CHECK_SPAN_SIZES(input, output);
applyGain<T>(gain, input.data(), output.data(), minSpanSize(input, output));
} }
/** /**
* @brief Applies a vector gain to an input stap * @brief Applies a scalar gain inplace
* *
* The output size will be the minimum of the gain, input span and output span size. * @param gain the gain to apply
* @param array
* @param size
*/
template<class T>
inline void applyGain(float gain, float* array, unsigned size) noexcept
{
applyGain<T>(gain, array, array, size);
}
template<class T>
inline void applyGain(float gain, absl::Span<float> array) noexcept
{
applyGain<T>(gain, array.data(), array.data(), array.size());
}
/**
* @brief Applies a vector gain to an input span
* *
* @tparam T the underlying type
* @tparam SIMD use the SIMD version or the scalar version
* @param gain * @param gain
* @param input * @param input
* @param output * @param output
* @param size
*/ */
template <class T, bool SIMD = SIMDConfig::gain> template<class T>
void applyGain(absl::Span<const T> gain, absl::Span<const T> input, absl::Span<T> output) noexcept void applyGain(const T* gain, const T* input, T* output, unsigned size) noexcept
{ {
CHECK(gain.size() == input.size()); const auto sentinel = output + size;
CHECK(input.size() <= output.size()); while (output < sentinel)
auto* in = input.begin(); *output++ = (*gain++) * (*input++);
auto* g = gain.begin();
auto* out = output.begin();
auto* sentinel = out + std::min(gain.size(), std::min(output.size(), input.size()));
while (out < sentinel)
_internals::snippetGainSpan<T>(g, in, out);
} }
/** template<>
* @brief Applies a scalar gain in-place on a span void applyGain<float>(const float* gain, const float* input, float* output, unsigned size) noexcept;
*
* @tparam T the underlying type template<class T>
* @tparam SIMD use the SIMD version or the scalar version inline void applyGain(absl::Span<const T> gain, absl::Span<const T> input, absl::Span<T> output) noexcept
* @param gain
* @param output
*/
template <class T, bool SIMD = SIMDConfig::gain>
void applyGain(T gain, absl::Span<T> output) noexcept
{ {
applyGain<T, SIMD>(gain, output, output); CHECK_SPAN_SIZES(gain, input, output);
applyGain<T>(gain.data(), input.data(), output.data(), minSpanSize(gain, input, output));
} }
/** /**
* @brief Applies a vector gain in-place on a span * @brief Applies a vector gain in-place on a span
* *
* The output size will be the minimum of the gain span and output span size.
*
* @tparam T the underlying type
* @tparam SIMD use the SIMD version or the scalar version
* @param gain * @param gain
* @param output * @param array
* @param size
*/ */
template <class T, bool SIMD = SIMDConfig::gain> template<class T>
void applyGain(absl::Span<const T> gain, absl::Span<T> output) noexcept inline void applyGain(const T* gain, T* array, unsigned size) noexcept
{ {
applyGain<T, SIMD>(gain, output, output); applyGain<T>(gain, array, array, size);
} }
template <> template<class T>
void applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept; inline void applyGain(absl::Span<const T> gain, absl::Span<T> array) noexcept
{
template <> CHECK_SPAN_SIZES(gain, array);
void applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept; applyGain<T>(gain.data(), array.data(), array.data(), minSpanSize(gain, array));
}
namespace _internals { namespace _internals {
template <class T> template <class T>

View file

@ -16,47 +16,6 @@
constexpr uintptr_t TypeAlignment = 4; constexpr uintptr_t TypeAlignment = 4;
template <>
void sfz::applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> 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)));
incrementAll<TypeAlignment>(out, in);
}
while (out < output.end())
*out++ = gain * (*in++);
}
template <>
void sfz::applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> 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)
_internals::snippetGainSpan<float>(g, in, out);
while (out < lastAligned) {
_mm_store_ps(out, _mm_mul_ps(_mm_load_ps(g), _mm_load_ps(in)));
incrementAll<TypeAlignment>(g, in, out);
}
while (out < output.end())
_internals::snippetGainSpan<float>(g, in, out);
}
template <> template <>

View file

@ -275,7 +275,8 @@ TEST_CASE("[Helpers] Gain, single")
std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, false>(fillValue, input, absl::MakeSpan(output)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(fillValue, input, absl::MakeSpan(output));
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -283,7 +284,8 @@ TEST_CASE("[Helpers] Gain, single and inplace")
{ {
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, false>(fillValue, buffer, absl::MakeSpan(buffer)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected); REQUIRE(buffer == expected);
} }
@ -293,7 +295,8 @@ TEST_CASE("[Helpers] Gain, spans")
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -302,7 +305,8 @@ TEST_CASE("[Helpers] Gain, spans and inplace")
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, false>(gain, buffer, absl::MakeSpan(buffer)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected); REQUIRE(buffer == expected);
} }
@ -311,7 +315,8 @@ TEST_CASE("[Helpers] Gain, single (SIMD)")
std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, true>(fillValue, input, absl::MakeSpan(output)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, true);
sfz::applyGain<float>(fillValue, input, absl::MakeSpan(output));
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -319,7 +324,8 @@ TEST_CASE("[Helpers] Gain, single and inplace (SIMD)")
{ {
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, true>(fillValue, buffer, absl::MakeSpan(buffer)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, true);
sfz::applyGain<float>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected); REQUIRE(buffer == expected);
} }
@ -329,7 +335,8 @@ TEST_CASE("[Helpers] Gain, spans (SIMD)")
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, true);
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -338,7 +345,8 @@ TEST_CASE("[Helpers] Gain, spans and inplace (SIMD)")
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, true>(gain, buffer, absl::MakeSpan(buffer)); sfz::setSIMDOpStatus(sfz::SIMDOps::gain, true);
sfz::applyGain<float>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected); REQUIRE(buffer == expected);
} }