From 6904e1d531e3305d8be848b9ef314f97a7b71a73 Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Sun, 4 Feb 2024 13:29:05 +0100 Subject: [PATCH] chore(ricepp): add benchmark --- ricepp/CMakeLists.txt | 8 ++ ricepp/ricepp_benchmark.cpp | 145 ++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 ricepp/ricepp_benchmark.cpp diff --git a/ricepp/CMakeLists.txt b/ricepp/CMakeLists.txt index b7e4b859..a3b8a265 100644 --- a/ricepp/CMakeLists.txt +++ b/ricepp/CMakeLists.txt @@ -41,6 +41,14 @@ target_compile_features(ricepp PUBLIC cxx_std_20) # add_executable(ricepp_demo ricepp_demo.cpp) # target_link_libraries(ricepp_demo PRIVATE ricepp fmt) +if(WITH_BENCHMARKS) + pkg_check_modules(BENCHMARK IMPORTED_TARGET benchmark) + if(BENCHMARK_FOUND) + add_executable(ricepp_benchmark ricepp_benchmark.cpp) + target_link_libraries(ricepp_benchmark ricepp PkgConfig::BENCHMARK) + endif() +endif() + if(WITH_TESTS) if(NOT TARGET gtest) FetchContent_Declare( diff --git a/ricepp/ricepp_benchmark.cpp b/ricepp/ricepp_benchmark.cpp new file mode 100644 index 00000000..42a24c42 --- /dev/null +++ b/ricepp/ricepp_benchmark.cpp @@ -0,0 +1,145 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of ricepp. + * + * ricepp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ricepp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ricepp. If not, see . + */ + +#include +#include + +#include + +#include +#include + +namespace { + +struct random_config { + std::endian byteorder{std::endian::big}; + unsigned unused_lsb{0}; + unsigned noise_bits{0}; + unsigned full_bits{0}; + double full_freq{0.0}; +}; + +template +std::vector generate_random_data(std::mt19937_64& rng, size_t count, + random_config const& cfg) { + std::exponential_distribution full_freq_dist(cfg.full_freq); + std::uniform_int_distribution noise( + 0, (UINT64_C(1) << (cfg.noise_bits + cfg.unused_lsb)) - 1); + std::uniform_int_distribution full( + 0, (UINT64_C(1) << (cfg.full_bits + cfg.unused_lsb)) - 1); + std::vector data(count); + ValueType mask = static_cast(std::numeric_limits::max() + << cfg.unused_lsb); + std::generate(data.begin(), data.end(), [&]() { + return ricepp::byteswap( + (full_freq_dist(rng) <= 1.0 ? full(rng) : noise(rng)) & mask, + cfg.byteorder); + }); + return data; +} + +class ricepp_bm : public ::benchmark::Fixture { + public: + void SetUp(::benchmark::State const& state) { + std::mt19937_64 rng(42); + + data_ = generate_random_data( + rng, state.range(0), + { + .byteorder = + state.range(1) ? std::endian::big : std::endian::little, + .unused_lsb = static_cast(state.range(2)), + .noise_bits = static_cast(state.range(3)), + .full_bits = static_cast(state.range(4)), + .full_freq = 1.0 / state.range(5), + }); + + codec_ = ricepp::create_codec({ + .block_size = static_cast(state.range(6)), + .component_stream_count = static_cast(state.range(7)), + .byteorder = state.range(1) ? std::endian::big : std::endian::little, + .unused_lsb_count = static_cast(state.range(2)), + }); + + encoded_ = codec_->encode(data_); + } + + void TearDown(::benchmark::State const&) {} + + std::unique_ptr> codec_; + std::vector data_; + std::vector encoded_; +}; + +void ricepp_params(benchmark::internal::Benchmark* b) { + b->ArgNames({"size", "bo", "ulsb", "noise", "full", "freq", "bs", "cs"}); + for (int64_t size : {1024 * 1024, 8 * 1024 * 1024}) { + for (int bo : {0, 1}) { + for (int cs : {1, 2}) { + b->Args({size, bo, 0, 6, 16, 10, 128, cs}); + } + } + } + + for (int64_t bs : {16, 32, 64, 128, 256, 512}) { + b->Args({1024 * 1024, 1, 0, 6, 16, 10, bs, 1}); + } + + for (int64_t full_freq : {2, 4, 8, 16, 32}) { + b->Args({1024 * 1024, 1, 0, 6, 16, full_freq, 128, 1}); + } + + // b->Args({1024*1024, 1, 0, 6, 16, 10, 64, 1}); + // b->Args({1024*1024, 1, 4, 6, 12, 10, 64, 1}); +} + +} // namespace + +BENCHMARK_DEFINE_F(ricepp_bm, encode)(::benchmark::State& state) { + std::vector encoded; + encoded.resize(codec_->worst_case_encoded_bytes(data_)); + for (auto _ : state) { + auto r = codec_->encode(encoded, data_); + ::benchmark::DoNotOptimize(r); + } + state.SetBytesProcessed(static_cast(state.iterations()) * + data_.size() * sizeof(data_[0])); +} + +BENCHMARK_DEFINE_F(ricepp_bm, decode)(::benchmark::State& state) { + std::vector decoded; + decoded.resize(data_.size()); + for (auto _ : state) { + codec_->decode(decoded, encoded_); + } + state.SetBytesProcessed(static_cast(state.iterations()) * + data_.size() * sizeof(data_[0])); +} + +BENCHMARK_REGISTER_F(ricepp_bm, encode) + ->Unit(benchmark::kMillisecond) + ->Apply(ricepp_params); + +BENCHMARK_REGISTER_F(ricepp_bm, decode) + ->Unit(benchmark::kMillisecond) + ->Apply(ricepp_params); + +BENCHMARK_MAIN();