From c0fac2cfbbebb041dbaf93f030335d2b1a76f0bc Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 2 Jun 2020 16:04:21 +0200 Subject: [PATCH] Simplified SIMD dispatch wrappers --- src/sfizz/SIMDHelpers.cpp | 375 ++++++++++++++++++++++----------- src/sfizz/SIMDHelpers.h | 6 + src/sfizz/simd/HelpersScalar.h | 11 +- tests/MainT.cpp | 14 +- 4 files changed, 277 insertions(+), 129 deletions(-) diff --git a/src/sfizz/SIMDHelpers.cpp b/src/sfizz/SIMDHelpers.cpp index de2e501d..8e02c6e9 100644 --- a/src/sfizz/SIMDHelpers.cpp +++ b/src/sfizz/SIMDHelpers.cpp @@ -5,236 +5,361 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "SIMDHelpers.h" -#include -#include "cpuid/cpuinfo.hpp" #include "SIMDConfig.h" +#include "Debug.h" #include "simd/HelpersSSE.h" #include "simd/HelpersAVX.h" +#include "cpuid/cpuinfo.hpp" +#include +#include namespace sfz { -static std::array(SIMDOps::_sentinel)> simdStatus; -static bool simdStatusInitialized = false; -static cpuid::cpuinfo cpuInfo; +template +struct SIMDDispatch { + constexpr SIMDDispatch() = default; -void resetSIMDStatus() + void resetStatus(); + bool getStatus(SIMDOps op) const; + void setStatus(SIMDOps op, bool enable); + + void (*writeInterleaved)(const T* inputLeft, const T* inputRight, T* output, unsigned outputSize) noexcept = &writeInterleavedScalar; + void (*readInterleaved)(const T* input, T* outputLeft, T* outputRight, unsigned inputSize) noexcept = &readInterleavedScalar; + void (*applyGain)(const T* gain, const T* input, T* output, unsigned size) noexcept = &applyGainScalar; + void (*applyGain1)(T gain, const T* input, T* output, unsigned size) noexcept = &applyGainScalar; + void (*divide)(const T* input, const T* divisor, T* output, unsigned size) noexcept = ÷Scalar; + void (*multiplyAdd)(const T* gain, const T* input, T* output, unsigned size) noexcept = &multiplyAddScalar; + void (*multiplyAdd1)(T gain, const T* input, T* output, unsigned size) noexcept = &multiplyAddScalar; + T (*linearRamp)(T* output, T start, T step, unsigned size) noexcept = &linearRampScalar; + T (*multiplicativeRamp)(T* output, T start, T step, unsigned size) noexcept = &multiplicativeRampScalar; + void (*add)(const T* input, T* output, unsigned size) noexcept = &addScalar; + void (*add1)(T value, T* output, unsigned size) noexcept = &addScalar; + void (*subtract)(const T* input, T* output, unsigned size) noexcept = &subtractScalar; + void (*subtract1)(T value, T* output, unsigned size) noexcept = &subtractScalar; + void (*copy)(const T* input, T* output, unsigned size) noexcept = ©Scalar; + void (*cumsum)(const T* input, T* output, unsigned size) noexcept = &cumsumScalar; + void (*diff)(const T* input, T* output, unsigned size) noexcept = &diffScalar; + T (*mean)(const T* vector, unsigned size) noexcept = &meanScalar; + T (*meanSquared)(const T* vector, unsigned size) noexcept = &meanSquaredScalar; + +private: + std::array(SIMDOps::_sentinel)> simdStatus; +}; + +/// + +static SIMDDispatch simdDispatch; + +void resetSIMDOpStatus() { - simdStatus[static_cast(SIMDOps::writeInterleaved)] = false; - simdStatus[static_cast(SIMDOps::readInterleaved)] = false; - simdStatus[static_cast(SIMDOps::fill)] = true; - simdStatus[static_cast(SIMDOps::gain)] = true; - simdStatus[static_cast(SIMDOps::divide)] = false; - simdStatus[static_cast(SIMDOps::linearRamp)] = false; - simdStatus[static_cast(SIMDOps::multiplicativeRamp)] = true; - simdStatus[static_cast(SIMDOps::add)] = false; - simdStatus[static_cast(SIMDOps::subtract)] = false; - simdStatus[static_cast(SIMDOps::multiplyAdd)] = false; - simdStatus[static_cast(SIMDOps::copy)] = false; - simdStatus[static_cast(SIMDOps::cumsum)] = true; - simdStatus[static_cast(SIMDOps::diff)] = false; - simdStatus[static_cast(SIMDOps::sfzInterpolationCast)] = true; - simdStatus[static_cast(SIMDOps::mean)] = false; - simdStatus[static_cast(SIMDOps::meanSquared)] = false; - simdStatus[static_cast(SIMDOps::upsampling)] = true; - simdStatusInitialized = true; + simdDispatch.resetStatus(); } void setSIMDOpStatus(SIMDOps op, bool status) { - if (!simdStatusInitialized) - resetSIMDStatus(); - - simdStatus[static_cast(op)] = status; + simdDispatch.setStatus(op, status); } bool getSIMDOpStatus(SIMDOps op) { - if (!simdStatusInitialized) - resetSIMDStatus(); - - return simdStatus[static_cast(op)]; + return simdDispatch.getStatus(op); } +/// + void readInterleaved(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept { - if (getSIMDOpStatus(SIMDOps::readInterleaved)) { - if (cpuInfo.has_sse()) - return readInterleavedSSE(input, outputLeft, outputRight, inputSize); - } - return readInterleavedScalar(input, outputLeft, outputRight, inputSize); + return simdDispatch.readInterleaved(input, outputLeft, outputRight, inputSize); } void writeInterleaved(const float* inputLeft, const float* inputRight, float* output, unsigned outputSize) noexcept { - if (getSIMDOpStatus(SIMDOps::writeInterleaved)) { - if (cpuInfo.has_sse()) - return writeInterleavedSSE(inputLeft, inputRight, output, outputSize); - } - return writeInterleavedScalar(inputLeft, inputRight, output, outputSize); + return simdDispatch.writeInterleaved(inputLeft, inputRight, output, outputSize); } template <> void applyGain(float gain, const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::gain)) { - if (cpuInfo.has_avx()) - return applyGainAVX(gain, input, output, size); - else if (cpuInfo.has_sse()) - return applyGainSSE(gain, input, output, size); - } - return applyGainScalar(gain, input, output, size); + return simdDispatch.applyGain1(gain, input, output, size); } template <> void applyGain(const float* gain, const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::gain)) { - if (cpuInfo.has_avx()) - return applyGainAVX(gain, input, output, size); - else if (cpuInfo.has_sse()) - return applyGainSSE(gain, input, output, size); - } - return applyGainScalar(gain, input, output, size); + return simdDispatch.applyGain(gain, input, output, size); } template <> void divide(const float* input, const float* divisor, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::divide)) { - if (cpuInfo.has_sse()) - return divideSSE(input, divisor, output, size); - } - return divideScalar(input, divisor, output, size); + return simdDispatch.divide(input, divisor, output, size); } template <> void multiplyAdd(const float* gain, const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::multiplyAdd)) { - if (cpuInfo.has_sse()) - return multiplyAddSSE(gain, input, output, size); - } - return multiplyAddScalar(gain, input, output, size); + return simdDispatch.multiplyAdd(gain, input, output, size); } template <> void multiplyAdd(float gain, const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::multiplyAdd)) { - if (cpuInfo.has_sse()) - return multiplyAddSSE(gain, input, output, size); - } - return multiplyAddScalar(gain, input, output, size); + return simdDispatch.multiplyAdd1(gain, input, output, size); } template <> float linearRamp(float* output, float start, float step, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::linearRamp)) { - if (cpuInfo.has_sse()) - return linearRampSSE(output, start, step, size); - } - return linearRampScalar(output, start, step, size); + return simdDispatch.linearRamp(output, start, step, size); } template <> float multiplicativeRamp(float* output, float start, float step, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::multiplicativeRamp)) { - if (cpuInfo.has_sse()) - return multiplicativeRampSSE(output, start, step, size); - } - return multiplicativeRampScalar(output, start, step, size); + return simdDispatch.multiplicativeRamp(output, start, step, size); } template <> void add(const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::add)) { - if (cpuInfo.has_sse()) - return addSSE(input, output, size); - } - return addScalar(input, output, size); + return simdDispatch.add(input, output, size); } template <> void add(float value, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::add)) { - if (cpuInfo.has_sse()) - return addSSE(value, output, size); - } - return addScalar(value, output, size); + return simdDispatch.add1(value, output, size); } template <> void subtract(const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::subtract)) { - if (cpuInfo.has_sse()) - return subtractSSE(input, output, size); - } - return subtractScalar(input, output, size); + return simdDispatch.subtract(input, output, size); } template <> void subtract(float value, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::subtract)) { - if (cpuInfo.has_sse()) - return subtractSSE(value, output, size); - } - return subtractScalar(value, output, size); + return simdDispatch.subtract1(value, output, size); } template <> void copy(const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::copy)) { - if (cpuInfo.has_sse()) - return copySSE(input, output, size); - } - std::copy(input, input + size, output); + return simdDispatch.copy(input, output, size); } template <> float mean(const float* vector, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::mean)) { - if (cpuInfo.has_sse()) - return meanSSE(vector, size); - } - return meanScalar(vector, size); + return simdDispatch.mean(vector, size); } template <> float meanSquared(const float* vector, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::meanSquared)) { - if (cpuInfo.has_sse()) - return meanSquaredSSE(vector, size); - } - return meanSquaredScalar(vector, size); + return simdDispatch.meanSquared(vector, size); } template <> void cumsum(const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::cumsum)) { - if (cpuInfo.has_sse()) - return cumsumSSE(input, output, size); - } - return cumsumScalar(input, output, size); + return simdDispatch.cumsum(input, output, size); } template <> void diff(const float* input, float* output, unsigned size) noexcept { - if (getSIMDOpStatus(SIMDOps::diff)) { - if (cpuInfo.has_sse()) - return diffSSE(input, output, size); + return simdDispatch.diff(input, output, size); +} + +/// + +static cpuid::cpuinfo& cpuInfo() +{ + static cpuid::cpuinfo info; + return info; +} + +template +bool SIMDDispatch::getStatus(SIMDOps op) const +{ + const unsigned index = static_cast(op); + ASSERT(index < simdStatus.size()); + return simdStatus[index]; +} + +template +void SIMDDispatch::setStatus(SIMDOps op, bool enable) +{ + const unsigned index = static_cast(op); + ASSERT(index < simdStatus.size()); + + simdStatus[index] = enable; + + const cpuid::cpuinfo& info = cpuInfo(); + bool useSSE = enable && info.has_sse(); + bool useAVX = enable && info.has_avx(); + + switch (op) { + default: + break; + + case SIMDOps::writeInterleaved: + if (useSSE) + writeInterleaved = &writeInterleavedSSE; + else + writeInterleaved = &writeInterleavedScalar; + break; + + case SIMDOps::readInterleaved: + if (useSSE) + readInterleaved = &readInterleavedSSE; + else + readInterleaved = &readInterleavedScalar; + break; + + case SIMDOps::gain: + if (useAVX) { + applyGain = &applyGainAVX; + applyGain1 = &applyGainAVX; + } + else if (useSSE) { + applyGain = &applyGainSSE; + applyGain1 = &applyGainSSE; + } + else { + applyGain = &applyGainScalar; + applyGain1 = &applyGainScalar; + } + break; + + case SIMDOps::divide: + if (useSSE) + divide = ÷SSE; + else + divide = ÷Scalar; + break; + + case SIMDOps::multiplyAdd: + if (useSSE) { + multiplyAdd = &multiplyAddSSE; + multiplyAdd1 = &multiplyAddSSE; + } + else { + multiplyAdd = &multiplyAddScalar; + multiplyAdd1 = &multiplyAddScalar; + } + break; + + case SIMDOps::linearRamp: + if (useSSE) + linearRamp = &linearRampSSE; + else + linearRamp = &linearRampScalar; + break; + + case SIMDOps::multiplicativeRamp: + if (useSSE) + multiplicativeRamp = &multiplicativeRampSSE; + else + multiplicativeRamp = &multiplicativeRampScalar; + break; + + case SIMDOps::add: + if (useSSE) { + add = &addSSE; + add1 = &addSSE; + } + else { + add = &addScalar; + add1 = &addScalar; + } + break; + + case SIMDOps::subtract: + if (useSSE) { + subtract = &subtractSSE; + subtract1 = &subtractSSE; + } + else { + subtract = &subtractScalar; + subtract1 = &subtractScalar; + } + break; + + case SIMDOps::copy: + if (useSSE) + copy = ©SSE; + else + copy = ©Scalar; + break; + + case SIMDOps::cumsum: + if (useSSE) + cumsum = &cumsumSSE; + else + cumsum = &cumsumScalar; + break; + + case SIMDOps::diff: + if (useSSE) + diff = &diffSSE; + else + diff = &diffScalar; + break; + + case SIMDOps::mean: + if (useSSE) + mean = &meanSSE; + else + mean = &meanScalar; + break; + + case SIMDOps::meanSquared: + if (useSSE) + meanSquared = &meanSquaredSSE; + else + meanSquared = &meanSquaredScalar; + break; + } +} + +template +void SIMDDispatch::resetStatus() +{ + setStatus(SIMDOps::writeInterleaved, false); + setStatus(SIMDOps::readInterleaved, false); + setStatus(SIMDOps::fill, true); + setStatus(SIMDOps::gain, true); + setStatus(SIMDOps::divide, false); + setStatus(SIMDOps::linearRamp, false); + setStatus(SIMDOps::multiplicativeRamp, true); + setStatus(SIMDOps::add, false); + setStatus(SIMDOps::subtract, false); + setStatus(SIMDOps::multiplyAdd, false); + setStatus(SIMDOps::copy, false); + setStatus(SIMDOps::cumsum, true); + setStatus(SIMDOps::diff, false); + setStatus(SIMDOps::sfzInterpolationCast, true); + setStatus(SIMDOps::mean, false); + setStatus(SIMDOps::meanSquared, false); + setStatus(SIMDOps::upsampling, true); +} + +/// + +static volatile bool simdInitialized = false; +static std::mutex simdMutex; + +SIMDInitializer::SIMDInitializer() +{ + std::lock_guard lock { simdMutex }; + + if (!simdInitialized) { + simdDispatch.resetStatus(); + simdInitialized = true; } - return diffScalar(input, output, size); } } diff --git a/src/sfizz/SIMDHelpers.h b/src/sfizz/SIMDHelpers.h index 9e3bd06b..7379875d 100644 --- a/src/sfizz/SIMDHelpers.h +++ b/src/sfizz/SIMDHelpers.h @@ -59,9 +59,15 @@ enum class SIMDOps { }; // Enable or disable SIMD accelerators at runtime +void resetSIMDOpStatus(); void setSIMDOpStatus(SIMDOps op, bool status); bool getSIMDOpStatus(SIMDOps op); +// Initializer object which ensures to prepare SIMD dispatch +struct SIMDInitializer { + SIMDInitializer(); +}; + /** * @brief Read interleaved stereo data from a buffer and separate it in a left/right pair of buffers. * diff --git a/src/sfizz/simd/HelpersScalar.h b/src/sfizz/simd/HelpersScalar.h index 1554e636..d24b75ea 100644 --- a/src/sfizz/simd/HelpersScalar.h +++ b/src/sfizz/simd/HelpersScalar.h @@ -5,9 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once +#include template -inline void readInterleavedScalar(const T* input, T* outputLeft, T* outputRight, unsigned inputSize) +inline void readInterleavedScalar(const T* input, T* outputLeft, T* outputRight, unsigned inputSize) noexcept { const auto sentinel = input + inputSize - 1; while (input < sentinel) { @@ -17,7 +18,7 @@ inline void readInterleavedScalar(const T* input, T* outputLeft, T* outputRight, } template -inline void writeInterleavedScalar(const T* inputLeft, const T* inputRight, T* output, unsigned outputSize) +inline void writeInterleavedScalar(const T* inputLeft, const T* inputRight, T* output, unsigned outputSize) noexcept { const auto sentinel = output + outputSize - 1; while (output < sentinel) { @@ -120,6 +121,12 @@ inline void subtractScalar(T value, T* output, unsigned size) noexcept *output++ -= value; } +template +void copyScalar(const T* input, T* output, unsigned size) noexcept +{ + std::copy(input, input + size, output); +} + template T meanScalar(const T* vector, unsigned size) noexcept { diff --git a/tests/MainT.cpp b/tests/MainT.cpp index ba9cc3b4..40865cab 100644 --- a/tests/MainT.cpp +++ b/tests/MainT.cpp @@ -1,2 +1,12 @@ -#define CATCH_CONFIG_MAIN -#include "catch2/catch.hpp" \ No newline at end of file +#include "sfizz/SIMDHelpers.h" + +#define CATCH_CONFIG_RUNNER +#include "catch2/catch.hpp" + +int main(int argc, char* argv[]) +{ + sfz::SIMDInitializer simdInit; + + int result = Catch::Session().run(argc, argv); + return result; +}