Refactor the SIMD once and for all, hopefully...

This commit is contained in:
paul 2019-08-04 22:42:48 +02:00
parent 5ba9fd0461
commit 1b81997c57
8 changed files with 121 additions and 84 deletions

View file

@ -32,20 +32,12 @@ endif()
message("Cmake flags: ${CMAKE_CXX_FLAGS}")
# Check SIMD files
# Check SIMD
set(USE_SIMD OFF)
include(CheckIncludeFiles)
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H)
CHECK_INCLUDE_FILES(arm_neon.h HAVE_ARM_NEON_H)
if (HAVE_X86INTRIN_H AND UNIX)
add_compile_options(-DHAVE_X86INTRIN_H)
endif()
if (HAVE_INTRIN_H AND WIN32)
add_compile_options(/DHAVE_INTRIN_H)
endif()
if (HAVE_ARM_NEON_H AND UNIX)
add_compile_options(-DHAVE_ARM_NEON_H)
endif()
# Build options and includes
set(BUILD_TESTING OFF CACHE BOOL "Disable Abseil's tests")
@ -81,6 +73,25 @@ set(COMMON_SOURCES
sources/Region.cpp
sources/Parser.cpp
)
set(SSE_SOURCES
sources/StereoBufferSSE.cpp
)
if (HAVE_X86INTRIN_H AND UNIX)
add_compile_options(-DHAVE_X86INTRIN_H)
add_compile_options(-DUSE_SIMD)
set(COMMON_SOURCES ${COMMON_SOURCES} ${SSE_SOURCES})
endif()
if (HAVE_INTRIN_H AND WIN32)
add_compile_options(/DHAVE_INTRIN_H)
add_compile_options(/DUSE_SIMD)
set(COMMON_SOURCES ${COMMON_SOURCES} ${SSE_SOURCES})
endif()
if (HAVE_ARM_NEON_H AND UNIX)
add_compile_options(-DUSE_SIMD)
add_compile_options(-DHAVE_ARM_NEON_H)
endif()
set(TEST_SOURCES
tests/RegexT.cpp

View file

@ -36,8 +36,10 @@ public:
SndfileHandle sndFile ( reinterpret_cast<const char*>(file.c_str()) );
FileInformation returnedValue;
returnedValue.end = static_cast<uint32_t>(sndFile.frames());
SF_INSTRUMENT instrumentInfo;
sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo));
if (instrumentInfo.loop_count == 1)
{
returnedValue.loopBegin = instrumentInfo.loops[0].start;
@ -46,7 +48,7 @@ public:
auto preloadedSize = std::min(returnedValue.end, static_cast<uint32_t>(config::preloadSize));
returnedValue.preloadedData = std::make_shared<StereoBuffer<float>>(preloadedSize);
sndFile.readf(tempReadBuffer.data(), preloadedSize);
returnedValue.preloadedData->readInterleaved<SIMDConfig::supported>(tempReadBuffer.data(), preloadedSize);
returnedValue.preloadedData->readInterleaved<SIMDConfig::useSIMD>(tempReadBuffer.data(), preloadedSize);
preloadedData[filename] = returnedValue.preloadedData;
// char buffer [2048] ;
// sndFile.command(SFC_GET_LOG_INFO, buffer, sizeof(buffer)) ;

View file

@ -20,21 +20,12 @@ namespace config
} // namespace sfz
enum class SIMD { scalar, sse, neon };
namespace SIMDConfig
{
inline constexpr unsigned int defaultAlignment { 16 };
#if HAVE_X86INTRIN_H || HAVE_INTRIN_H
inline constexpr SIMD supported { SIMD::sse };
#if USE_SIMD
inline constexpr bool useSIMD { true };
#else
inline constexpr SIMD supported {SIMD::scalar};
inline constexpr bool useSIMD { false };
#endif
} // namespace config
#if HAVE_X86INTRIN_H
#include <x86intrin.h>
#endif
#if HAVE_INTRIN_H
#include <intrin.h>
#endif
}

11
sources/Intrinsics.h Normal file
View file

@ -0,0 +1,11 @@
#if HAVE_X86INTRIN_H
#include <x86intrin.h>
#endif
#if HAVE_INTRIN_H
#include <intrin.h>
#endif
#if HAVE_ARM_NEON_H
#include <arm_neon.h>
#endif

View file

@ -84,10 +84,17 @@ void sfz::Parser::readSfzFile(const std::filesystem::path& fileName, std::vector
std::replace(includePath.begin(), includePath.end(), '\\', '/');
const auto newFile = rootDirectory / includePath;
auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile);
if (std::filesystem::exists(newFile) && alreadyIncluded == includedFiles.end())
if (std::filesystem::exists(newFile))
{
includedFiles.push_back(newFile);
readSfzFile(newFile, lines);
if (alreadyIncluded == includedFiles.end())
{
includedFiles.push_back(newFile);
readSfzFile(newFile, lines);
}
else if (!recursiveIncludeGuard)
{
readSfzFile(newFile, lines);
}
}
continue;
}

View file

