From 320fc2cdaa3e54a3de4dee5f82e643ec1ff892a3 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 26 Jul 2020 00:48:43 +0200 Subject: [PATCH] Add a benchmark --- benchmarks/BM_audioReaders.cpp | 229 +++++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 3 + src/sfizz/AudioReader.cpp | 46 +++++-- src/sfizz/AudioReader.h | 11 ++ 4 files changed, 276 insertions(+), 13 deletions(-) create mode 100644 benchmarks/BM_audioReaders.cpp diff --git a/benchmarks/BM_audioReaders.cpp b/benchmarks/BM_audioReaders.cpp new file mode 100644 index 00000000..874ff8d2 --- /dev/null +++ b/benchmarks/BM_audioReaders.cpp @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "AudioReader.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#else +#include +#endif + +/// +struct AutoFD { + AutoFD() {} + ~AutoFD() { reset(); } + + AutoFD(const AutoFD&) = delete; + AutoFD &operator=(const AutoFD&) = delete; + + AutoFD(AutoFD&& other) : fd_(other.fd_) { other.fd_ = -1; } + AutoFD &operator=(AutoFD&& other) + { + if (this == &other) return *this; + reset(other.fd_); + other.fd_ = -1; + return *this; + } + + explicit operator bool() const noexcept { return fd_ != -1; } + int get() const noexcept { return fd_; } + + int release() + { + int fd = fd_; + fd_ = -1; + return fd; + } + + void reset(int fd = -1) noexcept + { + if (fd_ == fd) return; + if (fd_ != -1) close(fd_); + fd_ = fd; + } + +private: + int fd_ = -1; +}; + +/// +class AudioReaderFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) override + { + workBuffer.resize(2 * static_cast(state.range(0))); + } + + void TearDown(const ::benchmark::State& /* state */) override + { + } + + static AutoFD createAudioFile(int format); + + static AutoFD fileWav; + static AutoFD fileFlac; + static AutoFD fileOgg; + + std::vector workBuffer; +}; + +AutoFD AudioReaderFixture::fileWav = createAudioFile(SF_FORMAT_WAV|SF_FORMAT_PCM_16); +AutoFD AudioReaderFixture::fileFlac = createAudioFile(SF_FORMAT_FLAC|SF_FORMAT_PCM_16); +AutoFD AudioReaderFixture::fileOgg = createAudioFile(SF_FORMAT_OGG|SF_FORMAT_VORBIS); + +AutoFD AudioReaderFixture::createAudioFile(int format) +{ + constexpr unsigned sampleRate = 44100; + constexpr unsigned fileDuration = 10; + constexpr unsigned fileFrames = sampleRate * fileDuration; + + // synth 2 channels of arbitrary waveform + std::unique_ptr sndData { new double[2 * fileFrames] }; + double phase = 0.0; + for (unsigned i = 0; i < fileFrames; ++i) { + sndData[2 * i ] = std::sin(2.0 * M_PI * phase); + sndData[2 * i + 1] = std::cos(2.0 * M_PI * phase); + phase += 440.0 * (1.0 / sampleRate); + phase -= static_cast(phase); + } + + // create anonymous temp file + FILE* file = tmpfile(); + if (!file) + throw std::system_error(errno, std::generic_category()); + + // convert FILE to fd, for sndfile + AutoFD fd; + fd.reset(dup(fileno(file))); + if (!fd) { + fclose(file); + throw std::system_error(errno, std::generic_category()); + } + fclose(file); + + // write to fd + SndfileHandle snd(fd.get(), false, SFM_WRITE, format, 2, sampleRate); + if (snd.error()) + throw std::runtime_error("cannot open sound file for writing"); + snd.writef(sndData.get(), fileFrames); + snd = SndfileHandle(); + + return fd; +} + +static void rewindFd(int fd) +{ +#ifndef _WIN32 + off_t off = lseek(fd, 0, SEEK_SET); +#else + off_t off = _lseek(fd, 0, SEEK_SET); +#endif + if (off == -1) + throw std::system_error(errno, std::generic_category()); +} + +static void doReaderBenchmark(int fd, std::vector &buffer, sfz::AudioReaderType type) +{ + rewindFd(fd); + sfz::AudioReaderPtr reader = sfz::createExplicitAudioReaderWithFd(fd, type); + while (reader->readNextBlock(buffer.data(), buffer.size() / 2) > 0); +} + +static void doEntireRead(int fd) +{ + rewindFd(fd); + + SndfileHandle handle(fd, false); + if (handle.error()) + throw std::runtime_error("cannot open sound file for reading"); + + std::vector buffer(static_cast(2 * handle.frames())); + handle.read(buffer.data(), buffer.size()); +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, EntireWav)(benchmark::State& state) +{ + for (auto _ : state) { + doEntireRead(fileWav.get()); + } +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardWav)(benchmark::State& state) +{ + for (auto _ : state) { + doReaderBenchmark(fileWav.get(), workBuffer, sfz::AudioReaderType::Forward); + } +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseWav)(benchmark::State& state) +{ + for (auto _ : state) { + doReaderBenchmark(fileWav.get(), workBuffer, sfz::AudioReaderType::Reverse); + } +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, EntireFlac)(benchmark::State& state) +{ + for (auto _ : state) { + doEntireRead(fileFlac.get()); + } +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardFlac)(benchmark::State& state) +{ + for (auto _ : state) { + doReaderBenchmark(fileFlac.get(), workBuffer, sfz::AudioReaderType::Forward); + } +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseFlac)(benchmark::State& state) +{ + for (auto _ : state) { + doReaderBenchmark(fileFlac.get(), workBuffer, sfz::AudioReaderType::Reverse); + } +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, EntireOgg)(benchmark::State& state) +{ + for (auto _ : state) { + doEntireRead(fileOgg.get()); + } +} + +BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardOgg)(benchmark::State& state) +{ + for (auto _ : state) { + doReaderBenchmark(fileOgg.get(), workBuffer, sfz::AudioReaderType::Forward); + } +} + +//BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseOgg)(benchmark::State& state) +//{ +// for (auto _ : state) { +// doReaderBenchmark(fileOgg.get(), workBuffer, sfz::AudioReaderType::Reverse); +// } +//} + +BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +BENCHMARK_REGISTER_F(AudioReaderFixture, EntireWav)->Range(1, 1); +BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardFlac)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseFlac)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +BENCHMARK_REGISTER_F(AudioReaderFixture, EntireFlac)->Range(1, 1); +BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +//BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +BENCHMARK_REGISTER_F(AudioReaderFixture, EntireOgg)->Range(1, 1); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 3c0082a1..cb38feb6 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -78,6 +78,9 @@ target_link_libraries(bm_wavfile PRIVATE sfizz-sndfile) sfizz_add_benchmark(bm_flacfile BM_flacfile.cpp) target_link_libraries(bm_flacfile PRIVATE sfizz-sndfile) +sfizz_add_benchmark(bm_audioReaders BM_audioReaders.cpp ../src/sfizz/AudioReader.cpp) +target_link_libraries(bm_audioReaders PRIVATE sfizz-sndfile) + sfizz_add_benchmark(bm_readChunk BM_readChunk.cpp) target_link_libraries(bm_readChunk PRIVATE sfizz-sndfile) sfizz_add_benchmark(bm_readChunkFlac BM_readChunkFlac.cpp) diff --git a/src/sfizz/AudioReader.cpp b/src/sfizz/AudioReader.cpp index bd397bc1..9122ee76 100644 --- a/src/sfizz/AudioReader.cpp +++ b/src/sfizz/AudioReader.cpp @@ -301,19 +301,13 @@ static bool formatHasFastSeeking(int format) return fast; } -AudioReaderPtr createAudioReader(const fs::path& path, bool reverse, std::error_code* ec) +static AudioReaderPtr createAudioReaderWithHandle(SndfileHandle handle, bool reverse, std::error_code* ec) { AudioReaderPtr reader; if (ec) ec->clear(); -#if defined(_WIN32) - SndfileHandle handle(path.wstring().c_str()); -#else - SndfileHandle handle(path.c_str()); -#endif - if (!handle) { if (ec) *ec = std::error_code(handle.error(), sndfile_category()); @@ -329,18 +323,28 @@ AudioReaderPtr createAudioReader(const fs::path& path, bool reverse, std::error_ return reader; } -AudioReaderPtr createExplicitAudioReader(const fs::path& path, AudioReaderType type, std::error_code* ec) +AudioReaderPtr createAudioReader(const fs::path& path, bool reverse, std::error_code* ec) { - AudioReaderPtr reader; - - if (ec) - ec->clear(); - #if defined(_WIN32) SndfileHandle handle(path.wstring().c_str()); #else SndfileHandle handle(path.c_str()); #endif + return createAudioReaderWithHandle(handle, reverse, ec); +} + +AudioReaderPtr createAudioReaderWithFd(int fd, bool reverse, std::error_code* ec) +{ + SndfileHandle handle(fd, false); + return createAudioReaderWithHandle(handle, reverse, ec); +} + +static AudioReaderPtr createExplicitAudioReaderWithHandle(SndfileHandle handle, AudioReaderType type, std::error_code* ec) +{ + AudioReaderPtr reader; + + if (ec) + ec->clear(); if (!handle) { if (ec) @@ -364,4 +368,20 @@ AudioReaderPtr createExplicitAudioReader(const fs::path& path, AudioReaderType t return reader; } +AudioReaderPtr createExplicitAudioReader(const fs::path& path, AudioReaderType type, std::error_code* ec) +{ +#if defined(_WIN32) + SndfileHandle handle(path.wstring().c_str()); +#else + SndfileHandle handle(path.c_str()); +#endif + return createExplicitAudioReaderWithHandle(handle, type, ec); +} + +AudioReaderPtr createExplicitAudioReaderWithFd(int fd, AudioReaderType type, std::error_code* ec) +{ + SndfileHandle handle(fd, false); + return createExplicitAudioReaderWithHandle(handle, type, ec); +} + } // namespace sfz diff --git a/src/sfizz/AudioReader.h b/src/sfizz/AudioReader.h index 653abce3..65a199a3 100644 --- a/src/sfizz/AudioReader.h +++ b/src/sfizz/AudioReader.h @@ -9,6 +9,7 @@ #include "ghc/fs_std.hpp" #include #include +#include #if defined(_WIN32) #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 #include @@ -51,9 +52,19 @@ typedef std::unique_ptr AudioReaderPtr; */ AudioReaderPtr createAudioReader(const fs::path& path, bool reverse, std::error_code* ec = nullptr); +/** + * @brief Create a file reader of detected type. + */ +AudioReaderPtr createAudioReaderWithFd(int fd, bool reverse, std::error_code* ec = nullptr); + /** * @brief Create a file reader of explicit type. (for testing purposes) */ AudioReaderPtr createExplicitAudioReader(const fs::path& path, AudioReaderType type, std::error_code* ec = nullptr); +/** + * @brief Create a file reader of explicit type. (for testing purposes) + */ +AudioReaderPtr createExplicitAudioReaderWithFd(int fd, AudioReaderType type, std::error_code* ec = nullptr); + } // namespace sfz