#include "mining_benchmark.h" #include #include namespace dragonx { namespace ui { void ThreadBenchmark::reset() { phase = Phase::Idle; candidates.clear(); current_index = 0; results.clear(); phase_timer = 0.0f; prev_window_avg = 0.0; window_sum = 0.0; window_samples = 0; window_timer = 0.0f; consecutive_stable = 0; measure_sum = 0.0; measure_samples = 0; optimal_threads = 0; optimal_hashrate = 0.0; was_pool_running = false; prev_threads = 0; total_warmup_secs = 0.0f; } void ThreadBenchmark::buildCandidates(int maxThreads) { candidates.clear(); int start = std::max(1, maxThreads / 2); for (int threads = start; threads <= maxThreads; ++threads) { candidates.push_back(threads); } } float ThreadBenchmark::avgWarmupSecs() const { if (current_index > 0) { return total_warmup_secs / static_cast(current_index); } return (MIN_WARMUP_SECS + MAX_WARMUP_SECS) * 0.5f; } float ThreadBenchmark::perTestSecs() const { return avgWarmupSecs() + MEASURE_SECS; } float ThreadBenchmark::totalEstimatedSecs() const { int count = static_cast(candidates.size()); if (count <= 0) return 0.0f; float completedTime = total_warmup_secs + static_cast(current_index) * (MEASURE_SECS + COOLDOWN_SECS); int remaining = count - current_index; float remainingTime = static_cast(remaining) * (avgWarmupSecs() + MEASURE_SECS) + static_cast(std::max(0, remaining - 1)) * COOLDOWN_SECS; return completedTime + remainingTime; } float ThreadBenchmark::elapsedSecs() const { float completed = total_warmup_secs + static_cast(current_index) * (MEASURE_SECS + COOLDOWN_SECS); return completed + phase_timer; } float ThreadBenchmark::progress() const { float total = totalEstimatedSecs(); return (total > 0.0f) ? std::min(1.0f, elapsedSecs() / total) : 0.0f; } void ThreadBenchmark::resetStabilityTracking() { prev_window_avg = 0.0; window_sum = 0.0; window_samples = 0; window_timer = 0.0f; consecutive_stable = 0; } bool ThreadBenchmark::active() const { return phase != Phase::Idle && phase != Phase::Done; } ThreadBenchmarkUpdate AdvanceThreadBenchmark(ThreadBenchmark& benchmark, float deltaSeconds, double poolHashrate10s) { ThreadBenchmarkUpdate update; if (!benchmark.active()) return update; benchmark.phase_timer += deltaSeconds; switch (benchmark.phase) { case ThreadBenchmark::Phase::Starting: if (benchmark.current_index < static_cast(benchmark.candidates.size())) { int threads = benchmark.candidates[benchmark.current_index]; update.stopPoolMining = true; update.startPoolMining = true; update.startThreads = threads; benchmark.phase = ThreadBenchmark::Phase::WarmingUp; benchmark.phase_timer = 0.0f; benchmark.resetStabilityTracking(); benchmark.measure_sum = 0.0; benchmark.measure_samples = 0; } else { benchmark.phase = ThreadBenchmark::Phase::Done; } break; case ThreadBenchmark::Phase::WarmingUp: { bool pastMin = benchmark.phase_timer >= ThreadBenchmark::MIN_WARMUP_SECS; bool pastMax = benchmark.phase_timer >= ThreadBenchmark::MAX_WARMUP_SECS; if (poolHashrate10s > 0.0) { benchmark.window_sum += poolHashrate10s; benchmark.window_samples++; } benchmark.window_timer += deltaSeconds; bool stable = false; if (pastMin && benchmark.window_timer >= ThreadBenchmark::STABILITY_WINDOW_SECS && benchmark.window_samples > 0) { double currentAverage = benchmark.window_sum / benchmark.window_samples; if (benchmark.prev_window_avg > 0.0) { double change = std::abs(currentAverage - benchmark.prev_window_avg) / benchmark.prev_window_avg; if (change < ThreadBenchmark::STABILITY_THRESHOLD) benchmark.consecutive_stable++; else benchmark.consecutive_stable = 0; if (benchmark.consecutive_stable >= ThreadBenchmark::STABLE_WINDOWS_NEEDED) stable = true; } benchmark.prev_window_avg = currentAverage; benchmark.window_sum = 0.0; benchmark.window_samples = 0; benchmark.window_timer = 0.0f; } if (stable || pastMax) { benchmark.total_warmup_secs += benchmark.phase_timer; benchmark.phase = ThreadBenchmark::Phase::Measuring; benchmark.phase_timer = 0.0f; benchmark.measure_sum = 0.0; benchmark.measure_samples = 0; } break; } case ThreadBenchmark::Phase::Measuring: if (poolHashrate10s > 0.0) { benchmark.measure_sum += poolHashrate10s; benchmark.measure_samples++; } if (benchmark.phase_timer >= ThreadBenchmark::MEASURE_SECS) { int threads = benchmark.candidates[benchmark.current_index]; double average = (benchmark.measure_samples > 0) ? benchmark.measure_sum / benchmark.measure_samples : 0.0; benchmark.results.push_back({threads, average}); if (average > benchmark.optimal_hashrate) { benchmark.optimal_hashrate = average; benchmark.optimal_threads = threads; } benchmark.phase = ThreadBenchmark::Phase::Advancing; benchmark.phase_timer = 0.0f; } break; case ThreadBenchmark::Phase::Advancing: update.stopPoolMining = true; benchmark.current_index++; if (benchmark.current_index < static_cast(benchmark.candidates.size())) { benchmark.phase = ThreadBenchmark::Phase::CoolingDown; benchmark.phase_timer = 0.0f; } else { benchmark.phase = ThreadBenchmark::Phase::Done; if (benchmark.optimal_threads > 0) { update.saveOptimalThreads = true; update.optimalThreads = benchmark.optimal_threads; if (benchmark.was_pool_running) { update.startPoolMining = true; update.startThreads = benchmark.optimal_threads; } } } break; case ThreadBenchmark::Phase::CoolingDown: if (benchmark.phase_timer >= ThreadBenchmark::COOLDOWN_SECS) { benchmark.phase = ThreadBenchmark::Phase::Starting; benchmark.phase_timer = 0.0f; } break; default: break; } return update; } } // namespace ui } // namespace dragonx