From e7797baf9cb819f3f4c1abf6cac0b038e9b7ed1c Mon Sep 17 00:00:00 2001 From: Christoph Hausner Date: Thu, 11 Sep 2025 20:39:40 +0200 Subject: [PATCH] Print incremental progress during each depth --- include/core/Solver.h | 4 +- src/core/Solver.cpp | 107 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 10 deletions(-) diff --git a/include/core/Solver.h b/include/core/Solver.h index d984172..3836c24 100644 --- a/include/core/Solver.h +++ b/include/core/Solver.h @@ -59,12 +59,14 @@ namespace sgbust std::optional bestScore; unsigned int beamSize = 0; double multiplier = 0; - std::shared_mutex mutex; + mutable std::shared_mutex mutex; void SolveDepth(bool maxDepthReached, bool& stop); unsigned int SolveGrid(const Grid& grid, Score score, std::map& newGrids, bool maxDepthReached, bool& stop); void CheckSolution(const Grid& grid, Score score, bool& stop); void PrintStats() const; + void PrintProgress(const std::map& newGrids, unsigned int gridsSolved, unsigned int newBeamSize) const; + void ClearProgress() const; void TrimBeam(); public: diff --git a/src/core/Solver.cpp b/src/core/Solver.cpp index ad3be66..3df0217 100644 --- a/src/core/Solver.cpp +++ b/src/core/Solver.cpp @@ -1,13 +1,19 @@ #include "core/Solver.h" #include +#include #include +#include #include +#include #include #include #include +#include #include #include +#include +#include #include #include @@ -68,17 +74,72 @@ namespace sgbust auto [minScore, maxScore] = std::ranges::minmax(grids | std::views::transform([](const auto& b) { return b.first.Value; })); long long scoreSum = std::transform_reduce(grids.begin(), grids.end(), 0LL, std::plus<>(), [](auto& b) { return b.first.Value * b.second.size(); }); double avgScore = static_cast(scoreSum) / beamSize; - std::cout << - "Depth: " << std::setw(3) << depth << - ", grids: " << std::setw(9) << beamSize << - ", hash sets: " << std::setw(4) << grids.size() << - ", scores (min/avg/max): " << minScore << "/" << std::fixed << std::setprecision(1) << avgScore << "/" << maxScore; + + std::string output = std::format( + "Depth: {:3}, grids: {:9}, hash sets: {:4}, scores (min/avg/max): {}/{:.1f}/{}", + depth, + beamSize, + grids.size(), + minScore, + avgScore, + maxScore + ); + + std::optional memoryUsage = GetCurrentMemoryUsage(); + if (memoryUsage.has_value()) + output += std::format(", memory: {}MB", (*memoryUsage / 1024 / 1024)); + + std::cout << output << std::endl; + } + + void Solver::PrintProgress(const std::map& newGrids, unsigned int gridsSolved, unsigned int newBeamSize) const + { + int curMinScore = 0; + int curMaxScore = 0; + double curAvgScore = 0.0; + + std::shared_lock lock(mutex); + std::size_t numHashSets = newGrids.size(); + + if (!newGrids.empty()) + { + auto [minScore, maxScore] = std::ranges::minmax(newGrids | std::views::transform([](const auto& b) { return b.first.Value; })); + curMinScore = minScore; + curMaxScore = maxScore; + long long scoreSum = std::transform_reduce(newGrids.begin(), newGrids.end(), 0LL, std::plus<>(), [](auto& b) { return b.first.Value * b.second.size(); }); + curAvgScore = static_cast(scoreSum) / newBeamSize; + } + + lock.unlock(); + + double percentProcessed = gridsSolved * 100.0 / beamSize; + double percentBeamSizeLimit = 0.0; + if (MaxBeamSize.has_value()) + percentBeamSizeLimit = newBeamSize * 100.0 / *MaxBeamSize; + double progress = std::max(percentProcessed, percentBeamSizeLimit); + + std::string output = std::format( + "Depth: {:3}, grids: {:9}, hash sets: {:4}, scores (min/avg/max): {}/{:.1f}/{}", + depth + 1, + newBeamSize, + numHashSets, + curMinScore, + curAvgScore, + curMaxScore + ); std::optional memoryUsage = GetCurrentMemoryUsage(); if (memoryUsage.has_value()) - std::cout << ", memory: " << (*memoryUsage / 1024 / 1024) << "MB"; + output += std::format(", memory: {}MB", (*memoryUsage / 1024 / 1024)); + + output += std::format(" ({:.1f}%)", progress); - std::cout << std::endl; + std::cout << "\x1b[2K\r" + output << std::flush; + } + + void Solver::ClearProgress() const + { + std::cout << "\x1b[2K\r" << std::flush; } void Solver::SolveDepth(bool maxDepthReached, bool& stop) @@ -88,6 +149,28 @@ namespace sgbust std::atomic_uint gridsSolved = 0; std::atomic_uint newBeamSize = 0; + std::optional reporter; + + if (!Quiet) + reporter.emplace([&](std::stop_token stopToken) { + std::mutex reporterMutex; + std::condition_variable reporterCv; + std::stop_callback callback(stopToken, [&]() { reporterCv.notify_all(); }); + + while (true) + { + { + std::unique_lock lock(reporterMutex); + if (reporterCv.wait_for(lock, std::chrono::seconds(1), [&]() { return stopToken.stop_requested(); })) + break; + } + + PrintProgress(newGrids, gridsSolved, newBeamSize); + } + + ClearProgress(); + }); + for (auto it = grids.begin(); it != grids.end(); it = grids.erase(it)) { auto& [score, hashSet] = *it; @@ -108,12 +191,18 @@ namespace sgbust // overall, deallocation is faster if we deallocate the data inside CompactGrids here already const_cast(grid) = CompactGrid(); - }); + }); if (stop || (MaxBeamSize && newBeamSize >= MaxBeamSize)) break; } + if (reporter.has_value()) + { + reporter->request_stop(); + reporter->join(); + } + multiplier = static_cast(newBeamSize) / gridsSolved; if (newBeamSize == 0) @@ -133,7 +222,7 @@ namespace sgbust auto getOrCreateHashSet = [&](const Score& score) -> GridHashSet& { { std::shared_lock lock(mutex); - auto it = newGrids.find(score); + auto it = newGrids.find(score); if (it != newGrids.end()) return it->second; }