diff --git a/ricepp/CMakeLists.txt b/ricepp/CMakeLists.txt
index d89560cb..0b3729b1 100644
--- a/ricepp/CMakeLists.txt
+++ b/ricepp/CMakeLists.txt
@@ -150,6 +150,9 @@ if(WITH_BENCHMARKS)
if(benchmark_FOUND)
add_executable(ricepp_benchmark ricepp_benchmark.cpp)
target_link_libraries(ricepp_benchmark ricepp benchmark::benchmark)
+
+ add_executable(ricepp_benchmark_fits ricepp_benchmark_fits.cpp)
+ target_link_libraries(ricepp_benchmark_fits ricepp benchmark::benchmark)
endif()
endif()
diff --git a/ricepp/ricepp_benchmark_fits.cpp b/ricepp/ricepp_benchmark_fits.cpp
new file mode 100644
index 00000000..d3c02b3a
--- /dev/null
+++ b/ricepp/ricepp_benchmark_fits.cpp
@@ -0,0 +1,193 @@
+/* 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
+#include
+#include
+
+#include
+
+#include
+#include
+
+namespace {
+
+std::filesystem::path const g_testdata_dir{
+ "/home/mhx/git/github/dwarfs/@ricepp-testdata"};
+
+struct config {
+ unsigned component_stream_count;
+ unsigned unused_lsb_count;
+};
+
+std::map const g_camera_info = {
+ {"ASI178MC", {2, 0}}, {"ASI294MC", {2, 2}}, {"ASI1600MM", {1, 4}},
+ {"ASI2600MC", {2, 0}}, {"ASI2600MM", {1, 0}}, {"ASI6200MC", {2, 0}},
+};
+
+std::string format_percentage(double value) {
+ std::ostringstream oss;
+ oss << std::fixed << std::setprecision(2) << "(" << value << "%)";
+ return oss.str();
+}
+
+class ricepp_bm : public ::benchmark::Fixture {
+ public:
+ void SetUp(::benchmark::State const& state) {
+ if (state.thread_index() > 0) {
+ latch_->wait();
+ return;
+ }
+
+ auto const [camera, test, operation] = [&] {
+ std::string name{state.name()};
+ auto const pos1 = name.find('/');
+ auto const pos2 = name.find('/', pos1 + 1);
+ return std::make_tuple(name.substr(0, pos1),
+ name.substr(pos1 + 1, pos2 - pos1 - 1),
+ name.substr(pos2 + 1));
+ }();
+
+ auto const& camera_info = g_camera_info.at(camera);
+
+ codec_ = ricepp::create_codec({
+ .block_size = 128,
+ .component_stream_count = camera_info.component_stream_count,
+ .byteorder = std::endian::big,
+ .unused_lsb_count = camera_info.unused_lsb_count,
+ });
+
+ if (data_.empty()) {
+ std::filesystem::path testdata_dir;
+ if (auto dir = std::getenv("RICEPP_FITS_TESTDATA_DIR")) {
+ testdata_dir = dir;
+ } else {
+ testdata_dir = g_testdata_dir;
+ }
+
+ data_ = load_fits_data(testdata_dir / camera / (test + ".fit"));
+
+ encoded_ = codec_->encode(data_);
+ }
+
+ latch_->count_down();
+ }
+
+ void TearDown(::benchmark::State const& state) {
+ latch_.reset();
+ latch_.emplace(1);
+ }
+
+ std::vector load_fits_data(std::filesystem::path const& path) {
+ static constexpr size_t kBlockSize = 2880;
+ static constexpr size_t kDataSize = 8 * 1024 * 1024;
+
+ std::ifstream ifs{path, std::ios::binary};
+ if (!ifs) {
+ throw std::runtime_error{"failed to open file: " + path.string()};
+ }
+
+ // skip a bunch of header blocks
+ ifs.seekg(8 * kBlockSize, std::ios::beg);
+
+ std::vector data;
+ data.resize(kDataSize / sizeof(data[0]));
+ ifs.read(reinterpret_cast(data.data()), kDataSize);
+
+ if (!ifs) {
+ throw std::runtime_error{"failed to read data from file: " +
+ path.string()};
+ }
+
+ return data;
+ }
+
+ std::unique_ptr> codec_;
+ std::vector data_;
+ std::vector encoded_;
+ std::optional latch_{1};
+};
+
+} // namespace
+
+#define RICEPP_BENCHMARK(camera, test) \
+ BENCHMARK_DEFINE_F(ricepp_bm, encode_##camera##_##test) \
+ (::benchmark::State & state) { \
+ thread_local 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])); \
+ state.SetLabel(format_percentage(100.0 * encoded_.size() / \
+ (data_.size() * sizeof(data_[0])))); \
+ } \
+ BENCHMARK_DEFINE_F(ricepp_bm, decode_##camera##_##test) \
+ (::benchmark::State & state) { \
+ thread_local 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_##camera##_##test) \
+ ->Unit(benchmark::kMillisecond) \
+ ->ThreadRange(1, 8) \
+ ->UseRealTime() \
+ ->Name(#camera "/" #test "/encode"); \
+ BENCHMARK_REGISTER_F(ricepp_bm, decode_##camera##_##test) \
+ ->Unit(benchmark::kMillisecond) \
+ ->ThreadRange(1, 8) \
+ ->UseRealTime() \
+ ->Name(#camera "/" #test "/decode");
+
+RICEPP_BENCHMARK(ASI1600MM, dark_120s_g139)
+RICEPP_BENCHMARK(ASI1600MM, dark_1s_g0)
+RICEPP_BENCHMARK(ASI1600MM, flat_h_2s_g0)
+RICEPP_BENCHMARK(ASI1600MM, light_g_60s_g0)
+RICEPP_BENCHMARK(ASI1600MM, light_s_120s_g139)
+RICEPP_BENCHMARK(ASI178MC, bias_g0)
+RICEPP_BENCHMARK(ASI178MC, dark_60s_g0)
+RICEPP_BENCHMARK(ASI178MC, flat_g0)
+RICEPP_BENCHMARK(ASI178MC, light_60s_g0)
+RICEPP_BENCHMARK(ASI2600MC, flat_g0)
+RICEPP_BENCHMARK(ASI2600MC, light_180s_g100)
+RICEPP_BENCHMARK(ASI2600MM, bias_g0)
+RICEPP_BENCHMARK(ASI2600MM, bias_g100)
+RICEPP_BENCHMARK(ASI2600MM, bias_g300)
+RICEPP_BENCHMARK(ASI2600MM, dark_900s_g300)
+RICEPP_BENCHMARK(ASI2600MM, flat_h_g0)
+RICEPP_BENCHMARK(ASI2600MM, light_b_30s_g0)
+RICEPP_BENCHMARK(ASI2600MM, light_h_120s_g300)
+RICEPP_BENCHMARK(ASI294MC, light_120s_g200)
+RICEPP_BENCHMARK(ASI294MC, light_180s_g0)
+RICEPP_BENCHMARK(ASI294MC, light_60s_g0)
+RICEPP_BENCHMARK(ASI6200MC, light_60s_g100)
+
+BENCHMARK_MAIN();