Update the oversampling benchmarks to be more contained

This commit is contained in:
Paul Ferrand 2019-12-22 23:34:39 +01:00
parent 528593fa76
commit 8e95ce63ec
2 changed files with 257 additions and 17 deletions

View file

@ -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 <benchmark/benchmark.h>
#include <memory>
#include <samplerate.h>
#include <sndfile.hh>
#include "ghc/filesystem.hpp"
#include "hiir/Upsampler2xFpu.h"
constexpr std::array<double, 12> 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<double, 4> coeffsStage4x {
0.042448989488488006,
0.17072114107630679,
0.39329183835224008,
0.74569514831986694
};
constexpr std::array<double, 3> coeffsStage8x {
0.055748680811302048,
0.24305119574153092,
0.6466991311926823
};
template<bool SIMD=false>
void upsample2xStage(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xFpu<coeffsStage2x.size()> upsampler;
upsampler.set_coefs(coeffsStage2x.data());
upsampler.process_block(output.data(), input.data(), input.size());
}
template<bool SIMD=false>
void upsample4xStage(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xFpu<coeffsStage4x.size()> upsampler;
upsampler.set_coefs(coeffsStage4x.data());
upsampler.process_block(output.data(), input.data(), input.size());
}
template<bool SIMD=false>
void upsample8xStage(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xFpu<coeffsStage8x.size()> 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<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xSse<coeffsStage2x.size()> upsampler;
upsampler.set_coefs(coeffsStage2x.data());
upsampler.process_block(output.data(), input.data(), input.size());
}
template<>
void upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xSse<coeffsStage4x.size()> upsampler;
upsampler.set_coefs(coeffsStage4x.data());
upsampler.process_block(output.data(), input.data(), input.size());
}
template<>
void upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xSse<coeffsStage8x.size()> 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<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xNeon<coeffsStage2x.size()> upsampler;
upsampler.set_coefs(coeffsStage2x.data());
upsampler.process_block(output.data(), input.data(), input.size());
}
template<>
void upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xNeon<coeffsStage4x.size()> upsampler;
upsampler.set_coefs(coeffsStage4x.data());
upsampler.process_block(output.data(), input.data(), input.size());
}
template<>
void upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
ASSERT(output.size() >= 2 * input.size());
hiir::Upsampler2xNeon<coeffsStage8x.size()> upsampler;
upsampler.set_coefs(coeffsStage8x.data());
upsampler.process_block(output.data(), input.data(), input.size());
}
#else
template<>
void upsample2xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
upsample2xStage<false>(input, output);
}
template<>
void upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
upsample4xStage<false>(input, output);
}
template<>
void upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
{
upsample8xStage<false>(input, output);
}
#endif
template <class T, bool SIMD=false>
std::unique_ptr<sfz::AudioBuffer<T>> upsample2x(const sfz::AudioBuffer<T>& buffer)
{
// auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 2);
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
upsample2xStage<SIMD>(buffer.getConstSpan(channelIdx), outputBuffer->getSpan(channelIdx));
}
return outputBuffer;
}
template <class T, bool SIMD=false>
std::unique_ptr<sfz::AudioBuffer<T>> upsample4x(const sfz::AudioBuffer<T>& buffer)
{
auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 4);
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
upsample2xStage<SIMD>(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer));
upsample4xStage<SIMD>(absl::MakeConstSpan(*tempBuffer), outputBuffer->getSpan(channelIdx));
}
return outputBuffer;
}
template <class T, bool SIMD=false>
std::unique_ptr<sfz::AudioBuffer<T>> upsample8x(const sfz::AudioBuffer<T>& buffer)
{
auto tempBuffer2x = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
auto tempBuffer4x = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 4);
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 8);
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
upsample2xStage<SIMD>(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer2x));
upsample4xStage<SIMD>(absl::MakeConstSpan(*tempBuffer2x), absl::MakeSpan(*tempBuffer4x));
upsample8xStage<SIMD>(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<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = sfz::upsample2x<float, false>(*baseBuffer);
auto outBuffer = upsample2x<float, false>(*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<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = sfz::upsample4x<float, false>(*baseBuffer);
auto outBuffer = upsample4x<float, false>(*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<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = sfz::upsample8x<float, false>(*baseBuffer);
auto outBuffer = upsample8x<float, false>(*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<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = sfz::upsample2x<float, true>(*baseBuffer);
auto outBuffer = upsample2x<float, true>(*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<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = sfz::upsample4x<float, true>(*baseBuffer);
auto outBuffer = upsample4x<float, true>(*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<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = sfz::upsample8x<float, true>(*baseBuffer);
auto outBuffer = upsample8x<float, true>(*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<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = sfz::upsample8x<float>(*baseBuffer);
auto outBuffer = upsample8x<float>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
}

View file

@ -30,6 +30,54 @@
#include <memory>
#include <iostream>
constexpr std::array<double, 12> 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<double, 4> coeffsStage4x {
0.042448989488488006,
0.17072114107630679,
0.39329183835224008,
0.74569514831986694
};
constexpr std::array<double, 3> coeffsStage8x {
0.055748680811302048,
0.24305119574153092,
0.6466991311926823
};
#if defined(__x86_64__) || defined(__i386__)
#include "hiir/Upsampler2xSse.h"
using Upsampler2x = hiir::Upsampler2xSse<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xSse<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xSse<coeffsStage8x.size()>;
#elif defined(__arm__) || defined(__aarch64__)
#include "hiir/Upsampler2xNeon.h"
using Upsampler2x = hiir::Upsampler2xNeon<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xNeon<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xNeon<coeffsStage8x.size()>;
#else
#include "hiir/Upsampler2xFpu.h"
using Upsampler2x = hiir::Upsampler2xFpu<coeffsStage2x.size()>;
using Upsampler4x = hiir::Upsampler2xFpu<coeffsStage4x.size()>;
using Upsampler8x = hiir::Upsampler2xFpu<coeffsStage8x.size()>;
#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<float> buffer { numFrames * sndfile.channels() };
sfz::Buffer<float> temp { numFrames * 4 };
Upsampler2x upsampler2x;
Upsampler4x upsampler4x;
upsampler2x.set_coefs(coeffsStage2x.data());
upsampler4x.set_coefs(coeffsStage4x.data());
sndfile.readf(buffer.data(), numFrames);
sfz::readInterleaved<float>(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<float>(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;