SIMD refactoring

This commit is contained in:
paul 2019-08-21 01:00:07 +02:00
parent 471b531d03
commit 7dc65999a4
22 changed files with 944 additions and 517 deletions

View file

@ -80,22 +80,23 @@ set(COMMON_SOURCES
)
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)
set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDSSE.cpp)
elseif (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)
set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDSSE.cpp)
elseif (HAVE_ARM_NEON_H AND UNIX)
add_compile_options(-DUSE_SIMD)
add_compile_options(-DHAVE_ARM_NEON_H)
set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDDummy.cpp)
else()
set(COMMON_SOURCES ${COMMON_SOURCES} sources/SIMDDummy.cpp)
endif()
set(TEST_SOURCES
@ -106,6 +107,7 @@ set(TEST_SOURCES
tests/OpcodeT.cpp
tests/BufferT.cpp
tests/StereoBufferT.cpp
tests/SIMDHelpersT.cpp
tests/FilesT.cpp
tests/OnePoleFilterT.cpp
tests/RegionActivationT.cpp
@ -124,9 +126,6 @@ target_link_libraries(sfzprint absl::strings cxxopts)
###############################
# Basic command line program
add_executable(sfizz sources/Main.cpp ${COMMON_SOURCES})
if (HAVE_INTRIN_H OR HAVE_X86INTRIN_H)
target_sources(sfizz PRIVATE ${SSE_SOURCES})
endif()
# Per OS properties
if(UNIX)
target_compile_options(sfizz PRIVATE -fno-rtti)
@ -138,9 +137,6 @@ target_link_libraries(sfizz absl::strings cxxopts sndfile absl::flat_hash_map re
# Test application
add_executable(sfizz_tests ${TEST_SOURCES} ${COMMON_SOURCES})
# Per OS properties
if (HAVE_INTRIN_H OR HAVE_X86INTRIN_H)
target_sources(sfizz_tests PRIVATE ${SSE_SOURCES})
endif()
if(UNIX)
target_link_libraries(sfizz_tests stdc++fs)
endif(UNIX)
@ -148,36 +144,29 @@ target_link_libraries(sfizz_tests Catch2::Catch2 absl::strings absl::flat_hash_m
target_include_directories(sfizz_tests SYSTEM PRIVATE sources)
file(COPY "tests" DESTINATION ${CMAKE_BINARY_DIR})
###############################
add_executable(bench_fill benchmarks/Stereo_Fill.cpp sources/StereoBufferSSE.cpp)
# Per OS properties
target_link_libraries(bench_fill benchmark)
###############################
add_executable(bench_read benchmarks/Stereo_Read_Interleaved.cpp sources/StereoBufferSSE.cpp)
# Per OS properties
target_link_libraries(bench_read benchmark)
###############################
add_executable(bench_write benchmarks/Stereo_Write_Interleaved.cpp sources/StereoBufferSSE.cpp)
# Per OS properties
target_link_libraries(bench_write benchmark)
###############################
add_executable(bench_span benchmarks/Spans.cpp)
# Per OS properties
target_link_libraries(bench_span benchmark gsl::gsl-lite absl::span)
###############################
add_executable(bench_opf_high_vs_low benchmarks/OPF_high_vs_low.cpp)
# Per OS properties
target_link_libraries(bench_opf_high_vs_low benchmark gsl::gsl-lite)
###############################
add_executable(bench_looping_index benchmarks/Looping_index.cpp)
# Per OS properties
target_link_libraries(bench_looping_index benchmark)
add_executable(bench_looping_index_2 benchmarks/Looping_index_2.cpp)
# Per OS properties
target_link_libraries(bench_looping_index_2 benchmark)
add_executable(bm_cum_prod benchmarks/Cum_Prod.cpp)
target_link_libraries(bm_cum_prod benchmark)
add_executable(bm_cum_sum benchmarks/Cum_Sum.cpp)
target_link_libraries(bm_cum_sum benchmark)
add_executable(bm_write benchmarks/BM_writeInterleaved.cpp sources/SIMDSSE.cpp)
target_link_libraries(bm_write benchmark gsl::gsl-lite)
add_executable(bm_fill benchmarks/BM_fill.cpp sources/SIMDSSE.cpp)
target_link_libraries(bm_fill benchmark gsl::gsl-lite)

53
benchmarks/BM_fill.cpp Normal file
View file

@ -0,0 +1,53 @@
#include <benchmark/benchmark.h>
#include "../sources/SIMDHelpers.h"
#include "../sources/Buffer.h"
#include <algorithm>
#include <random>
#include <numeric>
static void Dummy(benchmark::State& state) {
Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
auto fillValue = dist(gen);
benchmark::DoNotOptimize(fillValue);
}
}
static void Fill_float(benchmark::State& state) {
Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
fill<float, false>(buffer, dist(gen));
}
}
static void Fill_float_SSE(benchmark::State& state) {
Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
fill<float, true>(buffer, dist(gen));
}
}
static void Fill_double(benchmark::State& state) {
Buffer<double> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<double> dist { 1, 2 };
for (auto _ : state) {
fill<double>(buffer, dist(gen));
}
}
BENCHMARK(Dummy)->Range((2<<6), (2<<16));
BENCHMARK(Fill_float)->Range((2<<6), (2<<16));
BENCHMARK(Fill_float_SSE)->Range((2<<6), (2<<16));
BENCHMARK(Fill_double)->Range((2<<6), (2<<16));
BENCHMARK_MAIN();

View file

@ -0,0 +1,59 @@
#include <benchmark/benchmark.h>
#include "../sources/SIMDHelpers.h"
#include "../sources/Buffer.h"
#include <algorithm>
#include <numeric>
static void Interleaved_Write(benchmark::State& state) {
Buffer<float> inputLeft (state.range(0));
Buffer<float> inputRight (state.range(0));
Buffer<float> output (state.range(0) * 2);
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
writeInterleaved<float, false>(inputLeft, inputRight, output);
}
}
static void Interleaved_Write_SSE(benchmark::State& state) {
Buffer<float> inputLeft (state.range(0));
Buffer<float> inputRight (state.range(0));
Buffer<float> output (state.range(0) * 2);
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
writeInterleaved<float, true>(inputLeft, inputRight, output);
benchmark::DoNotOptimize(output);
}
}
static void Unaligned_Interleaved_Write(benchmark::State& state) {
Buffer<float> inputLeft (state.range(0));
Buffer<float> inputRight (state.range(0));
Buffer<float> output (state.range(0) * 2);
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
writeInterleaved<float, false>(gsl::span(inputLeft).subspan(1) , gsl::span(inputRight).subspan(1), gsl::span(output).subspan(1));
benchmark::DoNotOptimize(output);
}
}
static void Unaligned_Interleaved_Write_SSE(benchmark::State& state) {
Buffer<float> inputLeft (state.range(0));
Buffer<float> inputRight (state.range(0));
Buffer<float> output (state.range(0) * 2);
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
writeInterleaved<float, true>(gsl::span(inputLeft).subspan(1) , gsl::span(inputRight).subspan(1), gsl::span(output).subspan(1));
benchmark::DoNotOptimize(output);
}
}
BENCHMARK(Interleaved_Write)->Range((8<<10), (8<<20));
BENCHMARK(Interleaved_Write_SSE)->Range((8<<10), (8<<20));
BENCHMARK(Unaligned_Interleaved_Write)->Range((8<<10), (8<<20));
BENCHMARK(Unaligned_Interleaved_Write_SSE)->Range((8<<10), (8<<20));
BENCHMARK_MAIN();