@ -24,10 +24,13 @@ public:
virtual bool loadSfzFile(const std::filesystem::path& file);
const std::map<std::string, std::string>& getDefines() const noexcept { return defines; }
const std::vector<std::filesystem::path>& getIncludedFiles() const noexcept { return includedFiles; }
void disableRecursiveIncludeGuard() { recursiveIncludeGuard = false; }
void enableRecursiveIncludeGuard() { recursiveIncludeGuard = true; }
protected:
virtual void callback(std::string_view header, std::vector<Opcode> members) = 0;
std::filesystem::path rootDirectory { std::filesystem::current_path() };
private:
bool recursiveIncludeGuard { false };
std::map<std::string, std::string> defines;
std::vector<std::filesystem::path> includedFiles;
std::string aggregatedContent { };

View file

@ -46,14 +46,14 @@ public:
}
}
template<SIMD op = SIMD::scalar>
template<bool useSIMD = false>
void fill(Type value) noexcept
{
std::fill(begin(Channel::left), end(Channel::left), value);
std::fill(begin(Channel::right), end(Channel::right), value);
}
template<SIMD op = SIMD::scalar>
template<bool useSIMD=false>
void readInterleaved(Type* input, int numFrames) noexcept
{
auto* in = input;
@ -70,61 +70,6 @@ public:
}
}
template <>
void readInterleaved<SIMD::sse>(float *input, int numFrames) noexcept
{
ASSERT(this->numFrames >= numFrames);
const int residualFrames = numFrames & (2 * TypeAlignment - 1);
const int lastAligned = numFrames - residualFrames;
float *in = input;
auto [lOut, rOut] = getChannels();
const float *end = input + 2 * lastAligned;
while (in < end)
{
auto register0 = _mm_loadu_ps(in);
in += 4;
auto register1 = _mm_loadu_ps(in);
in += 4;
auto register2 = register0;
// register 2 holds the copy of register 0 that is going to get erased by the first operation
// Remember that the bit mask reads from the end; 10 00 10 00 means
// "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_store_ps(lOut, register0);
_mm_store_ps(rOut, register1);
lOut += 4;
rOut += 4;
}
end = input + numChannels * numFrames;
while (in < end)
{
*lOut = *in;
in++;
*rOut = *in;
in++;
lOut += 1;
rOut += 1;
}
}
template<>
void fill<SIMD::sse>(float value) noexcept
{
const __m128 mmValue = _mm_set_ps1(value);
auto [lBegin, rBegin] = getChannels();
auto [lEnd, rEnd] = alignedEnds();
auto mmLeft = reinterpret_cast<__m128 *>(lBegin);
auto mmRight = reinterpret_cast<__m128 *>(rBegin);
while (mmLeft < reinterpret_cast<__m128 *>(lEnd)) // we should only need to test a single channel
{
_mm_store_ps(reinterpret_cast<float *>(mmLeft), mmValue);
_mm_store_ps(reinterpret_cast<float *>(mmRight), mmValue);
mmLeft++;
mmRight++;
}
}
Type* getChannel(Channel channel) noexcept
{
switch(channel)
@ -189,3 +134,11 @@ private:
Buffer<Type, Alignment> rightBuffer {};
Type trash { 0 };
};
template <>
template <>
void StereoBuffer<float, 16>::readInterleaved<true>(float *input, int numFrames) noexcept;
template <>
template <>
void StereoBuffer<float, 16>::fill<true>(float value) noexcept;

View file

@ -0,0 +1,59 @@
#include "StereoBuffer.h"
#include "Intrinsics.h"
template <>
template <>
void StereoBuffer<float, 16>::readInterleaved<true>(float *input, int numFrames) noexcept
{
ASSERT(this->numFrames >= numFrames);
const int residualFrames = numFrames & (2 * TypeAlignment - 1);
const int lastAligned = numFrames - residualFrames;
float *in = input;
auto [lOut, rOut] = getChannels();
const float *end = input + 2 * lastAligned;
while (in < end)
{
auto register0 = _mm_loadu_ps(in);
in += 4;
auto register1 = _mm_loadu_ps(in);
in += 4;
auto register2 = register0;
// register 2 holds the copy of register 0 that is going to get erased by the first operation
// Remember that the bit mask reads from the end; 10 00 10 00 means
// "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_store_ps(lOut, register0);
_mm_store_ps(rOut, register1);
lOut += 4;
rOut += 4;
}
end = input + numChannels * numFrames;
while (in < end)
{
*lOut = *in;
in++;
*rOut = *in;
in++;
lOut += 1;
rOut += 1;
}
}
template<>
template<>
void StereoBuffer<float, 16>::fill<true>(float value) noexcept
{
const __m128 mmValue = _mm_set_ps1(value);
auto [lBegin, rBegin] = getChannels();
auto [lEnd, rEnd] = alignedEnds();
auto mmLeft = reinterpret_cast<__m128 *>(lBegin);
auto mmRight = reinterpret_cast<__m128 *>(rBegin);
while (mmLeft < reinterpret_cast<__m128 *>(lEnd)) // we should only need to test a single channel
{
_mm_store_ps(reinterpret_cast<float *>(mmLeft), mmValue);
_mm_store_ps(reinterpret_cast<float *>(mmRight), mmValue);
mmLeft++;
mmRight++;
}
}