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();