diff --git a/benchmarks/BM_readChunk.cpp b/benchmarks/BM_readChunk.cpp new file mode 100644 index 00000000..a1759a0a --- /dev/null +++ b/benchmarks/BM_readChunk.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2019, Paul Ferrand +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include +#include +#include "ghc/filesystem.hpp" +#include "Buffer.h" +#include "SIMDHelpers.h" +#define DR_WAV_IMPLEMENTATION +#include "dr_wav.h" +#include "AudioBuffer.h" +#include +#ifndef NDEBUG +#include +#endif + +class FileFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& /* state */) { + rootPath = getPath() / "sample1.wav"; + if (!ghc::filesystem::exists(rootPath)) { + #ifndef NDEBUG + std::cerr << "Can't find path" << '\n'; + #endif + std::terminate(); + } + + sndfile = SndfileHandle(rootPath.c_str()); + numFrames = static_cast(sndfile.frames()); + output = std::make_unique>(sndfile.channels(), sndfile.frames()); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + } + + ghc::filesystem::path getPath() + { + #ifdef __linux__ + char buf[PATH_MAX + 1]; + if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) == -1) + return {}; + std::string str { buf }; + return str.substr(0, str.rfind('/')); + #elif _WIN32 + return ghc::filesystem::current_path(); + #endif + } + + std::unique_ptr> output; + SndfileHandle sndfile; + ghc::filesystem::path rootPath; + size_t numFrames; +}; + + +BENCHMARK_DEFINE_F(FileFixture, JustRead)(benchmark::State& state) { + for (auto _ : state) + { + sfz::Buffer buffer { numFrames * sndfile.channels() }; + sndfile.readf(buffer.data(), numFrames); + sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1)); + } +} + +BENCHMARK_DEFINE_F(FileFixture, AllocInside)(benchmark::State& state) { + for (auto _ : state) + { + auto chunkSize = static_cast(state.range(0)); + size_t framesRead = 0; + sndfile.seek(0, SEEK_SET); + while(framesRead < numFrames) + { + sfz::Buffer buffer { chunkSize * sndfile.channels() }; + auto read = sndfile.readf(buffer.data(), chunkSize); + sfz::readInterleaved( + absl::MakeSpan(buffer).first(read), + output->getSpan(0).subspan(framesRead), + output->getSpan(1).subspan(framesRead) + ); + framesRead += read; + } + } +} + +BENCHMARK_DEFINE_F(FileFixture, AllocOutside)(benchmark::State& state) { + auto chunkSize = static_cast(state.range(0)); + sfz::Buffer buffer { chunkSize * sndfile.channels() }; + for (auto _ : state) + { + size_t framesRead = 0; + sndfile.seek(0, SEEK_SET); + while(framesRead < numFrames) + { + auto read = sndfile.readf(buffer.data(), chunkSize); + sfz::readInterleaved( + absl::MakeSpan(buffer).first(read), + output->getSpan(0).subspan(framesRead), + output->getSpan(1).subspan(framesRead) + ); + framesRead += read; + } + } +} + + +BENCHMARK_DEFINE_F(FileFixture, DrWavChunked)(benchmark::State& state) { + auto chunkSize = static_cast(state.range(0)); + drwav wav; + if (!drwav_init_file(&wav, rootPath.c_str(), nullptr)) { + #ifndef NDEBUG + std::cerr << "Can't find path" << '\n'; + #endif + std::terminate(); + } + sfz::Buffer buffer { chunkSize * wav.channels }; + + for (auto _ : state) + { + size_t framesRead = 0; + drwav_seek_to_first_pcm_frame(&wav); + while(framesRead < numFrames) + { + auto read = drwav_read_pcm_frames_f32(&wav, chunkSize, buffer.data()); + sfz::readInterleaved( + absl::MakeSpan(buffer).first(read), + output->getSpan(0).subspan(framesRead), + output->getSpan(1).subspan(framesRead) + ); + framesRead += read; + } + } +} + +BENCHMARK_REGISTER_F(FileFixture, JustRead); +BENCHMARK_REGISTER_F(FileFixture, AllocInside)->RangeMultiplier(4)->Range((1 << 8), (1 << 16)); +BENCHMARK_REGISTER_F(FileFixture, AllocOutside)->RangeMultiplier(4)->Range((1 << 8), (1 << 16)); +BENCHMARK_REGISTER_F(FileFixture, DrWavChunked)->RangeMultiplier(4)->Range((1 << 8), (1 << 16)); +BENCHMARK_MAIN(); diff --git a/benchmarks/BM_resample.cpp b/benchmarks/BM_resample.cpp index 97cc0df9..c10a7164 100644 --- a/benchmarks/BM_resample.cpp +++ b/benchmarks/BM_resample.cpp @@ -35,7 +35,7 @@ public: void SetUp(const ::benchmark::State& state) { - const auto rootPath = getPath() / "ride.wav"; + const auto rootPath = getPath() / "sample1.wav"; if (!ghc::filesystem::exists(rootPath)) { #ifndef NDEBUG std::cerr << "Can't find path" << '\n'; @@ -286,6 +286,15 @@ BENCHMARK_DEFINE_F(SndFile, SRC8x_FASTEST)(benchmark::State& state) } } +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); + benchmark::DoNotOptimize(outBuffer); + } +} BENCHMARK_REGISTER_F(SndFile, HIIR2X_scalar); BENCHMARK_REGISTER_F(SndFile, HIIR4X_scalar); @@ -302,4 +311,5 @@ BENCHMARK_REGISTER_F(SndFile, SRC2x_FASTEST); BENCHMARK_REGISTER_F(SndFile, SRC8x_MEDIUM); BENCHMARK_REGISTER_F(SndFile, SRC4x_FASTEST); BENCHMARK_REGISTER_F(SndFile, SRC8x_FASTEST); +BENCHMARK_REGISTER_F(SndFile, HIIR8X_default); BENCHMARK_MAIN(); diff --git a/benchmarks/BM_resampleChunk.cpp b/benchmarks/BM_resampleChunk.cpp new file mode 100644 index 00000000..10dcd75f --- /dev/null +++ b/benchmarks/BM_resampleChunk.cpp @@ -0,0 +1,140 @@ +// Copyright (c) 2019, Paul Ferrand +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include +#include +#include "ghc/filesystem.hpp" +#include "Buffer.h" +#include "SIMDHelpers.h" +#include "Oversampler.h" +#include "AudioBuffer.h" +#include +#include + +class FileFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& /* state */) { + rootPath = getPath() / "sample1.flac"; + if (!ghc::filesystem::exists(rootPath)) { + #ifndef NDEBUG + std::cerr << "Can't find path" << '\n'; + #endif + std::terminate(); + } + + sndfile = SndfileHandle(rootPath.c_str()); + numFrames = static_cast(sndfile.frames()); + output = std::make_unique>(sndfile.channels(), numFrames * 4); + } + + void TearDown(const ::benchmark::State& state [[maybe_unused]]) { + } + + ghc::filesystem::path getPath() + { + #ifdef __linux__ + char buf[PATH_MAX + 1]; + if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) == -1) + return {}; + std::string str { buf }; + return str.substr(0, str.rfind('/')); + #elif _WIN32 + return ghc::filesystem::current_path(); + #endif + } + + std::unique_ptr> output; + SndfileHandle sndfile; + ghc::filesystem::path rootPath; + size_t numFrames { 0 }; +}; + +BENCHMARK_DEFINE_F(FileFixture, NoResampling)(benchmark::State& state) { + for (auto _ : state) + { + sfz::Buffer buffer { numFrames * sndfile.channels() }; + sndfile.readf(buffer.data(), sndfile.frames()); + sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1)); + } +} + +BENCHMARK_DEFINE_F(FileFixture, ResampleAtOnce)(benchmark::State& state) { + for (auto _ : state) + { + sfz::Buffer buffer { numFrames * sndfile.channels() }; + sfz::Buffer temp { numFrames * 4 }; + 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)); + + sfz::upsample2xStage(output->getConstSpan(1).first(numFrames), absl::MakeSpan(temp)); + sfz::upsample4xStage(absl::MakeConstSpan(temp).first(numFrames * 2), output->getSpan(1)); + } +} + +BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) { + for (auto _ : state) + { + auto chunkSize = static_cast(state.range(0)); + sfz::Buffer buffer { numFrames * sndfile.channels() }; + + sfz::Buffer leftInput { chunkSize }; + sfz::Buffer rightInput { chunkSize }; + sfz::Buffer chunk { chunkSize * 2 }; + + sndfile.readf(buffer.data(), numFrames); + + auto bufferSpan = absl::MakeSpan(buffer); + auto leftSpan = absl::MakeSpan(leftInput); + auto rightSpan = absl::MakeSpan(rightInput); + auto chunkSpan = absl::MakeSpan(chunk); + + size_t inputFrameCounter { 0 }; + size_t outputFrameCounter { 0 }; + while(inputFrameCounter < numFrames) + { + // std::cout << "Input frames: " << inputFrameCounter << "/" << numFrames << '\n'; + const auto thisChunkSize = std::min(chunkSize, numFrames - inputFrameCounter); + const auto bufferChunk = bufferSpan.subspan( + inputFrameCounter * sndfile.channels(), + thisChunkSize * sndfile.channels() + ); + + 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)); + + inputFrameCounter += chunkSize; + outputFrameCounter += chunkSize * 4; + } + } +} + +BENCHMARK_REGISTER_F(FileFixture, NoResampling); +BENCHMARK_REGISTER_F(FileFixture, ResampleAtOnce); +BENCHMARK_REGISTER_F(FileFixture, ResampleInChunks)->RangeMultiplier(4)->Range((1 << 4), (1 << 16)); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 8997d843..cfadf051 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -125,6 +125,14 @@ add_executable(bm_flacfile BM_flacfile.cpp ${BENCHMARK_SIMD_SOURCES}) target_link_libraries(bm_flacfile benchmark absl::span absl::algorithm sndfile) target_include_directories(bm_flacfile PRIVATE ../src/sfizz ../src/external) +add_executable(bm_readChunk BM_readChunk.cpp ${BENCHMARK_SIMD_SOURCES}) +target_link_libraries(bm_readChunk benchmark absl::span absl::algorithm sndfile) +target_include_directories(bm_readChunk PRIVATE ../src/sfizz ../src/external) + +add_executable(bm_resampleChunk BM_resampleChunk.cpp ${BENCHMARK_SIMD_SOURCES}) +target_link_libraries(bm_resampleChunk benchmark absl::span absl::algorithm sndfile) +target_include_directories(bm_resampleChunk PRIVATE ../src/sfizz ../src/external) + add_custom_target(sfizz_benchmarks) add_dependencies(sfizz_benchmarks bm_opf_high_vs_low @@ -148,7 +156,9 @@ add_dependencies(sfizz_benchmarks bm_pan bm_subtract bm_multiplyAdd + bm_readChunk bm_resample + bm_resampleChunk bm_envelopes bm_wavfile bm_flacfile