The thread benchmark state machine was advanced from the Mining tab's render path, so it only progressed while that tab was visible and the window was on screen. Hiding the tab or minimizing the window froze the benchmark mid-sweep. - The benchmark now advances from the main update loop, ungated by connectivity, so it keeps running when the tab is hidden or the window is minimized. - Its clock is driven from a steady wall-clock reading rather than the render delta, and each step is clamped like the other logic timers so a suspend/resume cannot leap a benchmark phase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
81 lines
2.6 KiB
C++
81 lines
2.6 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
struct ThreadBenchmark {
|
|
enum class Phase { Idle, Starting, WarmingUp, Measuring, Advancing, CoolingDown, Done };
|
|
|
|
struct Result {
|
|
int threads = 0;
|
|
double hashrate = 0.0;
|
|
};
|
|
|
|
static constexpr float MIN_WARMUP_SECS = 90.0f;
|
|
static constexpr float MAX_WARMUP_SECS = 300.0f;
|
|
static constexpr float MEASURE_SECS = 30.0f;
|
|
static constexpr float COOLDOWN_SECS = 5.0f;
|
|
static constexpr float STABILITY_WINDOW_SECS = 10.0f;
|
|
static constexpr float STABILITY_THRESHOLD = 0.05f;
|
|
static constexpr int STABLE_WINDOWS_NEEDED = 3;
|
|
|
|
Phase phase = Phase::Idle;
|
|
std::vector<int> candidates;
|
|
int current_index = 0;
|
|
std::vector<Result> results;
|
|
float phase_timer = 0.0f;
|
|
double prev_window_avg = 0.0;
|
|
double window_sum = 0.0;
|
|
int window_samples = 0;
|
|
float window_timer = 0.0f;
|
|
int consecutive_stable = 0;
|
|
double measure_sum = 0.0;
|
|
int measure_samples = 0;
|
|
int optimal_threads = 0;
|
|
double optimal_hashrate = 0.0;
|
|
bool was_pool_running = false;
|
|
int prev_threads = 0;
|
|
float total_warmup_secs = 0.0f;
|
|
|
|
void reset();
|
|
void buildCandidates(int maxThreads);
|
|
float avgWarmupSecs() const;
|
|
float perTestSecs() const;
|
|
float totalEstimatedSecs() const;
|
|
float elapsedSecs() const;
|
|
float progress() const;
|
|
void resetStabilityTracking();
|
|
bool active() const;
|
|
};
|
|
|
|
struct ThreadBenchmarkUpdate {
|
|
bool stopPoolMining = false;
|
|
bool startPoolMining = false;
|
|
int startThreads = 0;
|
|
bool saveOptimalThreads = false;
|
|
int optimalThreads = 0;
|
|
bool inconclusive = false; // finished with no nonzero hashrate sample — caller should warn
|
|
};
|
|
|
|
// What a user-initiated cancel of an active benchmark must do with the miner. Always drop the candidate-thread
|
|
// miner; restart only if the user was mining BEFORE the benchmark (snapshot, not live state - the sweep
|
|
// transiently stops the miner, so a live check would drop the restore, L-04/L-11).
|
|
struct ThreadBenchmarkCancel {
|
|
bool restartMiner = false;
|
|
int restartThreads = 0;
|
|
};
|
|
ThreadBenchmarkCancel PlanThreadBenchmarkCancel(const ThreadBenchmark& benchmark);
|
|
|
|
// Wall-clock step for the benchmark: seconds since the previous call, clamped to [0, maxStep]. The first call
|
|
// (lastSec < 0) returns 0. Updates lastSec.
|
|
float AdvanceBenchmarkClock(double& lastSec, double nowSec, float maxStep);
|
|
|
|
ThreadBenchmarkUpdate AdvanceThreadBenchmark(ThreadBenchmark& benchmark,
|
|
float deltaSeconds,
|
|
double poolHashrate10s);
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|