SIMD refactoring
This commit is contained in:
parent
471b531d03
commit
7dc65999a4
22 changed files with 944 additions and 517 deletions
|
|
@ -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
53
benchmarks/BM_fill.cpp
Normal 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();
|
||||
59
benchmarks/BM_writeInterleaved.cpp
Normal file
59
benchmarks/BM_writeInterleaved.cpp
Normal 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
100
benchmarks/Cum_Prod.cpp
Normal 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
100
benchmarks/Cum_Sum.cpp
Normal 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();
|
||||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
@ -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();
|
||||
|
|
@ -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
40
sources/ADSREnvelope.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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 };
|
||||
};
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
13
sources/LinearEnvelope.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#include "Globals.h"
|
||||
namespace sfz
|
||||
{
|
||||
|
||||
class LinearEnvelope
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
35
sources/SIMDDummy.cpp
Normal file
35
sources/SIMDDummy.cpp
Normal 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
67
sources/SIMDHelpers.h
Normal 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
107
sources/SIMDSSE.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
267
tests/SIMDHelpersT.cpp
Normal 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 );
|
||||
}
|
||||
|
|
@ -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 );
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue