Simplified SIMD dispatch wrappers

This commit is contained in:
Jean Pierre Cimalando 2020-06-02 16:04:21 +02:00
parent 75073cf8c0
commit c0fac2cfbb
4 changed files with 277 additions and 129 deletions

View file

@ -5,236 +5,361 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SIMDHelpers.h"
#include <array>
#include "cpuid/cpuinfo.hpp"
#include "SIMDConfig.h"
#include "Debug.h"
#include "simd/HelpersSSE.h"
#include "simd/HelpersAVX.h"
#include "cpuid/cpuinfo.hpp"
#include <array>
#include <mutex>
namespace sfz {
static std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus;
static bool simdStatusInitialized = false;
static cpuid::cpuinfo cpuInfo;
template <class T>
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<float>;
void (*readInterleaved)(const T* input, T* outputLeft, T* outputRight, unsigned inputSize) noexcept = &readInterleavedScalar<float>;
void (*applyGain)(const T* gain, const T* input, T* output, unsigned size) noexcept = &applyGainScalar<float>;
void (*applyGain1)(T gain, const T* input, T* output, unsigned size) noexcept = &applyGainScalar<float>;
void (*divide)(const T* input, const T* divisor, T* output, unsigned size) noexcept = &divideScalar<float>;
void (*multiplyAdd)(const T* gain, const T* input, T* output, unsigned size) noexcept = &multiplyAddScalar<float>;
void (*multiplyAdd1)(T gain, const T* input, T* output, unsigned size) noexcept = &multiplyAddScalar<float>;
T (*linearRamp)(T* output, T start, T step, unsigned size) noexcept = &linearRampScalar<float>;
T (*multiplicativeRamp)(T* output, T start, T step, unsigned size) noexcept = &multiplicativeRampScalar<float>;
void (*add)(const T* input, T* output, unsigned size) noexcept = &addScalar<float>;
void (*add1)(T value, T* output, unsigned size) noexcept = &addScalar<float>;
void (*subtract)(const T* input, T* output, unsigned size) noexcept = &subtractScalar<float>;
void (*subtract1)(T value, T* output, unsigned size) noexcept = &subtractScalar<float>;
void (*copy)(const T* input, T* output, unsigned size) noexcept = &copyScalar<float>;
void (*cumsum)(const T* input, T* output, unsigned size) noexcept = &cumsumScalar<float>;
void (*diff)(const T* input, T* output, unsigned size) noexcept = &diffScalar<float>;
T (*mean)(const T* vector, unsigned size) noexcept = &meanScalar<float>;
T (*meanSquared)(const T* vector, unsigned size) noexcept = &meanSquaredScalar<float>;
private:
std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus;
};
///
static SIMDDispatch<float> simdDispatch;
void resetSIMDOpStatus()
{
simdStatus[static_cast<unsigned>(SIMDOps::writeInterleaved)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::readInterleaved)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::fill)] = true;
simdStatus[static_cast<unsigned>(SIMDOps::gain)] = true;
simdStatus[static_cast<unsigned>(SIMDOps::divide)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::linearRamp)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::multiplicativeRamp)] = true;
simdStatus[static_cast<unsigned>(SIMDOps::add)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::subtract)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::multiplyAdd)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::copy)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::cumsum)] = true;
simdStatus[static_cast<unsigned>(SIMDOps::diff)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::sfzInterpolationCast)] = true;
simdStatus[static_cast<unsigned>(SIMDOps::mean)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::meanSquared)] = false;
simdStatus[static_cast<unsigned>(SIMDOps::upsampling)] = true;
simdStatusInitialized = true;
simdDispatch.resetStatus();
}
void setSIMDOpStatus(SIMDOps op, bool status)
{
if (!simdStatusInitialized)
resetSIMDStatus();
simdStatus[static_cast<unsigned>(op)] = status;
simdDispatch.setStatus(op, status);
}
bool getSIMDOpStatus(SIMDOps op)
{
if (!simdStatusInitialized)
resetSIMDStatus();
return simdStatus[static_cast<unsigned>(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>(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<float>(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<float>(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<float>(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>(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>(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>(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<float>(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>(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<float>(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>(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<float>(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<float>(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<float>(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<float>(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<float>(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 <class T>
bool SIMDDispatch<T>::getStatus(SIMDOps op) const
{
const unsigned index = static_cast<unsigned>(op);
ASSERT(index < simdStatus.size());
return simdStatus[index];
}
template <class T>
void SIMDDispatch<T>::setStatus(SIMDOps op, bool enable)
{
const unsigned index = static_cast<unsigned>(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<float>;
break;
case SIMDOps::readInterleaved:
if (useSSE)
readInterleaved = &readInterleavedSSE;
else
readInterleaved = &readInterleavedScalar<float>;
break;
case SIMDOps::gain:
if (useAVX) {
applyGain = &applyGainAVX;
applyGain1 = &applyGainAVX;
}
else if (useSSE) {
applyGain = &applyGainSSE;
applyGain1 = &applyGainSSE;
}
else {
applyGain = &applyGainScalar<float>;
applyGain1 = &applyGainScalar<float>;
}
break;
case SIMDOps::divide:
if (useSSE)
divide = &divideSSE;
else
divide = &divideScalar<float>;
break;
case SIMDOps::multiplyAdd:
if (useSSE) {
multiplyAdd = &multiplyAddSSE;
multiplyAdd1 = &multiplyAddSSE;
}
else {
multiplyAdd = &multiplyAddScalar<float>;
multiplyAdd1 = &multiplyAddScalar<float>;
}
break;
case SIMDOps::linearRamp:
if (useSSE)
linearRamp = &linearRampSSE;
else
linearRamp = &linearRampScalar<float>;
break;
case SIMDOps::multiplicativeRamp:
if (useSSE)
multiplicativeRamp = &multiplicativeRampSSE;
else
multiplicativeRamp = &multiplicativeRampScalar<float>;
break;
case SIMDOps::add:
if (useSSE) {
add = &addSSE;
add1 = &addSSE;
}
else {
add = &addScalar<float>;
add1 = &addScalar<float>;
}
break;
case SIMDOps::subtract:
if (useSSE) {
subtract = &subtractSSE;
subtract1 = &subtractSSE;
}
else {
subtract = &subtractScalar<float>;
subtract1 = &subtractScalar<float>;
}
break;
case SIMDOps::copy:
if (useSSE)
copy = &copySSE;
else
copy = &copyScalar<float>;
break;
case SIMDOps::cumsum:
if (useSSE)
cumsum = &cumsumSSE;
else
cumsum = &cumsumScalar<float>;
break;
case SIMDOps::diff:
if (useSSE)
diff = &diffSSE;
else
diff = &diffScalar<float>;
break;
case SIMDOps::mean:
if (useSSE)
mean = &meanSSE;
else
mean = &meanScalar<float>;
break;
case SIMDOps::meanSquared:
if (useSSE)
meanSquared = &meanSquaredSSE;
else
meanSquared = &meanSquaredScalar<float>;
break;
}
}
template <class T>
void SIMDDispatch<T>::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<std::mutex> lock { simdMutex };
if (!simdInitialized) {
simdDispatch.resetStatus();
simdInitialized = true;
}
return diffScalar(input, output, size);
}
}

View file

@ -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.
*

View file

@ -5,9 +5,10 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include <algorithm>
template<class T>
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<class T>
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 <class T>
void copyScalar(const T* input, T* output, unsigned size) noexcept
{
std::copy(input, input + size, output);
}
template <class T>
T meanScalar(const T* vector, unsigned size) noexcept
{

View file

@ -1,2 +1,12 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#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;
}