diff --git a/benchmarks/BM_resample.cpp b/benchmarks/BM_resample.cpp index c10a7164..27110ef7 100644 --- a/benchmarks/BM_resample.cpp +++ b/benchmarks/BM_resample.cpp @@ -22,17 +22,190 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Buffer.h" -#include "Oversampler.h" +#include "AudioBuffer.h" #include "SIMDHelpers.h" #include #include #include #include #include "ghc/filesystem.hpp" +#include "hiir/Upsampler2xFpu.h" +constexpr std::array coeffsStage2x { + 0.036681502163648017, + 0.13654762463195771, + 0.27463175937945411, + 0.42313861743656667, + 0.56109869787919475, + 0.67754004997416162, + 0.76974183386322659, + 0.83988962484963803, + 0.89226081800387891, + 0.9315419599631839, + 0.96209454837808395, + 0.98781637073289708 +}; + +constexpr std::array coeffsStage4x { + 0.042448989488488006, + 0.17072114107630679, + 0.39329183835224008, + 0.74569514831986694 +}; + +constexpr std::array coeffsStage8x { + 0.055748680811302048, + 0.24305119574153092, + 0.6466991311926823 +}; + +template +void upsample2xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xFpu upsampler; + upsampler.set_coefs(coeffsStage2x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + + +template +void upsample4xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xFpu upsampler; + upsampler.set_coefs(coeffsStage4x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +template +void upsample8xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xFpu upsampler; + upsampler.set_coefs(coeffsStage8x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +#if defined(__x86_64__) || defined(__i386__) +#include "hiir/Upsampler2xSse.h" +template<> +void upsample2xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xSse upsampler; + upsampler.set_coefs(coeffsStage2x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +template<> +void upsample4xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xSse upsampler; + upsampler.set_coefs(coeffsStage4x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +template<> +void upsample8xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xSse upsampler; + upsampler.set_coefs(coeffsStage8x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +#elif defined(__arm__) || defined (__aarch64__) + +#include "hiir/Upsampler2xNeon.h" + +template<> +void upsample2xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xNeon upsampler; + upsampler.set_coefs(coeffsStage2x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +template<> +void upsample4xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xNeon upsampler; + upsampler.set_coefs(coeffsStage4x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +template<> +void upsample8xStage(absl::Span input, absl::Span output) +{ + ASSERT(output.size() >= 2 * input.size()); + hiir::Upsampler2xNeon upsampler; + upsampler.set_coefs(coeffsStage8x.data()); + upsampler.process_block(output.data(), input.data(), input.size()); +} + +#else + +template<> +void upsample2xStage(absl::Span input, absl::Span output) +{ + upsample2xStage(input, output); +} +template<> +void upsample4xStage(absl::Span input, absl::Span output) +{ + upsample4xStage(input, output); +} +template<> +void upsample8xStage(absl::Span input, absl::Span output) +{ + upsample8xStage(input, output); +} +#endif + +template +std::unique_ptr> upsample2x(const sfz::AudioBuffer& buffer) +{ + // auto tempBuffer = std::make_unique>(buffer.getNumFrames() * 2); + auto outputBuffer = std::make_unique>(buffer.getNumChannels(), buffer.getNumFrames() * 2); + for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) { + upsample2xStage(buffer.getConstSpan(channelIdx), outputBuffer->getSpan(channelIdx)); + } + return outputBuffer; +} + +template +std::unique_ptr> upsample4x(const sfz::AudioBuffer& buffer) +{ + auto tempBuffer = std::make_unique>(buffer.getNumFrames() * 2); + auto outputBuffer = std::make_unique>(buffer.getNumChannels(), buffer.getNumFrames() * 4); + for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) { + upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer)); + upsample4xStage(absl::MakeConstSpan(*tempBuffer), outputBuffer->getSpan(channelIdx)); + } + return outputBuffer; +} + +template +std::unique_ptr> upsample8x(const sfz::AudioBuffer& buffer) +{ + auto tempBuffer2x = std::make_unique>(buffer.getNumFrames() * 2); + auto tempBuffer4x = std::make_unique>(buffer.getNumFrames() * 4); + auto outputBuffer = std::make_unique>(buffer.getNumChannels(), buffer.getNumFrames() * 8); + for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) { + upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer2x)); + upsample4xStage(absl::MakeConstSpan(*tempBuffer2x), absl::MakeSpan(*tempBuffer4x)); + upsample8xStage(absl::MakeConstSpan(*tempBuffer4x), outputBuffer->getSpan(channelIdx)); + } + return outputBuffer; +} + class SndFile : public benchmark::Fixture { public: - void SetUp(const ::benchmark::State& state) + void SetUp(const ::benchmark::State& /* state */) { const auto rootPath = getPath() / "sample1.wav"; @@ -77,7 +250,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR2X_scalar)(benchmark::State& state) for (auto _ : state) { auto baseBuffer = std::make_unique>(numChannels, numFrames); sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1)); - auto outBuffer = sfz::upsample2x(*baseBuffer); + auto outBuffer = upsample2x(*baseBuffer); benchmark::DoNotOptimize(outBuffer); } } @@ -87,7 +260,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR4X_scalar)(benchmark::State& state) for (auto _ : state) { auto baseBuffer = std::make_unique>(numChannels, numFrames); sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1)); - auto outBuffer = sfz::upsample4x(*baseBuffer); + auto outBuffer = upsample4x(*baseBuffer); benchmark::DoNotOptimize(outBuffer); } } @@ -97,7 +270,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_scalar)(benchmark::State& state) for (auto _ : state) { auto baseBuffer = std::make_unique>(numChannels, numFrames); sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1)); - auto outBuffer = sfz::upsample8x(*baseBuffer); + auto outBuffer = upsample8x(*baseBuffer); benchmark::DoNotOptimize(outBuffer); } } @@ -107,7 +280,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR2X_vector)(benchmark::State& state) for (auto _ : state) { auto baseBuffer = std::make_unique>(numChannels, numFrames); sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1)); - auto outBuffer = sfz::upsample2x(*baseBuffer); + auto outBuffer = upsample2x(*baseBuffer); benchmark::DoNotOptimize(outBuffer); } } @@ -117,7 +290,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR4X_vector)(benchmark::State& state) for (auto _ : state) { auto baseBuffer = std::make_unique>(numChannels, numFrames); sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1)); - auto outBuffer = sfz::upsample4x(*baseBuffer); + auto outBuffer = upsample4x(*baseBuffer); benchmark::DoNotOptimize(outBuffer); } } @@ -127,7 +300,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_vector)(benchmark::State& state) for (auto _ : state) { auto baseBuffer = std::make_unique>(numChannels, numFrames); sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1)); - auto outBuffer = sfz::upsample8x(*baseBuffer); + auto outBuffer = upsample8x(*baseBuffer); benchmark::DoNotOptimize(outBuffer); } } @@ -291,7 +464,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_default)(benchmark::State& state) for (auto _ : state) { auto baseBuffer = std::make_unique>(numChannels, numFrames); sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1)); - auto outBuffer = sfz::upsample8x(*baseBuffer); + auto outBuffer = upsample8x(*baseBuffer); benchmark::DoNotOptimize(outBuffer); } } diff --git a/benchmarks/BM_resampleChunk.cpp b/benchmarks/BM_resampleChunk.cpp index 10dcd75f..9e4358b6 100644 --- a/benchmarks/BM_resampleChunk.cpp +++ b/benchmarks/BM_resampleChunk.cpp @@ -30,6 +30,54 @@ #include #include + +constexpr std::array coeffsStage2x { + 0.036681502163648017, + 0.13654762463195771, + 0.27463175937945411, + 0.42313861743656667, + 0.56109869787919475, + 0.67754004997416162, + 0.76974183386322659, + 0.83988962484963803, + 0.89226081800387891, + 0.9315419599631839, + 0.96209454837808395, + 0.98781637073289708 +}; + +constexpr std::array coeffsStage4x { + 0.042448989488488006, + 0.17072114107630679, + 0.39329183835224008, + 0.74569514831986694 +}; + +constexpr std::array coeffsStage8x { + 0.055748680811302048, + 0.24305119574153092, + 0.6466991311926823 +}; + + +#if defined(__x86_64__) || defined(__i386__) +#include "hiir/Upsampler2xSse.h" +using Upsampler2x = hiir::Upsampler2xSse; +using Upsampler4x = hiir::Upsampler2xSse; +using Upsampler8x = hiir::Upsampler2xSse; +#elif defined(__arm__) || defined(__aarch64__) +#include "hiir/Upsampler2xNeon.h" +using Upsampler2x = hiir::Upsampler2xNeon; +using Upsampler4x = hiir::Upsampler2xNeon; +using Upsampler8x = hiir::Upsampler2xNeon; +#else +#include "hiir/Upsampler2xFpu.h" +using Upsampler2x = hiir::Upsampler2xFpu; +using Upsampler4x = hiir::Upsampler2xFpu; +using Upsampler8x = hiir::Upsampler2xFpu; +#endif + + class FileFixture : public benchmark::Fixture { public: void SetUp(const ::benchmark::State& /* state */) { @@ -82,14 +130,23 @@ BENCHMARK_DEFINE_F(FileFixture, ResampleAtOnce)(benchmark::State& state) { { sfz::Buffer buffer { numFrames * sndfile.channels() }; sfz::Buffer temp { numFrames * 4 }; + + Upsampler2x upsampler2x; + Upsampler4x upsampler4x; + upsampler2x.set_coefs(coeffsStage2x.data()); + upsampler4x.set_coefs(coeffsStage4x.data()); + sndfile.readf(buffer.data(), numFrames); sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1)); - sfz::upsample2xStage(output->getConstSpan(0).first(numFrames), absl::MakeSpan(temp)); - sfz::upsample4xStage(absl::MakeConstSpan(temp).first(numFrames * 2), output->getSpan(0)); + upsampler2x.process_block(temp.data(), output->channelReader(0), numFrames); + upsampler4x.process_block(output->channelWriter(0), temp.data(), numFrames * 2); - sfz::upsample2xStage(output->getConstSpan(1).first(numFrames), absl::MakeSpan(temp)); - sfz::upsample4xStage(absl::MakeConstSpan(temp).first(numFrames * 2), output->getSpan(1)); + upsampler2x.clear_buffers(); + upsampler4x.clear_buffers(); + + upsampler2x.process_block(temp.data(), output->channelReader(1), numFrames); + upsampler4x.process_block(output->channelWriter(1), temp.data(), numFrames * 2); } } @@ -110,6 +167,15 @@ BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) { auto rightSpan = absl::MakeSpan(rightInput); auto chunkSpan = absl::MakeSpan(chunk); + Upsampler2x upsampler2xLeft; + Upsampler2x upsampler2xRight; + Upsampler4x upsampler4xLeft; + Upsampler4x upsampler4xRight; + upsampler2xLeft.set_coefs(coeffsStage2x.data()); + upsampler2xRight.set_coefs(coeffsStage2x.data()); + upsampler4xLeft.set_coefs(coeffsStage4x.data()); + upsampler4xRight.set_coefs(coeffsStage4x.data()); + size_t inputFrameCounter { 0 }; size_t outputFrameCounter { 0 }; while(inputFrameCounter < numFrames) @@ -122,11 +188,12 @@ BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) { ); sfz::readInterleaved(bufferChunk, leftSpan, rightSpan); - sfz::upsample2xStage(leftSpan.first(thisChunkSize), chunkSpan); - sfz::upsample4xStage(chunkSpan.first(thisChunkSize * 2), output->getSpan(0).subspan(outputFrameCounter)); - sfz::upsample2xStage(rightSpan.first(thisChunkSize), chunkSpan); - sfz::upsample4xStage(chunkSpan.first(thisChunkSize * 2), output->getSpan(1).subspan(outputFrameCounter)); + upsampler2xLeft.process_block(chunkSpan.data(), leftSpan.data(), thisChunkSize); + upsampler4xLeft.process_block(output->channelWriter(0) + outputFrameCounter, chunkSpan.data(), thisChunkSize * 2); + + upsampler2xRight.process_block(chunkSpan.data(), rightSpan.data(), thisChunkSize); + upsampler4xRight.process_block(output->channelWriter(1) + outputFrameCounter, chunkSpan.data(), thisChunkSize * 2); inputFrameCounter += chunkSize; outputFrameCounter += chunkSize * 4;