100
benchmarks/Cum_Prod.cpp Normal file
View file

@ -0,0 +1,100 @@
#include <benchmark/benchmark.h>
#include <random>
#include "../sources/Intrinsics.h"
#include "../sources/Buffer.h"
static void Dummy(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
benchmark::DoNotOptimize(value);
}
}
static void Straight(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
for (auto& out: output)
{
out = value;
value *= value;
}
}
}
static void SIMD(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
auto out = output.begin();
const auto alignedEnd = state.range(0) - (state.range(0) & 3);
auto baseReg = _mm_set_ps1(value);
auto prodReg = _mm_set_ps(value*value*value*value, value*value*value, value*value, value);
while (out < output.data() + alignedEnd)
{
baseReg = _mm_mul_ps(baseReg, prodReg);
_mm_storeu_ps(out, baseReg);
baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3));
out += 4;
}
while (out < output.end())
{
*out++ = value;
value *= value;
}
}
}
static void SIMD_unaligned(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
auto out = output.begin() + 1;
const auto alignedEnd = state.range(0) - (state.range(0) & 3);
auto baseReg = _mm_set_ps1(value);
auto prodReg = _mm_set_ps(value*value*value*value, value*value*value, value*value, value);
while (out < output.data() + alignedEnd)
{
baseReg = _mm_mul_ps(baseReg, prodReg);
_mm_storeu_ps(out, baseReg);
baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3));
out += 4;
}
while (out < output.end())
{
*out++ = value;
value *= value;
}
}
}
// Register the function as a benchmark
BENCHMARK(Dummy)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK(Straight)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK(SIMD)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK(SIMD_unaligned)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK_MAIN();

100
benchmarks/Cum_Sum.cpp Normal file
View file

@ -0,0 +1,100 @@
#include <benchmark/benchmark.h>
#include <random>
#include "../sources/Intrinsics.h"
#include "../sources/Buffer.h"
static void Dummy(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, 1 };
for (auto _ : state)
{
auto value = dist(gen);
benchmark::DoNotOptimize(value);
}
}
static void Straight(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, 1 };
for (auto _ : state)
{
auto value = dist(gen);
for (auto& out: output)
{
out = value;
value *= value;
}
}
}
static void SIMD(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, 1 };
for (auto _ : state)
{
auto value = dist(gen);
auto out = output.begin();
const auto alignedEnd = state.range(0) - (state.range(0) & 3);
auto baseReg = _mm_set_ps1(value);
auto prodReg = _mm_set_ps(value+value+value+value, value+value+value, value+value, value);
while (out < output.data() + alignedEnd)
{
baseReg = _mm_add_ps(baseReg, prodReg);
_mm_storeu_ps(out, baseReg);
baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3));
out += 4;
}
while (out < output.end())
{
*out++ = value;
value *= value;
}
}
}
static void SIMD_unaligned(benchmark::State& state) {
Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, 1 };
for (auto _ : state)
{
auto value = dist(gen);
auto out = output.begin() + 1;
const auto alignedEnd = state.range(0) - (state.range(0) & 3);
auto baseReg = _mm_set_ps1(value);
auto prodReg = _mm_set_ps(value+value+value+value, value+value+value, value+value, value);
while (out < output.data() + alignedEnd)
{
baseReg = _mm_add_ps(baseReg, prodReg);
_mm_storeu_ps(out, baseReg);
baseReg = _mm_shuffle_ps(baseReg, baseReg, _MM_SHUFFLE(3, 3, 3, 3));
out += 4;
}
while (out < output.end())
{
*out++ = value;
value *= value;
}
}
}
// Register the function as a benchmark
BENCHMARK(Dummy)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK(Straight)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK(SIMD)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK(SIMD_unaligned)->RangeMultiplier(2)->Range((1 << 2), (1 << 8));
BENCHMARK_MAIN();

View file

@ -8,7 +8,7 @@
// In this one we have an array of indices
constexpr int loopOffset { 5 };
constexpr int loopPoint { 51 };
constexpr int loopPoint { 1076 };
constexpr int loopBack { loopPoint - loopOffset };
constexpr float maxJump { 4 };

View file

@ -1,39 +0,0 @@
#include <benchmark/benchmark.h>
#include "../sources/StereoBuffer.h"
#include <algorithm>
#include <numeric>
static void Fill_float(benchmark::State& state) {
StereoBuffer<float> buffer (100001);
float fillValue = 0.0f;
for (auto _ : state) {
buffer.fill(fillValue);
fillValue += 1.0f;
}
}
// Register the function as a benchmark
BENCHMARK(Fill_float);
static void Fill_float_SSE(benchmark::State& state) {
StereoBuffer<float> buffer (100001);
float fillValue = 0.0f;
for (auto _ : state) {
buffer.fill<SIMD::sse>(fillValue);
fillValue += 1.0f;
}
}
// Register the function as a benchmark
BENCHMARK(Fill_float_SSE);
static void Fill_double(benchmark::State& state) {
StereoBuffer<double> buffer (100001);
double fillValue = 0.0;
for (auto _ : state) {
buffer.fill(fillValue);
fillValue += 1.0;
}
}
// Register the function as a benchmark
BENCHMARK(Fill_double);
BENCHMARK_MAIN();

View file

@ -1,35 +0,0 @@
#include <benchmark/benchmark.h>
#include "../sources/StereoBuffer.h"
#include <algorithm>
#include <numeric>
static void Interleaved_Read(benchmark::State& state) {
StereoBuffer<float> buffer (state.range(0));
std::vector<float> input (state.range(0) * 2);
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.readInterleaved(input.data(), state.range(0));
}
}
BENCHMARK(Interleaved_Read)->Range((8<<10) + 3, (8<<20) + 3);
static void Interleaved_Read_SSE(benchmark::State& state) {
StereoBuffer<float> buffer (state.range(0));
std::vector<float> input (state.range(0) * 2);
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.readInterleaved<SIMD::sse>(input.data(), state.range(0));
}
}
BENCHMARK(Interleaved_Read_SSE)->Range((8<<10) + 3, (8<<20) + 3);
static void Unaligned_Interleaved_Read_SSE(benchmark::State& state) {
StereoBuffer<float> buffer (state.range(0));
std::vector<float> input (state.range(0) * 2);
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.readInterleaved<SIMD::sse>(input.data() + 1, state.range(0) - 1);
}
}
BENCHMARK(Unaligned_Interleaved_Read_SSE)->Range((8<<10) + 3, (8<<20) + 3);
BENCHMARK_MAIN();

View file

@ -1,53 +0,0 @@
#include <benchmark/benchmark.h>
#include "../sources/StereoBuffer.h"
#include <algorithm>
#include <numeric>
static void Interleaved_Write(benchmark::State& state) {
StereoBuffer<float> buffer (state.range(0));
std::vector<float> input (state.range(0) * 2);
buffer.readInterleaved(input.data(), state.range(0));
std::vector<float> output (state.range(0) * 2);
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.writeInterleaved(output.data(), state.range(0));
}
}
BENCHMARK(Interleaved_Write)->Range((8<<10) + 3, (8<<20) + 3);
static void Interleaved_Write_SSE(benchmark::State& state) {
StereoBuffer<float> buffer (state.range(0));
std::vector<float> input (state.range(0) * 2);
buffer.readInterleaved<true>(input.data(), state.range(0));
std::vector<float> output (state.range(0) * 2);
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.writeInterleaved<true>(output.data(), state.range(0));
}
}
BENCHMARK(Interleaved_Write_SSE)->Range((8<<10) + 3, (8<<20) + 3);
static void Unaligned_Interleaved_Write(benchmark::State& state) {
StereoBuffer<float> buffer (state.range(0));
std::vector<float> input (state.range(0) * 2);
buffer.readInterleaved(input.data(), state.range(0));
std::vector<float> output (state.range(0) * 2 + 1);
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.writeInterleaved(output.data() + 1, state.range(0));
}
}
BENCHMARK(Unaligned_Interleaved_Write)->Range((8<<10) + 3, (8<<20) + 3);
static void Unaligned_Interleaved_Write_SSE(benchmark::State& state) {
StereoBuffer<float> buffer (state.range(0));
std::vector<float> input (state.range(0) * 2);
buffer.readInterleaved<true>(input.data(), state.range(0));
std::vector<float> output (state.range(0) * 2 + 1);
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
buffer.writeInterleaved<true>(output.data() + 1, state.range(0));
}
}
BENCHMARK(Unaligned_Interleaved_Write_SSE)->Range((8<<10) + 3, (8<<20) + 3);
BENCHMARK_MAIN();

40
sources/ADSREnvelope.h Normal file
View file

@ -0,0 +1,40 @@
#include "Globals.h"
namespace sfz
{
template<class Type>
class ADSREnvelope
{
public:
struct Description
{
Description()
: depth(1) {}
Descrition(Type depth)
: depth(depth) {}
int delay { 0 };
int attack { 0 };
int decay { 0 };
int release { 0 };
int hold { 0 };
float start { 0 };
float sustain { 0 };
Type depth;
};
ADSREnvelope() = default;
void reset(Description desc)
{
}
private:
enum class State
{
Delay, Attack, Hold, Sustain, Release, Done
};
State currentState { Done };
};
}

View file

@ -8,7 +8,19 @@ template<class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
class Buffer
{
public:
Buffer() { }
using value_type = std::remove_cv_t<Type>;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = size_t;
using difference_type = ptrdiff_t;
constexpr Buffer() { }
Buffer(size_t size)
{
resize(size);
@ -22,14 +34,14 @@ public:
}
auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end
auto* newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(Type)) : std::malloc(tempSize * sizeof(Type));
auto* newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(value_type)) : std::malloc(tempSize * sizeof(value_type));
if (newData == nullptr)
return false;
largerSize = tempSize;
alignedSize = newSize;
paddedData = static_cast<Type*>(newData);
normalData = static_cast<Type*>(std::align(Alignment, alignedSize, newData, tempSize));
paddedData = static_cast<pointer>(newData);
normalData = static_cast<pointer>(std::align(Alignment, alignedSize, newData, tempSize));
normalEnd = normalData + alignedSize;
if (auto endMisalignment = (alignedSize & TypeAlignmentMask); endMisalignment != 0)
_alignedEnd = normalEnd + Alignment - endMisalignment;
@ -38,7 +50,6 @@ public:
return true;
}
Type* data() { return normalData; }
void clear()
{
largerSize = 0;
@ -54,22 +65,23 @@ public:
}
Type& operator[](int idx) { return *(normalData + idx); }
size_t size() const noexcept { return alignedSize; }
bool empty() const noexcept { return alignedSize == 0; }
Type* begin() noexcept { return data(); }
Type* end() noexcept { return normalEnd; }
Type* alignedEnd() noexcept { return _alignedEnd; }
constexpr pointer data() const noexcept { return normalData; }
constexpr size_type size() const noexcept { return alignedSize; }
constexpr bool empty() const noexcept { return alignedSize == 0; }
constexpr iterator begin() noexcept { return data(); }
constexpr iterator end() noexcept { return normalEnd; }
constexpr pointer alignedEnd() noexcept { return _alignedEnd; }
private:
static constexpr auto AlignmentMask { Alignment - 1 };
static constexpr auto TypeAlignment { Alignment / sizeof(Type) };
static constexpr auto TypeAlignment { Alignment / sizeof(value_type) };
static constexpr auto TypeAlignmentMask { TypeAlignment - 1 };
static_assert(std::is_arithmetic<Type>::value, "Type should be arithmetic");
static_assert(std::is_arithmetic<value_type>::value, "Type should be arithmetic");
static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value");
static_assert(TypeAlignment * sizeof(Type) == Alignment, "The alignment does not appear to be divided by the size of the Type");
size_t largerSize { 0 };
size_t alignedSize { 0 };
Type* normalData { nullptr };
Type* paddedData { nullptr };
Type* normalEnd { nullptr };
Type* _alignedEnd { nullptr };
static_assert(TypeAlignment * sizeof(value_type) == Alignment, "The alignment does not appear to be divided by the size of the Type");
size_type largerSize { 0 };
size_type alignedSize { 0 };
pointer normalData { nullptr };
pointer paddedData { nullptr };
pointer normalEnd { nullptr };
pointer _alignedEnd { nullptr };
};

View file

@ -1,4 +1,5 @@
#include "FilePool.h"
#include "gsl/gsl-lite.hpp"
#include <chrono>
using namespace std::chrono_literals;
@ -23,7 +24,7 @@ std::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(
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::useSIMD>(tempReadBuffer.data(), preloadedSize);
returnedValue.preloadedData->readInterleaved(gsl::make_span(tempReadBuffer).first(preloadedSize));
preloadedData[filename] = returnedValue.preloadedData;
// char buffer [2048] ;
// sndFile.command(SFC_GET_LOG_INFO, buffer, sizeof(buffer)) ;
@ -65,7 +66,7 @@ void sfz::FilePool::loadingThread()
auto fileLoaded = std::make_unique<StereoBuffer<float>>(fileToLoad.numFrames);
auto readBuffer = std::make_unique<Buffer<float>>(fileToLoad.numFrames * 2);
sndFile.readf(readBuffer->data(), fileToLoad.numFrames);
fileLoaded->readInterleaved<SIMDConfig::useSIMD>(readBuffer->data(), fileToLoad.numFrames);
fileLoaded->readInterleaved(*readBuffer);
fileToLoad.voice->setFileData(std::move(fileLoaded));
}

View file

@ -5,27 +5,30 @@ namespace sfz
namespace config
{
inline constexpr double defaultSampleRate { 48000 };
inline constexpr int defaultSamplesPerBlock { 1024 };
inline constexpr int preloadSize { 32768 };
inline constexpr int numChannels { 2 };
inline constexpr int numVoices { 64 };
inline constexpr int numLoadingThreads { 4 };
inline constexpr int centPerSemitone { 100 };
inline constexpr float virtuallyZero { 0.00005f };
inline constexpr double fastReleaseDuration { 0.01 };
inline constexpr char defineCharacter { '$' };
inline constexpr int oversamplingFactor { 2 };
constexpr double defaultSampleRate { 48000 };
constexpr int defaultSamplesPerBlock { 1024 };
constexpr int preloadSize { 32768 };
constexpr int numChannels { 2 };
constexpr int numVoices { 64 };
constexpr int numLoadingThreads { 4 };
constexpr int centPerSemitone { 100 };
constexpr float virtuallyZero { 0.00005f };
constexpr double fastReleaseDuration { 0.01 };
constexpr char defineCharacter { '$' };
constexpr int oversamplingFactor { 2 };
} // namespace config
} // namespace sfz
namespace SIMDConfig
{
inline constexpr unsigned int defaultAlignment { 16 };
constexpr unsigned int defaultAlignment { 16 };
constexpr bool writeInterleaved { true };
constexpr bool readInterleaved { true };
constexpr bool fill { false };
#if USE_SIMD
inline constexpr bool useSIMD { true };
constexpr bool useSIMD { true };
#else
inline constexpr bool useSIMD { false };
constexpr bool useSIMD { false };
#endif
}

13
sources/LinearEnvelope.h Normal file
View file

@ -0,0 +1,13 @@
#include "Globals.h"
namespace sfz
{
class LinearEnvelope
{
public:
private:
};
}

35
sources/SIMDDummy.cpp Normal file
View file

@ -0,0 +1,35 @@
#include "SIMDHelpers.h"
#include "Helpers.h"
template<>
void readInterleaved<float, true>(gsl::span<const float> input, gsl::span<float> outputLeft, gsl::span<float> outputRight) noexcept
{
readInterleaved<float, false>(input, outputLeft, outputRight);
}
template<>
void writeInterleaved<float, true>(gsl::span<const float> inputLeft, gsl::span<const float> inputRight, gsl::span<float> output) noexcept
{
writeInterleaved<float, false>(inputLeft, inputRight, output);
}
// template<class Type, bool SIMD=false>
// void loopingSFZIndex(gsl::span<const Type> inputLeft, gsl::span<const Type> inputRight, gsl::span<Type> output);
// template<class Type, bool SIMD=false>
// void linearRamp(gsl::span<Type> output, Type start, Type end);
// template<class Type, bool SIMD=false>
// void exponentialRamp(gsl::span<Type> output, Type start, Type end);
// template<class Type, bool SIMD=false>
// void applyGain(Type gain, gsl::span<Type> output);
// template<class Type, bool SIMD=false>
// void applyGain(gsl::span<const Type> output, gsl::span<Type> output);
template<>
void fill<float, true>(gsl::span<float> output, float value) noexcept
{
fill<float, false>(output, value);
}

67
sources/SIMDHelpers.h Normal file
View file

@ -0,0 +1,67 @@
#include "gsl/gsl-lite.hpp"
#include "Globals.h"
#include "Helpers.h"
template<class T, bool SIMD=SIMDConfig::readInterleaved>
void readInterleaved(gsl::span<const T> input, gsl::span<T> outputLeft, gsl::span<T> outputRight) noexcept
{
// The size of the output is not big enough for the input...
ASSERT(outputLeft.size() >= input.size() / 2);
ASSERT(outputRight.size() >= input.size() / 2);
auto* in = input.begin();
auto* lOut = outputLeft.begin();
auto* rOut = outputRight.begin();
while (in < (input.end() - 1) && lOut < outputLeft.end() && rOut < outputRight.end())
{
*lOut++ = *in++;
*rOut++ = *in++;
}
}
template<class T, bool SIMD=SIMDConfig::writeInterleaved>
void writeInterleaved(gsl::span<const T> inputLeft, gsl::span<const T> inputRight, gsl::span<T> output) noexcept
{
ASSERT(inputLeft.size() <= output.size() / 2);
ASSERT(inputRight.size() <= output.size() / 2);
auto* lIn = inputLeft.begin();
auto* rIn = inputRight.begin();
auto* out = output.begin();
while (lIn < inputLeft.end() && rIn < inputRight.end() && out < (output.end() - 1))
{
*out++ = *lIn++;
*out++ = *rIn++;
}
}
// Specializations
template<>
void writeInterleaved<float, true>(gsl::span<const float> inputLeft, gsl::span<const float> inputRight, gsl::span<float> output) noexcept;
template<>
void readInterleaved<float, true>(gsl::span<const float> input, gsl::span<float> outputLeft, gsl::span<float> outputRight) noexcept;
template<class T, bool SIMD=SIMDConfig::fill>
void fill(gsl::span<T> output, T value) noexcept
{
std::fill(output.begin(), output.end(), value);
}
template<>
void fill<float, true>(gsl::span<float> output, float value) noexcept;
template<class T, bool SIMD=SIMDConfig::useSIMD>
void loopingSFZIndex(gsl::span<const T> inputLeft, gsl::span<const T> inputRight, gsl::span<T> output);
template<class T, bool SIMD=SIMDConfig::useSIMD>
void linearRamp(gsl::span<T> output, T start, T end);
template<class T, bool SIMD=SIMDConfig::useSIMD>
void exponentialRamp(gsl::span<T> output, T start, T end);
template<class T, bool SIMD=SIMDConfig::useSIMD>
void applyGain(T gain, gsl::span<T> output);
template<class T, bool SIMD=SIMDConfig::useSIMD>
void applyGain(gsl::span<const T> gain, gsl::span<T> output);

107
sources/SIMDSSE.cpp Normal file
View file

@ -0,0 +1,107 @@
#include "SIMDHelpers.h"
#include "Helpers.h"
#include "x86intrin.h"
constexpr int TypeAlignment { 4 };
using Type = float;
template<>
void readInterleaved<Type, true>(gsl::span<const Type> input, gsl::span<Type> outputLeft, gsl::span<Type> outputRight) noexcept
{
// The size of the outputs is not big enough for the input...
ASSERT(outputLeft.size() >= input.size() / 2);
ASSERT(outputRight.size() >= input.size() / 2);
// Input is too small
ASSERT(input.size() > 1);
auto* in = input.begin();
auto* lOut = outputLeft.begin();
auto* rOut = outputRight.begin();
const int unalignedEnd = input.size() & (2 * TypeAlignment - 1);
const int lastAligned = input.size() - unalignedEnd;
auto* inputSentinel = in + lastAligned;
while (in < inputSentinel && lOut < outputLeft.end() && rOut < outputRight.end())
{
auto register0 = _mm_loadu_ps(in);
in += TypeAlignment;
auto register1 = _mm_loadu_ps(in);
in += TypeAlignment;
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_storeu_ps(lOut, register0);
_mm_storeu_ps(rOut, register1);
lOut += TypeAlignment;
rOut += TypeAlignment;
}
inputSentinel = input.end() - 1;
while (in < inputSentinel && lOut < outputLeft.end() && rOut < outputRight.end())
{
*lOut++ = *in++;
*rOut++ = *in++;
}
}
template<>
void writeInterleaved<Type, true>(gsl::span<const Type> inputLeft, gsl::span<const Type> inputRight, gsl::span<Type> output) noexcept
{
// The size of the output is not big enough for the inputs...
ASSERT(inputLeft.size() <= output.size() / 2);
ASSERT(inputRight.size() <= output.size() / 2);
auto* lIn = inputLeft.begin();
auto* rIn = inputRight.begin();
auto* out = output.begin();
const int residualLeft = inputLeft.size() & (TypeAlignment - 1);
const int residualRight = inputRight.size() & (TypeAlignment - 1);
const auto* leftSentinel = lIn + inputLeft.size() - residualLeft;
const auto* rightSentinel = rIn + inputRight.size() - residualRight;
const auto* outputSentinel = output.end() - 1;
while (lIn < leftSentinel && rIn < rightSentinel && out < outputSentinel)
{
const auto lInRegister = _mm_loadu_ps(lIn);
const auto rInRegister = _mm_loadu_ps(rIn);
const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister);
_mm_storeu_ps(out, outRegister1);
out += TypeAlignment;
const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister);
_mm_storeu_ps(out, outRegister2);
out += TypeAlignment;
lIn += TypeAlignment;
rIn += TypeAlignment;
}
while (lIn < inputLeft.end() && rIn < inputRight.end() && out < outputSentinel)
{
*out++ = *lIn++;
*out++ = *rIn++;
}
}
template<>
void fill<float, true>(gsl::span<float> output, float value) noexcept
{
const auto mmValue = _mm_set_ps1(value);
auto* out = output.begin();
const int residual = output.size() & (TypeAlignment - 1);
const auto* sentinel = output.end() - residual;
while (out < sentinel) // we should only need to test a single channel
{
_mm_storeu_ps(out, mmValue);
out += TypeAlignment;
}
while (out < output.end())
*out++ = value;
}

View file

@ -2,9 +2,11 @@
#include "Buffer.h"
#include "Helpers.h"
#include "Globals.h"
#include "gsl/gsl-lite.hpp"
#include <array>
#include <iostream>
#include <type_traits>
#include "SIMDHelpers.h"
enum class Channel {left, right};
@ -46,42 +48,22 @@ public:
}
}
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);
::fill<Type>(leftBuffer, value);
::fill<Type>(rightBuffer, value);
}
template<bool useSIMD=false>
void readInterleaved(Type* input, int numFrames) noexcept
void readInterleaved(gsl::span<const Type> input) noexcept
{
auto* in = input;
auto* end = input + numChannels * numFrames;
auto [lOut, rOut] = getChannels();
while (in < end)
{
*lOut = *in;
in++;
*rOut = *in;
in++;
lOut += 1;
rOut += 1;
}
ASSERT(input.size() <= numChannels * numFrames);
::readInterleaved<Type>(input, leftBuffer, rightBuffer);
}
template<bool useSIMD=false>
void writeInterleaved(Type* output, int numFrames) noexcept
void writeInterleaved(gsl::span<Type> output) noexcept
{
ASSERT(numFrames <= this->numFrames);
auto [lIn, rIn] = getChannels();
auto* out = output;
auto* end = output + numChannels * numFrames;
while (out < end)
{
*out++ = *lIn++;
*out++ = *rIn++;
}
ASSERT(output.size() >= numChannels * numFrames);
::writeInterleaved<Type>(leftBuffer, rightBuffer, output);
}
Type* getChannel(Channel channel) noexcept
@ -148,11 +130,3 @@ 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

@ -1,95 +0,0 @@
#include "StereoBuffer.h"
#include "Intrinsics.h"
template <>
template <>
void StereoBuffer<float, 16>::readInterleaved<true>(float *input, int numFrames) noexcept
{
// The number of frames to read has to fit!
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();
// Cast to m128 so that pointer arithmetics make sense
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++;
}
}
template<>
template<>
void StereoBuffer<float, 16>::writeInterleaved<true>(float* output, int numFrames) noexcept
{
ASSERT(numFrames <= this->numFrames);
const int residualFrames = numFrames & (TypeAlignment - 1);
const int lastAligned = numFrames - residualFrames;
auto [lIn, rIn] = getChannels();
auto* out = output;
const auto* sseEnd = lIn + lastAligned;
while (lIn < sseEnd)
{
const auto lInRegister = _mm_load_ps(lIn);
const auto rInRegister = _mm_load_ps(rIn);
const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister);
_mm_storeu_ps(out, outRegister1);
out += TypeAlignment;
const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister);
_mm_storeu_ps(out, outRegister2);
out += TypeAlignment;
lIn += TypeAlignment;
rIn += TypeAlignment;
}
const auto* end = output + numChannels * numFrames;
while (out < end)
{
*out++ = *lIn++;
*out++ = *rIn++;
}
}

View file

@ -22,7 +22,8 @@ public:
const Region* getRegionView(int idx) const noexcept { return (size_t)idx < regions.size() ? regions[idx].get() : nullptr; }
auto getUnknownOpcodes() { return unknownOpcodes; }
size_t getNumPreloadedSamples() { return filePool.getNumPreloadedSamples(); }
void prepareToPlay(int samplesPerBlock, double sampleRate);
void renderBlock(StereoBuffer<float>& buffer);
protected:
void callback(std::string_view header, std::vector<Opcode> members) final;
private:

267
tests/SIMDHelpersT.cpp Normal file
View file

@ -0,0 +1,267 @@
#include "catch2/catch.hpp"
#include "../sources/SIMDHelpers.h"
#include <algorithm>
using namespace Catch::literals;
constexpr int smallBufferSize { 3 };
constexpr int bigBufferSize { 4095 };
constexpr int medBufferSize { 127 };
constexpr double fillValue { 1.3 };
TEST_CASE("[Helpers] fill() - Manual buffer")
{
std::vector<float> buffer (5);
std::vector<float> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
fill<float, false>(buffer, fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Small buffer")
{
std::vector<float> buffer (smallBufferSize);
std::vector<float> expected (smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
fill<float, false>(buffer, fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Big buffer")
{
std::vector<float> buffer (bigBufferSize);
std::vector<float> expected (bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
fill<float, false>(buffer, fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Small buffer -- SIMD")
{
std::vector<float> buffer (smallBufferSize);
std::vector<float> expected (smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
fill<float, true>(buffer, fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Big buffer -- SIMD")
{
std::vector<float> buffer (bigBufferSize);
std::vector<float> expected (bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
fill<float, true>(buffer, fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Small buffer -- doubles")
{
std::vector<double> buffer (smallBufferSize);
std::vector<double> expected (smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
fill<double, false>(buffer, fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Big buffer -- doubles")
{
std::vector<double> buffer (bigBufferSize);
std::vector<double> expected (bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
fill<double, false>(buffer, fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] Interleaved read")
{
std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput { 0.0f };
std::array<float, 8> rightOutput { 0.0f };
readInterleaved<float, false>(input, leftOutput, rightOutput);
std::array<float, 16> real { 0.0f };
auto realIdx = 0;
for (auto value: leftOutput)
real[realIdx++] = value;
for (auto value: rightOutput)
real[realIdx++] = value;
REQUIRE( real == expected );
}
TEST_CASE("[Helpers] Interleaved read unaligned end")
{
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f};
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f};
std::array<float, 10> leftOutput { 0.0f };
std::array<float, 10> rightOutput { 0.0f };
readInterleaved<float, false>(input, leftOutput, rightOutput);
std::array<float, 20> real { 0.0f };
auto realIdx = 0;
for (auto value: leftOutput)
real[realIdx++] = value;
for (auto value: rightOutput)
real[realIdx++] = value;
REQUIRE( real == expected );
}
TEST_CASE("[Helpers] Small interleaved read unaligned end")
{
std::array<float, 6> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f};
std::array<float, 6> expected = { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f};
std::array<float, 3> leftOutput { 0.0f };
std::array<float, 3> rightOutput { 0.0f };
readInterleaved<float, false>(input, leftOutput, rightOutput);
std::array<float, 6> real { 0.0f };
auto realIdx = 0;
for (auto value: leftOutput)
real[realIdx++] = value;
for (auto value: rightOutput)
real[realIdx++] = value;
REQUIRE( real == expected );
}
TEST_CASE("[Helpers] Interleaved read -- SIMD")
{
std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput { 0.0f };
std::array<float, 8> rightOutput { 0.0f };
readInterleaved<float, true>(input, leftOutput, rightOutput);
std::array<float, 16> real { 0.0f };
auto realIdx = 0;
for (auto value: leftOutput)
real[realIdx++] = value;
for (auto value: rightOutput)
real[realIdx++] = value;
REQUIRE( real == expected );
}
TEST_CASE("[Helpers] Interleaved read unaligned end -- SIMD")
{
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f};
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f};
std::array<float, 10> leftOutput { 0.0f };
std::array<float, 10> rightOutput { 0.0f };
readInterleaved<float, true>(input, leftOutput, rightOutput);
std::array<float, 20> real { 0.0f };
auto realIdx = 0;
for (auto value: leftOutput)
real[realIdx++] = value;
for (auto value: rightOutput)
real[realIdx++] = value;
REQUIRE( real == expected );
}
TEST_CASE("[Helpers] Small interleaved read unaligned end -- SIMD")
{
std::array<float, 6> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f};
std::array<float, 6> expected = { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f};
std::array<float, 3> leftOutput { 0.0f };
std::array<float, 3> rightOutput { 0.0f };
readInterleaved<float, true>(input, leftOutput, rightOutput);
std::array<float, 6> real { 0.0f };
auto realIdx = 0;
for (auto value: leftOutput)
real[realIdx++] = value;
for (auto value: rightOutput)
real[realIdx++] = value;
REQUIRE( real == expected );
}
TEST_CASE("[Helpers] Interleaved read SIMD vs Scalar")
{
std::array<float, medBufferSize * 2> input;
std::array<float, medBufferSize> leftOutputScalar;
std::array<float, medBufferSize> rightOutputScalar;
std::array<float, medBufferSize> leftOutputSIMD;
std::array<float, medBufferSize> rightOutputSIMD;
std::iota(input.begin(), input.end(), 0.0f);
readInterleaved<float, false>(input, leftOutputScalar, rightOutputScalar);
readInterleaved<float, true>(input, leftOutputSIMD, rightOutputSIMD);
REQUIRE( leftOutputScalar == leftOutputSIMD );
REQUIRE( rightOutputScalar == rightOutputSIMD );
}
TEST_CASE("[Helpers] Interleaved write")
{
std::array<float, 8> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, };
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output;
std::array<float, 16> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
writeInterleaved<float, false>(leftInput, rightInput, output);
REQUIRE( output == expected );
}
TEST_CASE("[Helpers] Interleaved write unaligned end")
{
std::array<float, 10> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f};
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output;
std::array<float, 20> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f};
writeInterleaved<float, false>(leftInput, rightInput, output);
REQUIRE( output == expected );
}
TEST_CASE("[Helpers] Small interleaved write unaligned end")
{
std::array<float, 3> leftInput { 0.0f, 1.0f, 2.0f};
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output;
std::array<float, 6> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f};
writeInterleaved<float, false>(leftInput, rightInput, output);
REQUIRE( output == expected );
}
TEST_CASE("[Helpers] Interleaved write -- SIMD")
{
std::array<float, 8> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, };
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output;
std::array<float, 16> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
writeInterleaved<float, true>(leftInput, rightInput, output);
REQUIRE( output == expected );
}
TEST_CASE("[Helpers] Interleaved write unaligned end -- SIMD")
{
std::array<float, 10> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f};
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output;
std::array<float, 20> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f};
writeInterleaved<float, true>(leftInput, rightInput, output);
REQUIRE( output == expected );
}
TEST_CASE("[Helpers] Small interleaved write unaligned end -- SIMD")
{
std::array<float, 3> leftInput { 0.0f, 1.0f, 2.0f};
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output;
std::array<float, 6> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f};
writeInterleaved<float, true>(leftInput, rightInput, output);
REQUIRE( output == expected );
}
TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar")
{
std::array<float, medBufferSize> leftInput;
std::array<float, medBufferSize> rightInput;
std::array<float, medBufferSize * 2> outputScalar;
std::array<float, medBufferSize * 2> outputSIMD;
std::iota(leftInput.begin(), leftInput.end(), 0.0f);
std::iota(rightInput.begin(), rightInput.end(), medBufferSize);
writeInterleaved<float, false>(leftInput, rightInput, outputScalar);
writeInterleaved<float, true>(leftInput, rightInput, outputSIMD);
REQUIRE( outputScalar == outputSIMD );
}

View file

@ -131,194 +131,22 @@ TEST_CASE("[StereoBuffer] Channel alignments (doubles)")
}
TEST_CASE("[AudioBuffer] fills")
{
SECTION("Floats - 0.0")
{
StereoBuffer<float> buffer(10);
buffer.fill(0.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0f);
std::array<float, 10> real { 2.0f };
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected );
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
SECTION("Floats - 1.0")
{
StereoBuffer<float> buffer(10);
buffer.fill(1.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 1.0f);
std::array<float, 10> real { 2.0f };
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected );
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
SECTION("Doubles - 0.0")
{
StereoBuffer<double> buffer(10);
buffer.fill(0.0);
std::array<double, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0);
std::array<double, 10> real { 2.0 };
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected );
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
SECTION("Doubles - 1.0")
{
StereoBuffer<double> buffer(10);
buffer.fill(1.0);
std::array<double, 10> expected;
std::fill(expected.begin(), expected.end(), 1.0);
std::array<double, 10> real { 2.0 };
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected );
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
SECTION("Floats - 0.0 - SIMD")
{
StereoBuffer<float> buffer(10);
buffer.fill<true>(0.0f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 0.0f);
std::array<float, 10> real { 2.0f };
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected );
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
SECTION("Floats - 1.0 - SIMD")
{
StereoBuffer<float> buffer(10);
buffer.fill<true>(1.0f);
std::array<float, 10> expected { 1.0f };
std::fill(expected.begin(), expected.end(), 1.0f);
std::array<float, 10> real { 2.0f };
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected );
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
}
TEST_CASE("[StereoBuffer] Interleave read")
{
StereoBuffer<float> buffer(8);
std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
buffer.readInterleaved(input.data(), 8);
std::array<float, 16> real { 0.0f };
auto realIdx = 0;
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::left, frameIdx);
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
TEST_CASE("[StereoBuffer] Interleave read -- SIMD")
{
StereoBuffer<float> buffer(8);
std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f};
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
buffer.readInterleaved<true>(input.data(), 8);
std::array<float, 16> real { 0.0f };
auto realIdx = 0;
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::left, frameIdx);
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
TEST_CASE("[StereoBuffer] Interleave read unaligned end -- SIMD")
{
StereoBuffer<float> buffer(10);
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f};
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f};
buffer.readInterleaved<true>(input.data(), 10);
std::array<float, 20> real { 0.0f };
auto realIdx = 0;
buffer.fill(1.3f);
std::array<float, 10> expected;
std::fill(expected.begin(), expected.end(), 1.3f);
std::array<float, 10> real { 0.0f };
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::left, frameIdx);
real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected );
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::right, frameIdx);
real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
TEST_CASE("[StereoBuffer] small interleaved read unaligned end -- SIMD")
{
StereoBuffer<float> buffer(3);
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f};
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f};
buffer.readInterleaved<true>(input.data(), 3);
std::array<float, 20> real { 0.0f };
auto realIdx = 0;
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::left, frameIdx);
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[realIdx++] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected );
}
TEST_CASE("[StereoBuffer] SIMD vs scalar")
{
constexpr int numFrames { 91 };
StereoBuffer<float> buffer(numFrames);
std::array<float, numFrames * 2> input;
std::iota(input.begin(), input.end(), 0.0f);
StereoBuffer<float> expectedBuffer (numFrames);
expectedBuffer.readInterleaved(input.data(), 10);
buffer.readInterleaved<true>(input.data(), 10);
std::array<float, numFrames * 2> expectedArray { 0.0f };
std::array<float, numFrames * 2> realArray { 0.0f };
auto sampleIdx = 0;
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
{
expectedArray[sampleIdx] = buffer(Channel::left, frameIdx);
realArray[sampleIdx++] = buffer(Channel::left, frameIdx);
}
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
{
expectedArray[sampleIdx] = buffer(Channel::right, frameIdx);
realArray[sampleIdx++] = buffer(Channel::right, frameIdx);
}
REQUIRE( realArray == expectedArray );
}
TEST_CASE("[AudioBuffer] Fill a big Audiobuffer")
{
@ -326,7 +154,7 @@ TEST_CASE("[AudioBuffer] Fill a big Audiobuffer")
StereoBuffer<float> buffer (size);
std::vector<float> input (2*size);
std::iota(input.begin(), input.end(), 1.0f);
buffer.readInterleaved(input.data(), size);
buffer.readInterleaved(input);
}
TEST_CASE("[StereoBuffer] Interleaved write -- Scalar")
@ -334,8 +162,8 @@ TEST_CASE("[StereoBuffer] Interleaved write -- Scalar")
StereoBuffer<float> buffer(10);
std::array<float, 20> input = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f};
std::array<float, 20> output { 0.0f };
buffer.readInterleaved<false>(input.data(), 10);
buffer.writeInterleaved<false>(output.data(), 10);
buffer.readInterleaved(input);
buffer.writeInterleaved(output);
REQUIRE( output == input );
}
@ -344,17 +172,17 @@ TEST_CASE("[StereoBuffer] Interleaved write -- SIMD")
StereoBuffer<float> buffer(10);
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f};
std::array<float, 20> output { 0.0f };
buffer.readInterleaved<false>(input.data(), 10);
buffer.writeInterleaved<true>(output.data(), 10);
buffer.readInterleaved(input);
buffer.writeInterleaved(output);
REQUIRE( output == input );
}
TEST_CASE("[StereoBuffer] Small interleaved write -- SIMD")
{
StereoBuffer<float> buffer(3);
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f};
std::array<float, 20> output { 0.0f };
buffer.readInterleaved<false>(input.data(), 3);
buffer.writeInterleaved<true>(output.data(), 3);
std::array<float, 6> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f};
std::array<float, 6> output { 0.0f };
buffer.readInterleaved(input);
buffer.writeInterleaved(output);
REQUIRE( output == input );
}