From c87c8f70dc550a826b560044e0220eb75710883a Mon Sep 17 00:00:00 2001 From: wopdevries Date: Fri, 4 Sep 2026 09:49:39 +0200 Subject: [PATCH 01/13] instrument: TT lookup/hit counters (hot-path) + env-gated summary --- library/src/ab_search.cpp | 7 +++++++ library/src/solver_if.cpp | 28 ++++++++++++++++++++++++++++ library/src/system/thread_data.hpp | 3 +++ 3 files changed, 38 insertions(+) diff --git a/library/src/ab_search.cpp b/library/src/ab_search.cpp index a055f4bba..27b950dca 100644 --- a/library/src/ab_search.cpp +++ b/library/src/ab_search.cpp @@ -56,6 +56,13 @@ auto apply_ab_tt_lookup( limit, lowerFlag); TIMER_END(TIMER_NO_LOOKUP, depth); + // Instrumentation: per-thread TT lookup/hit counters + if (thrp) { + ++thrp->tt_lookup_count; + if (cardsP) + ++thrp->tt_hit_count; + } + if (!cardsP) return false; diff --git a/library/src/solver_if.cpp b/library/src/solver_if.cpp index f935f004e..edab80554 100644 --- a/library/src/solver_if.cpp +++ b/library/src/solver_if.cpp @@ -670,6 +670,20 @@ auto solve_board_internal( futp->nodes = ctx.search().trick_nodes(); } + // Print TT stats if requested + if (std::getenv("DDS_PRINT_TT_STATS")) { + ThreadData* thrp_ptr = ctx.thread_ptr(); + if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { + double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / + (double)thrp_ptr->tt_lookup_count; + std::fprintf(stderr, + "DDS_TT_STATS: lookups=%llu hits=%llu hit_rate=%.2f%%\n", + (unsigned long long)thrp_ptr->tt_lookup_count, + (unsigned long long)thrp_ptr->tt_hit_count, + hit_rate); + } + } + #ifdef DDS_MEMORY_LEAKS_WIN32 _CrtDumpMemoryLeaks(); #endif @@ -811,6 +825,20 @@ auto solve_same_board( futp->nodes = ctx.search().trick_nodes(); } + // Print TT stats if requested + if (std::getenv("DDS_PRINT_TT_STATS")) { + ThreadData* thrp_ptr = ctx.thread_ptr(); + if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { + double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / + (double)thrp_ptr->tt_lookup_count; + std::fprintf(stderr, + "DDS_TT_STATS: lookups=%llu hits=%llu hit_rate=%.2f%%\n", + (unsigned long long)thrp_ptr->tt_lookup_count, + (unsigned long long)thrp_ptr->tt_hit_count, + hit_rate); + } + } + #ifdef DDS_MEMORY_LEAKS_WIN32 _CrtDumpMemoryLeaks(); #endif diff --git a/library/src/system/thread_data.hpp b/library/src/system/thread_data.hpp index a9b1035d0..d4d2f797a 100644 --- a/library/src/system/thread_data.hpp +++ b/library/src/system/thread_data.hpp @@ -61,6 +61,9 @@ struct ThreadData double memUsed; int nodes; int trickNodes; + // TT instrumentation (per-context, single-threaded) + uint64_t tt_lookup_count = 0; + uint64_t tt_hit_count = 0; // Constant for a given hand. // 960 KB From a1057b10902a4aad3d343c510c607613344b5dcc Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sat, 5 Sep 2026 06:07:52 +0200 Subject: [PATCH 02/13] fix: reset TT counters per-solve; add add/overwrite counters to PageStats --- library/src/solver_if.cpp | 6 ++++++ library/src/trans_table/trans_table_l.cpp | 5 ++++- library/src/trans_table/trans_table_l.hpp | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/library/src/solver_if.cpp b/library/src/solver_if.cpp index edab80554..b1c3fe7cb 100644 --- a/library/src/solver_if.cpp +++ b/library/src/solver_if.cpp @@ -284,6 +284,8 @@ auto solve_board_internal( thrp->ABStats.Reset(); thrp->ABStats.ResetCum(); #endif + thrp->tt_lookup_count = 0; + thrp->tt_hit_count = 0; #ifdef DDS_TOP_LEVEL { @@ -735,6 +737,8 @@ auto solve_same_board( thrp->ABStats.Reset(); thrp->ABStats.ResetCum(); #endif + thrp->tt_lookup_count = 0; + thrp->tt_hit_count = 0; #ifdef DDS_TOP_LEVEL { @@ -933,6 +937,8 @@ auto analyse_later_board( thrp->ABStats.Reset(); thrp->ABStats.ResetCum(); #endif + thrp->tt_lookup_count = 0; + thrp->tt_hit_count = 0; #ifdef DDS_TOP_LEVEL { diff --git a/library/src/trans_table/trans_table_l.cpp b/library/src/trans_table/trans_table_l.cpp index e8dea203c..c5c12404d 100644 --- a/library/src/trans_table/trans_table_l.cpp +++ b/library/src/trans_table/trans_table_l.cpp @@ -209,7 +209,7 @@ TransTableL::TransTableL() pages_maximum_ = 0; harvest_trick_ = 0; harvest_hand_ = 0; - page_stats_ = PageStats{0,0,0,0,0}; + page_stats_ = PageStats{0,0,0,0,0,0,0}; timestamp_ = 0; pool_ = nullptr; next_block_ = nullptr; @@ -931,7 +931,10 @@ auto TransTableL::create_or_update( return; } + // Instrumentation: count new insertions and overwrites + page_stats_.num_adds_++; if (n == BlocksPerEntry) { + page_stats_.num_overwrites_++; if (bp->next_write_no_ >= BlocksPerEntry) bp->next_write_no_ = 0; } diff --git a/library/src/trans_table/trans_table_l.hpp b/library/src/trans_table/trans_table_l.hpp index 34c7a2b8f..625af462a 100644 --- a/library/src/trans_table/trans_table_l.hpp +++ b/library/src/trans_table/trans_table_l.hpp @@ -142,6 +142,8 @@ class TransTableL: public TransTable int num_frees_; ///< Total deallocations int num_harvests_; ///< Total harvest operations int last_current_; ///< Last current page number + int num_adds_; ///< Total new entries inserted + int num_overwrites_; ///< Insertions that overwrote existing entries }; /// \brief Harvested blocks saved for potential reuse (16 bytes). From 7bc384c00be86503872f19cdc7cd063cd6f1352c Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sun, 6 Sep 2026 16:57:52 +0200 Subject: [PATCH 03/13] fix: add includes, reset op_stats per-solve, reset new PageStats fields --- library/src/solver_if.cpp | 23 +++++++++++++++++++++++ library/src/system/thread_data.hpp | 1 + library/src/trans_table/trans_table.hpp | 4 ++++ library/src/trans_table/trans_table_l.cpp | 2 ++ library/src/trans_table/trans_table_l.hpp | 10 ++++++++++ library/src/trans_table/trans_table_s.hpp | 4 ++++ 6 files changed, 44 insertions(+) diff --git a/library/src/solver_if.cpp b/library/src/solver_if.cpp index b1c3fe7cb..b4972b69f 100644 --- a/library/src/solver_if.cpp +++ b/library/src/solver_if.cpp @@ -7,6 +7,8 @@ See LICENSE and README. */ +#include +#include #include #include #include @@ -286,6 +288,7 @@ auto solve_board_internal( #endif thrp->tt_lookup_count = 0; thrp->tt_hit_count = 0; + if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); #ifdef DDS_TOP_LEVEL { @@ -683,6 +686,15 @@ auto solve_board_internal( (unsigned long long)thrp_ptr->tt_lookup_count, (unsigned long long)thrp_ptr->tt_hit_count, hit_rate); + if (auto* tt = ctx.trans_table()) { + int adds, overwrites, harvests; + tt->get_op_stats(adds, overwrites, harvests); + double ow_rate = adds > 0 ? + 100.0 * (double)overwrites / (double)adds : 0.0; + std::fprintf(stderr, + "DDS_TT_STATS: adds=%d overwrites=%d overwrite_rate=%.2f%% harvests=%d\n", + adds, overwrites, ow_rate, harvests); + } } } @@ -739,6 +751,7 @@ auto solve_same_board( #endif thrp->tt_lookup_count = 0; thrp->tt_hit_count = 0; + if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); #ifdef DDS_TOP_LEVEL { @@ -840,6 +853,15 @@ auto solve_same_board( (unsigned long long)thrp_ptr->tt_lookup_count, (unsigned long long)thrp_ptr->tt_hit_count, hit_rate); + if (auto* tt = ctx.trans_table()) { + int adds, overwrites, harvests; + tt->get_op_stats(adds, overwrites, harvests); + double ow_rate = adds > 0 ? + 100.0 * (double)overwrites / (double)adds : 0.0; + std::fprintf(stderr, + "DDS_TT_STATS: adds=%d overwrites=%d overwrite_rate=%.2f%% harvests=%d\n", + adds, overwrites, ow_rate, harvests); + } } } @@ -939,6 +961,7 @@ auto analyse_later_board( #endif thrp->tt_lookup_count = 0; thrp->tt_hit_count = 0; + if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); #ifdef DDS_TOP_LEVEL { diff --git a/library/src/system/thread_data.hpp b/library/src/system/thread_data.hpp index d4d2f797a..909b1b4da 100644 --- a/library/src/system/thread_data.hpp +++ b/library/src/system/thread_data.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef DDS_AB_STATS #include "ab_stats.hpp" diff --git a/library/src/trans_table/trans_table.hpp b/library/src/trans_table/trans_table.hpp index 82f9df323..9db97533f 100644 --- a/library/src/trans_table/trans_table.hpp +++ b/library/src/trans_table/trans_table.hpp @@ -242,6 +242,10 @@ class TransTable virtual auto print_all_suit_stats(std::ofstream& fout) const -> void = 0; /// \brief Print summary suit statistics. + /// \brief Get add/overwrite/harvest counters for instrumentation. + virtual auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void = 0; + virtual auto reset_op_stats() -> void = 0; + virtual auto print_summary_suit_stats(std::ofstream& fout) const -> void = 0; /// \brief Print entries distribution for a specific hand. diff --git a/library/src/trans_table/trans_table_l.cpp b/library/src/trans_table/trans_table_l.cpp index c5c12404d..5e8adf3d9 100644 --- a/library/src/trans_table/trans_table_l.cpp +++ b/library/src/trans_table/trans_table_l.cpp @@ -470,6 +470,8 @@ auto TransTableL::return_all_memory() -> void page_stats_.num_frees_ = 0; page_stats_.num_harvests_ = 0; page_stats_.last_current_ = 0; + page_stats_.num_adds_ = 0; + page_stats_.num_overwrites_ = 0; TransTableL::release_tt(); diff --git a/library/src/trans_table/trans_table_l.hpp b/library/src/trans_table/trans_table_l.hpp index 625af462a..47dac22ad 100644 --- a/library/src/trans_table/trans_table_l.hpp +++ b/library/src/trans_table/trans_table_l.hpp @@ -465,6 +465,16 @@ class TransTableL: public TransTable /// \brief Print summary suit statistics. /// /// \param fout Output stream + auto reset_op_stats() -> void override { + page_stats_.num_adds_ = 0; + page_stats_.num_overwrites_ = 0; + page_stats_.num_harvests_ = 0; + } + auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { + adds = page_stats_.num_adds_; + overwrites = page_stats_.num_overwrites_; + harvests = page_stats_.num_harvests_; + } auto print_summary_suit_stats(std::ofstream& fout) const -> void override; /// \brief Print entries for a specific hand distribution. diff --git a/library/src/trans_table/trans_table_s.hpp b/library/src/trans_table/trans_table_s.hpp index f044018b2..a7d3d746b 100644 --- a/library/src/trans_table/trans_table_s.hpp +++ b/library/src/trans_table/trans_table_s.hpp @@ -299,6 +299,10 @@ class TransTableS: public TransTable auto print_all_suit_stats(std::ofstream& /*fout*/) const -> void override { } + auto reset_op_stats() -> void override {} + auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { + adds = 0; overwrites = 0; harvests = 0; // TransTableS not instrumented + } auto print_summary_suit_stats(std::ofstream& /*fout*/) const -> void override { } From 00277ea5de071f501a6fd6d82be06dfff0c65eee Mon Sep 17 00:00:00 2001 From: wopdevries Date: Mon, 7 Sep 2026 18:03:47 +0200 Subject: [PATCH 04/13] fix: add get_op_stats and reset_op_stats to MockTransTable --- library/tests/trans_table/trans_table_base_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/tests/trans_table/trans_table_base_test.cpp b/library/tests/trans_table/trans_table_base_test.cpp index 8834a91cf..4a671fea2 100644 --- a/library/tests/trans_table/trans_table_base_test.cpp +++ b/library/tests/trans_table/trans_table_base_test.cpp @@ -37,6 +37,8 @@ class TransTableBaseTest : public ::testing::Test MockTransTable() : TransTable() {} ~MockTransTable() override = default; + auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { adds = 0; overwrites = 0; harvests = 0; } + auto reset_op_stats() -> void override {} void init(const int handLookup[][15]) override { From 5c165a7cafe84e9cb2d6beaf605e12384601442b Mon Sep 17 00:00:00 2001 From: wopdevries Date: Wed, 9 Sep 2026 00:35:44 +0200 Subject: [PATCH 05/13] fix: Allman style, strict env var check, doxygen for reset_op_stats --- library/src/solver_if.cpp | 4 ++-- library/src/trans_table/trans_table.hpp | 2 ++ library/src/trans_table/trans_table_l.hpp | 6 ++++-- library/src/trans_table/trans_table_s.hpp | 11 ++++++++--- library/tests/trans_table/trans_table_base_test.cpp | 11 +++++++++-- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/library/src/solver_if.cpp b/library/src/solver_if.cpp index b4972b69f..c9702f191 100644 --- a/library/src/solver_if.cpp +++ b/library/src/solver_if.cpp @@ -676,7 +676,7 @@ auto solve_board_internal( } // Print TT stats if requested - if (std::getenv("DDS_PRINT_TT_STATS")) { + if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && std::string(env) == "1") { ThreadData* thrp_ptr = ctx.thread_ptr(); if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / @@ -843,7 +843,7 @@ auto solve_same_board( } // Print TT stats if requested - if (std::getenv("DDS_PRINT_TT_STATS")) { + if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && std::string(env) == "1") { ThreadData* thrp_ptr = ctx.thread_ptr(); if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / diff --git a/library/src/trans_table/trans_table.hpp b/library/src/trans_table/trans_table.hpp index 9db97533f..975e1d181 100644 --- a/library/src/trans_table/trans_table.hpp +++ b/library/src/trans_table/trans_table.hpp @@ -244,6 +244,8 @@ class TransTable /// \brief Print summary suit statistics. /// \brief Get add/overwrite/harvest counters for instrumentation. virtual auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void = 0; + + /// \brief Reset add/overwrite/harvest counters for per-solve stats. virtual auto reset_op_stats() -> void = 0; virtual auto print_summary_suit_stats(std::ofstream& fout) const -> void = 0; diff --git a/library/src/trans_table/trans_table_l.hpp b/library/src/trans_table/trans_table_l.hpp index 47dac22ad..ad6e981b3 100644 --- a/library/src/trans_table/trans_table_l.hpp +++ b/library/src/trans_table/trans_table_l.hpp @@ -465,12 +465,14 @@ class TransTableL: public TransTable /// \brief Print summary suit statistics. /// /// \param fout Output stream - auto reset_op_stats() -> void override { + auto reset_op_stats() -> void override + { page_stats_.num_adds_ = 0; page_stats_.num_overwrites_ = 0; page_stats_.num_harvests_ = 0; } - auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { + auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override + { adds = page_stats_.num_adds_; overwrites = page_stats_.num_overwrites_; harvests = page_stats_.num_harvests_; diff --git a/library/src/trans_table/trans_table_s.hpp b/library/src/trans_table/trans_table_s.hpp index a7d3d746b..6560c225f 100644 --- a/library/src/trans_table/trans_table_s.hpp +++ b/library/src/trans_table/trans_table_s.hpp @@ -299,9 +299,14 @@ class TransTableS: public TransTable auto print_all_suit_stats(std::ofstream& /*fout*/) const -> void override { } - auto reset_op_stats() -> void override {} - auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { - adds = 0; overwrites = 0; harvests = 0; // TransTableS not instrumented + auto reset_op_stats() -> void override + { + } + auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override + { + adds = 0; + overwrites = 0; + harvests = 0; // TransTableS not instrumented } auto print_summary_suit_stats(std::ofstream& /*fout*/) const -> void override { diff --git a/library/tests/trans_table/trans_table_base_test.cpp b/library/tests/trans_table/trans_table_base_test.cpp index 4a671fea2..420a4e258 100644 --- a/library/tests/trans_table/trans_table_base_test.cpp +++ b/library/tests/trans_table/trans_table_base_test.cpp @@ -37,8 +37,15 @@ class TransTableBaseTest : public ::testing::Test MockTransTable() : TransTable() {} ~MockTransTable() override = default; - auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { adds = 0; overwrites = 0; harvests = 0; } - auto reset_op_stats() -> void override {} + auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override + { + adds = 0; + overwrites = 0; + harvests = 0; + } + auto reset_op_stats() -> void override + { + } void init(const int handLookup[][15]) override { From 93f4abfd9878ed59587c42504b7a288b1c8976dd Mon Sep 17 00:00:00 2001 From: wopdevries Date: Thu, 10 Sep 2026 09:33:38 +0200 Subject: [PATCH 06/13] fix: use PRIu64 and avoid string allocation in env var check --- library/src/solver_if.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/library/src/solver_if.cpp b/library/src/solver_if.cpp index c9702f191..8001a492e 100644 --- a/library/src/solver_if.cpp +++ b/library/src/solver_if.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -676,15 +678,15 @@ auto solve_board_internal( } // Print TT stats if requested - if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && std::string(env) == "1") { + if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && env[0] == '1' && env[1] == '\0') { ThreadData* thrp_ptr = ctx.thread_ptr(); if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / (double)thrp_ptr->tt_lookup_count; std::fprintf(stderr, - "DDS_TT_STATS: lookups=%llu hits=%llu hit_rate=%.2f%%\n", - (unsigned long long)thrp_ptr->tt_lookup_count, - (unsigned long long)thrp_ptr->tt_hit_count, + "DDS_TT_STATS: lookups=%" PRIu64 " hits=%" PRIu64 " hit_rate=%.2f%%\n", + thrp_ptr->tt_lookup_count, + thrp_ptr->tt_hit_count, hit_rate); if (auto* tt = ctx.trans_table()) { int adds, overwrites, harvests; @@ -843,15 +845,15 @@ auto solve_same_board( } // Print TT stats if requested - if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && std::string(env) == "1") { + if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && env[0] == '1' && env[1] == '\0') { ThreadData* thrp_ptr = ctx.thread_ptr(); if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / (double)thrp_ptr->tt_lookup_count; std::fprintf(stderr, - "DDS_TT_STATS: lookups=%llu hits=%llu hit_rate=%.2f%%\n", - (unsigned long long)thrp_ptr->tt_lookup_count, - (unsigned long long)thrp_ptr->tt_hit_count, + "DDS_TT_STATS: lookups=%" PRIu64 " hits=%" PRIu64 " hit_rate=%.2f%%\n", + thrp_ptr->tt_lookup_count, + thrp_ptr->tt_hit_count, hit_rate); if (auto* tt = ctx.trans_table()) { int adds, overwrites, harvests; From 3e7a6278ac062a56f5b7d08a2cf03c97dd317871 Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sat, 12 Sep 2026 01:31:08 +0200 Subject: [PATCH 07/13] fix: remove duplicate include, early reset, safe division in hit_rate --- library/src/solver_if.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/library/src/solver_if.cpp b/library/src/solver_if.cpp index 8001a492e..667f0c1b7 100644 --- a/library/src/solver_if.cpp +++ b/library/src/solver_if.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -174,6 +173,11 @@ auto solve_board_internal( if (ret != RETURN_NO_FAULT) return ret; + // Reset per-solve TT stats here so the SOLVER_DONE report reflects + // this board even when the last-trick early exit below is taken. + thrp->tt_lookup_count = 0; + thrp->tt_hit_count = 0; + if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); // ---------------------------------------------------------- // Last trick, easy to solve. @@ -288,9 +292,6 @@ auto solve_board_internal( thrp->ABStats.Reset(); thrp->ABStats.ResetCum(); #endif - thrp->tt_lookup_count = 0; - thrp->tt_hit_count = 0; - if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); #ifdef DDS_TOP_LEVEL { @@ -680,9 +681,10 @@ auto solve_board_internal( // Print TT stats if requested if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && env[0] == '1' && env[1] == '\0') { ThreadData* thrp_ptr = ctx.thread_ptr(); - if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { - double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / - (double)thrp_ptr->tt_lookup_count; + if (thrp_ptr) { + double hit_rate = thrp_ptr->tt_lookup_count > 0 ? + 100.0 * (double)thrp_ptr->tt_hit_count / + (double)thrp_ptr->tt_lookup_count : 0.0; std::fprintf(stderr, "DDS_TT_STATS: lookups=%" PRIu64 " hits=%" PRIu64 " hit_rate=%.2f%%\n", thrp_ptr->tt_lookup_count, @@ -751,9 +753,6 @@ auto solve_same_board( thrp->ABStats.Reset(); thrp->ABStats.ResetCum(); #endif - thrp->tt_lookup_count = 0; - thrp->tt_hit_count = 0; - if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); #ifdef DDS_TOP_LEVEL { @@ -847,9 +846,10 @@ auto solve_same_board( // Print TT stats if requested if (auto* env = std::getenv("DDS_PRINT_TT_STATS"); env && env[0] == '1' && env[1] == '\0') { ThreadData* thrp_ptr = ctx.thread_ptr(); - if (thrp_ptr && thrp_ptr->tt_lookup_count > 0) { - double hit_rate = 100.0 * (double)thrp_ptr->tt_hit_count / - (double)thrp_ptr->tt_lookup_count; + if (thrp_ptr) { + double hit_rate = thrp_ptr->tt_lookup_count > 0 ? + 100.0 * (double)thrp_ptr->tt_hit_count / + (double)thrp_ptr->tt_lookup_count : 0.0; std::fprintf(stderr, "DDS_TT_STATS: lookups=%" PRIu64 " hits=%" PRIu64 " hit_rate=%.2f%%\n", thrp_ptr->tt_lookup_count, @@ -961,9 +961,6 @@ auto analyse_later_board( thrp->ABStats.Reset(); thrp->ABStats.ResetCum(); #endif - thrp->tt_lookup_count = 0; - thrp->tt_hit_count = 0; - if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); #ifdef DDS_TOP_LEVEL { From b45d7513e1edec0a1ad55aed5ea802ec9615827a Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sat, 12 Sep 2026 22:50:59 +0200 Subject: [PATCH 08/13] fix: add get_op_stats/reset_op_stats to TransTableP after rebase on #368 --- library/src/trans_table/trans_table_p.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/src/trans_table/trans_table_p.hpp b/library/src/trans_table/trans_table_p.hpp index c5c9caeb8..cead8f93e 100644 --- a/library/src/trans_table/trans_table_p.hpp +++ b/library/src/trans_table/trans_table_p.hpp @@ -92,6 +92,15 @@ class TransTableP : public TransTable auto print_all_suits(std::ofstream& fout) const -> void override; auto print_suit_stats(std::ofstream& fout, int trick, int hand) const -> void override; auto print_all_suit_stats(std::ofstream& fout) const -> void override; + auto reset_op_stats() -> void override + { + } + auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override + { + adds = 0; + overwrites = 0; + harvests = 0; // TransTableP not instrumented + } auto print_summary_suit_stats(std::ofstream& fout) const -> void override; auto print_entries_dist( std::ofstream& fout, int trick, int hand, const int hand_dist[]) const -> void override; From a9d643c947747ecdb5f1b8e8cdec332c6b8bffed Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sun, 13 Sep 2026 10:22:53 +0200 Subject: [PATCH 09/13] instrument: add op stats to TransTableP (adds/overwrites) --- library/src/trans_table/trans_table_p.cpp | 2 ++ library/src/trans_table/trans_table_p.hpp | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/library/src/trans_table/trans_table_p.cpp b/library/src/trans_table/trans_table_p.cpp index cb5903354..b6ddcc3bf 100644 --- a/library/src/trans_table/trans_table_p.cpp +++ b/library/src/trans_table/trans_table_p.cpp @@ -647,6 +647,7 @@ auto TransTableP::add( } if (stored_weight == weight && same_pattern(stored.key, pattern)) { tighten(stored.cards, cards, flag); + ++num_overwrites_; return; } } @@ -662,6 +663,7 @@ auto TransTableP::add( ++tree.bucket_end[b]; } ++node_count_; + ++num_adds_; } diff --git a/library/src/trans_table/trans_table_p.hpp b/library/src/trans_table/trans_table_p.hpp index cead8f93e..f51fc4eb3 100644 --- a/library/src/trans_table/trans_table_p.hpp +++ b/library/src/trans_table/trans_table_p.hpp @@ -94,13 +94,19 @@ class TransTableP : public TransTable auto print_all_suit_stats(std::ofstream& fout) const -> void override; auto reset_op_stats() -> void override { + num_adds_ = 0; + num_overwrites_ = 0; } auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { - adds = 0; - overwrites = 0; - harvests = 0; // TransTableP not instrumented + adds = num_adds_; + overwrites = num_overwrites_; + harvests = 0; // TransTableP has no harvest mechanism } + // Instrumentation counters + mutable int num_adds_ = 0; + mutable int num_overwrites_ = 0; + auto print_summary_suit_stats(std::ofstream& fout) const -> void override; auto print_entries_dist( std::ofstream& fout, int trick, int hand, const int hand_dist[]) const -> void override; From 7a36775e637af16258e971f63392773e83eee8c2 Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sun, 13 Sep 2026 15:44:13 +0200 Subject: [PATCH 10/13] fix: reset stats in solve_same_board; rename overwrites to tightens in PatternTT --- library/src/solver_if.cpp | 4 ++++ library/src/system/thread_data.hpp | 5 ++++- library/src/trans_table/trans_table_p.cpp | 2 +- library/src/trans_table/trans_table_p.hpp | 4 ++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/library/src/solver_if.cpp b/library/src/solver_if.cpp index 667f0c1b7..0c0bf5da8 100644 --- a/library/src/solver_if.cpp +++ b/library/src/solver_if.cpp @@ -753,6 +753,10 @@ auto solve_same_board( thrp->ABStats.Reset(); thrp->ABStats.ResetCum(); #endif + // Reset per-solve TT stats (keeps warm TT, resets counters only) + thrp->tt_lookup_count = 0; + thrp->tt_hit_count = 0; + if (auto* tt = ctx.trans_table()) tt->reset_op_stats(); #ifdef DDS_TOP_LEVEL { diff --git a/library/src/system/thread_data.hpp b/library/src/system/thread_data.hpp index 909b1b4da..8af9971ef 100644 --- a/library/src/system/thread_data.hpp +++ b/library/src/system/thread_data.hpp @@ -62,7 +62,10 @@ struct ThreadData double memUsed; int nodes; int trickNodes; - // TT instrumentation (per-context, single-threaded) + // TT instrumentation (per-context, single-threaded). + // tt_lookup_count: total TT probe calls on the AB hot path. + // tt_hit_count: probes that returned a cached result (higher is better). + // hit_rate = tt_hit_count / tt_lookup_count; target: maximize. uint64_t tt_lookup_count = 0; uint64_t tt_hit_count = 0; diff --git a/library/src/trans_table/trans_table_p.cpp b/library/src/trans_table/trans_table_p.cpp index b6ddcc3bf..49b078c25 100644 --- a/library/src/trans_table/trans_table_p.cpp +++ b/library/src/trans_table/trans_table_p.cpp @@ -647,7 +647,7 @@ auto TransTableP::add( } if (stored_weight == weight && same_pattern(stored.key, pattern)) { tighten(stored.cards, cards, flag); - ++num_overwrites_; + ++num_tightens_; return; } } diff --git a/library/src/trans_table/trans_table_p.hpp b/library/src/trans_table/trans_table_p.hpp index f51fc4eb3..94642fff6 100644 --- a/library/src/trans_table/trans_table_p.hpp +++ b/library/src/trans_table/trans_table_p.hpp @@ -100,12 +100,12 @@ class TransTableP : public TransTable auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { adds = num_adds_; - overwrites = num_overwrites_; + overwrites = num_tightens_; // tighten() updates, not evictions harvests = 0; // TransTableP has no harvest mechanism } // Instrumentation counters mutable int num_adds_ = 0; - mutable int num_overwrites_ = 0; + mutable int num_tightens_ = 0; ///< Duplicate-pattern tighten updates (not replacement churn) auto print_summary_suit_stats(std::ofstream& fout) const -> void override; auto print_entries_dist( From 51054fba0146289377eb15e93786eb5410e7a414 Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sun, 13 Sep 2026 15:46:01 +0200 Subject: [PATCH 11/13] fix: update remaining num_overwrites_ reference to num_tightens_ --- library/src/trans_table/trans_table_p.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/trans_table/trans_table_p.hpp b/library/src/trans_table/trans_table_p.hpp index 94642fff6..3c63823d8 100644 --- a/library/src/trans_table/trans_table_p.hpp +++ b/library/src/trans_table/trans_table_p.hpp @@ -95,7 +95,7 @@ class TransTableP : public TransTable auto reset_op_stats() -> void override { num_adds_ = 0; - num_overwrites_ = 0; + num_tightens_ = 0; } auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { From 2ce27dbde44546dbad3ac6f4d1e171cef78c6abd Mon Sep 17 00:00:00 2001 From: wopdevries Date: Sun, 13 Sep 2026 20:36:10 +0200 Subject: [PATCH 12/13] fix: correct doxygen placement and PatternTT overwrite semantics --- library/src/trans_table/trans_table.hpp | 2 +- library/src/trans_table/trans_table_l.hpp | 8 +++++--- library/src/trans_table/trans_table_p.hpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/library/src/trans_table/trans_table.hpp b/library/src/trans_table/trans_table.hpp index 975e1d181..6c262d58b 100644 --- a/library/src/trans_table/trans_table.hpp +++ b/library/src/trans_table/trans_table.hpp @@ -241,13 +241,13 @@ class TransTable /// \brief Print suit statistics for all tricks and hands. virtual auto print_all_suit_stats(std::ofstream& fout) const -> void = 0; - /// \brief Print summary suit statistics. /// \brief Get add/overwrite/harvest counters for instrumentation. virtual auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void = 0; /// \brief Reset add/overwrite/harvest counters for per-solve stats. virtual auto reset_op_stats() -> void = 0; + /// \brief Print summary suit statistics. virtual auto print_summary_suit_stats(std::ofstream& fout) const -> void = 0; /// \brief Print entries distribution for a specific hand. diff --git a/library/src/trans_table/trans_table_l.hpp b/library/src/trans_table/trans_table_l.hpp index ad6e981b3..98f17089e 100644 --- a/library/src/trans_table/trans_table_l.hpp +++ b/library/src/trans_table/trans_table_l.hpp @@ -462,21 +462,23 @@ class TransTableL: public TransTable /// \param fout Output stream auto print_all_suit_stats(std::ofstream& fout) const -> void override; - /// \brief Print summary suit statistics. - /// - /// \param fout Output stream + /// \brief Reset per-solve operation counters (adds, overwrites, harvests). auto reset_op_stats() -> void override { page_stats_.num_adds_ = 0; page_stats_.num_overwrites_ = 0; page_stats_.num_harvests_ = 0; } + /// \brief Get per-solve operation counters for instrumentation. auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { adds = page_stats_.num_adds_; overwrites = page_stats_.num_overwrites_; harvests = page_stats_.num_harvests_; } + /// \brief Print summary suit statistics. + /// + /// \param fout Output stream auto print_summary_suit_stats(std::ofstream& fout) const -> void override; /// \brief Print entries for a specific hand distribution. diff --git a/library/src/trans_table/trans_table_p.hpp b/library/src/trans_table/trans_table_p.hpp index 3c63823d8..49433fb9b 100644 --- a/library/src/trans_table/trans_table_p.hpp +++ b/library/src/trans_table/trans_table_p.hpp @@ -100,7 +100,7 @@ class TransTableP : public TransTable auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { adds = num_adds_; - overwrites = num_tightens_; // tighten() updates, not evictions + overwrites = 0; // PatternTT has no eviction; use tightens() for in-place updates harvests = 0; // TransTableP has no harvest mechanism } // Instrumentation counters From 45fa5f0ae235bb7cf04758fc6830be053b0a763c Mon Sep 17 00:00:00 2001 From: wopdevries Date: Mon, 14 Sep 2026 20:30:31 +0200 Subject: [PATCH 13/13] fix: remove unused num_tightens_ counter from TransTableP --- library/src/trans_table/trans_table_p.cpp | 1 - library/src/trans_table/trans_table_p.hpp | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/library/src/trans_table/trans_table_p.cpp b/library/src/trans_table/trans_table_p.cpp index 49b078c25..067bd119c 100644 --- a/library/src/trans_table/trans_table_p.cpp +++ b/library/src/trans_table/trans_table_p.cpp @@ -647,7 +647,6 @@ auto TransTableP::add( } if (stored_weight == weight && same_pattern(stored.key, pattern)) { tighten(stored.cards, cards, flag); - ++num_tightens_; return; } } diff --git a/library/src/trans_table/trans_table_p.hpp b/library/src/trans_table/trans_table_p.hpp index 49433fb9b..13692418f 100644 --- a/library/src/trans_table/trans_table_p.hpp +++ b/library/src/trans_table/trans_table_p.hpp @@ -95,7 +95,7 @@ class TransTableP : public TransTable auto reset_op_stats() -> void override { num_adds_ = 0; - num_tightens_ = 0; + } auto get_op_stats(int& adds, int& overwrites, int& harvests) const -> void override { @@ -105,7 +105,6 @@ class TransTableP : public TransTable } // Instrumentation counters mutable int num_adds_ = 0; - mutable int num_tightens_ = 0; ///< Duplicate-pattern tighten updates (not replacement churn) auto print_summary_suit_stats(std::ofstream& fout) const -> void override; auto print_entries_dist(