Refactor the SIMD once and for all, hopefully...
This commit is contained in:
parent
5ba9fd0461
commit
1b81997c57
8 changed files with 121 additions and 84 deletions
|
|
@ -32,20 +32,12 @@ endif()
|
||||||
|
|
||||||
message("Cmake flags: ${CMAKE_CXX_FLAGS}")
|
message("Cmake flags: ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
# Check SIMD files
|
# Check SIMD
|
||||||
|
set(USE_SIMD OFF)
|
||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
|
CHECK_INCLUDE_FILES(x86intrin.h HAVE_X86INTRIN_H)
|
||||||
CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H)
|
CHECK_INCLUDE_FILES(intrin.h HAVE_INTRIN_H)
|
||||||
CHECK_INCLUDE_FILES(arm_neon.h HAVE_ARM_NEON_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
|
# Build options and includes
|
||||||
set(BUILD_TESTING OFF CACHE BOOL "Disable Abseil's tests")
|
set(BUILD_TESTING OFF CACHE BOOL "Disable Abseil's tests")
|
||||||
|
|
@ -82,6 +74,25 @@ set(COMMON_SOURCES
|
||||||
sources/Parser.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
|
set(TEST_SOURCES
|
||||||
tests/RegexT.cpp
|
tests/RegexT.cpp
|
||||||
tests/HelpersT.cpp
|
tests/HelpersT.cpp
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,10 @@ public:
|
||||||
SndfileHandle sndFile ( reinterpret_cast<const char*>(file.c_str()) );
|
SndfileHandle sndFile ( reinterpret_cast<const char*>(file.c_str()) );
|
||||||
FileInformation returnedValue;
|
FileInformation returnedValue;
|
||||||
returnedValue.end = static_cast<uint32_t>(sndFile.frames());
|
returnedValue.end = static_cast<uint32_t>(sndFile.frames());
|
||||||
|
|
||||||
SF_INSTRUMENT instrumentInfo;
|
SF_INSTRUMENT instrumentInfo;
|
||||||
sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo));
|
sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo));
|
||||||
|
|
||||||
if (instrumentInfo.loop_count == 1)
|
if (instrumentInfo.loop_count == 1)
|
||||||
{
|
{
|
||||||
returnedValue.loopBegin = instrumentInfo.loops[0].start;
|
returnedValue.loopBegin = instrumentInfo.loops[0].start;
|
||||||
|
|
@ -46,7 +48,7 @@ public:
|
||||||
auto preloadedSize = std::min(returnedValue.end, static_cast<uint32_t>(config::preloadSize));
|
auto preloadedSize = std::min(returnedValue.end, static_cast<uint32_t>(config::preloadSize));
|
||||||
returnedValue.preloadedData = std::make_shared<StereoBuffer<float>>(preloadedSize);
|
returnedValue.preloadedData = std::make_shared<StereoBuffer<float>>(preloadedSize);
|
||||||
sndFile.readf(tempReadBuffer.data(), 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;
|
preloadedData[filename] = returnedValue.preloadedData;
|
||||||
// char buffer [2048] ;
|
// char buffer [2048] ;
|
||||||
// sndFile.command(SFC_GET_LOG_INFO, buffer, sizeof(buffer)) ;
|
// sndFile.command(SFC_GET_LOG_INFO, buffer, sizeof(buffer)) ;
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,12 @@ namespace config
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
||||||
enum class SIMD { scalar, sse, neon };
|
|
||||||
namespace SIMDConfig
|
namespace SIMDConfig
|
||||||
{
|
{
|
||||||
inline constexpr unsigned int defaultAlignment { 16 };
|
inline constexpr unsigned int defaultAlignment { 16 };
|
||||||
#if HAVE_X86INTRIN_H || HAVE_INTRIN_H
|
#if USE_SIMD
|
||||||
inline constexpr SIMD supported { SIMD::sse };
|
inline constexpr bool useSIMD { true };
|
||||||
#else
|
#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
|
#endif
|
||||||
|
}
|
||||||
11
sources/Intrinsics.h
Normal file
11
sources/Intrinsics.h
Normal 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
|
||||||
|
|
@ -84,11 +84,18 @@ void sfz::Parser::readSfzFile(const std::filesystem::path& fileName, std::vector
|
||||||
std::replace(includePath.begin(), includePath.end(), '\\', '/');
|
std::replace(includePath.begin(), includePath.end(), '\\', '/');
|
||||||
const auto newFile = rootDirectory / includePath;
|
const auto newFile = rootDirectory / includePath;
|
||||||
auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile);
|
auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile);
|
||||||
if (std::filesystem::exists(newFile) && alreadyIncluded == includedFiles.end())
|
if (std::filesystem::exists(newFile))
|
||||||
|
{
|
||||||
|
if (alreadyIncluded == includedFiles.end())
|
||||||
{
|
{
|
||||||
includedFiles.push_back(newFile);
|
includedFiles.push_back(newFile);
|
||||||
readSfzFile(newFile, lines);
|
readSfzFile(newFile, lines);
|
||||||
}
|
}
|
||||||
|
else if (!recursiveIncludeGuard)
|
||||||
|
{
|
||||||
|
readSfzFile(newFile, lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,13 @@ public:
|
||||||
virtual bool loadSfzFile(const std::filesystem::path& file);
|
virtual bool loadSfzFile(const std::filesystem::path& file);
|
||||||
const std::map<std::string, std::string>& getDefines() const noexcept { return defines; }
|
const std::map<std::string, std::string>& getDefines() const noexcept { return defines; }
|
||||||
const std::vector<std::filesystem::path>& getIncludedFiles() const noexcept { return includedFiles; }
|
const std::vector<std::filesystem::path>& getIncludedFiles() const noexcept { return includedFiles; }
|
||||||
|
void disableRecursiveIncludeGuard() { recursiveIncludeGuard = false; }
|
||||||
|
void enableRecursiveIncludeGuard() { recursiveIncludeGuard = true; }
|
||||||
protected:
|
protected:
|
||||||
virtual void callback(std::string_view header, std::vector<Opcode> members) = 0;
|
virtual void callback(std::string_view header, std::vector<Opcode> members) = 0;
|
||||||
std::filesystem::path rootDirectory { std::filesystem::current_path() };
|
std::filesystem::path rootDirectory { std::filesystem::current_path() };
|
||||||
private:
|
private:
|
||||||
|
bool recursiveIncludeGuard { false };
|
||||||
std::map<std::string, std::string> defines;
|
std::map<std::string, std::string> defines;
|
||||||
std::vector<std::filesystem::path> includedFiles;
|
std::vector<std::filesystem::path> includedFiles;
|
||||||
std::string aggregatedContent { };
|
std::string aggregatedContent { };
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,14 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<SIMD op = SIMD::scalar>
|
template<bool useSIMD = false>
|
||||||
void fill(Type value) noexcept
|
void fill(Type value) noexcept
|
||||||
{
|
{
|
||||||
std::fill(begin(Channel::left), end(Channel::left), value);
|
std::fill(begin(Channel::left), end(Channel::left), value);
|
||||||
std::fill(begin(Channel::right), end(Channel::right), 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
|
void readInterleaved(Type* input, int numFrames) noexcept
|
||||||
{
|
{
|
||||||
auto* in = input;
|
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
|
Type* getChannel(Channel channel) noexcept
|
||||||
{
|
{
|
||||||
switch(channel)
|
switch(channel)
|
||||||
|
|
@ -189,3 +134,11 @@ private:
|
||||||
Buffer<Type, Alignment> rightBuffer {};
|
Buffer<Type, Alignment> rightBuffer {};
|
||||||
Type trash { 0 };
|
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;
|
||||||
59
sources/StereoBufferSSE.cpp
Normal file
59
sources/StereoBufferSSE.cpp
Normal 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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue