perf(mining): one save, at most one restart per thread-count gesture
Every thread-count change on the Mining tab saved settings.json and, with a miner running, stopped and restarted it: xmrig re-inits its RandomX dataset and reconnects to the pool, seconds of lost hashing each time. A drag across the grid did this once per tile crossed (up to ~24), with a "Starting pool miner" toast each; every -/+ click or Enter did it again. The tab now hands each pick to the App, which puts the newest in settings at once and applies it when the input settles: on drag release, or 0.5 s after the last click, even if the Mining tab was left in the meantime. The header, RAM estimate and tile fill still follow the drag live. - Pool: the restart is decided on the mining-control thread, after every earlier start or stop has run. It never starts a miner the user stopped, is a no-op when xmrig already runs at that count, and is no longer silently skipped when the pick lands in another restart's stop window (which left xmrig on the old count). - Solo: one setgenerate per gesture. A pick made while one is in flight now waits for it instead of being dropped (another stale count). - Clicking the tile that is already selected does nothing; it used to save settings and restart the miner. - With idle thread scaling on, the grid no longer snaps back to the old count while a pick is on its way to the miner. A pool auto-balance restart keeps a count still queued. - Idle thread scaling changes the pool count through the same retarget. Its correction after a start compares with the count last requested, not the xmrig API's lagging report, so it comes about 1 s in instead of after the first API report (3-5 s). - A retarget whose pool config no longer validates shows the same error and stops the miner, as the old stop-then-start did. - While a benchmark runs a pick is saved, but the miner change is dropped: the benchmark owns the count until it ends. A crash within 0.5 s of a pick, or mid-drag, loses that pick; every clean exit saves it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+70
-7
@@ -819,8 +819,7 @@ void App::checkIdleMining() {
|
||||
if (!idle_scaled_to_idle_) {
|
||||
idle_scaled_to_idle_ = true;
|
||||
if (isPool) {
|
||||
stopPoolMining();
|
||||
startPoolMining(idleThreads);
|
||||
retargetPoolMining(idleThreads);
|
||||
} else {
|
||||
startMining(idleThreads);
|
||||
}
|
||||
@@ -831,8 +830,7 @@ void App::checkIdleMining() {
|
||||
if (idle_scaled_to_idle_) {
|
||||
idle_scaled_to_idle_ = false;
|
||||
if (isPool) {
|
||||
stopPoolMining();
|
||||
startPoolMining(activeThreads);
|
||||
retargetPoolMining(activeThreads);
|
||||
} else {
|
||||
startMining(activeThreads);
|
||||
}
|
||||
@@ -840,13 +838,16 @@ void App::checkIdleMining() {
|
||||
} else {
|
||||
// Mining just started while user is active — ensure active
|
||||
// thread count is applied (grid selection may differ).
|
||||
// Pool: compare with the count last REQUESTED (a restart still queued on the control thread
|
||||
// included), not the xmrig API's last report, which lags a restart by seconds.
|
||||
int currentThreads = isPool
|
||||
? xmrig_manager_->getStats().threads_active
|
||||
? services::effectivePoolThreads(pool_threads_target_.load(std::memory_order_relaxed),
|
||||
xmrig_manager_->getRequestedThreads(),
|
||||
xmrig_manager_->getStats().threads_active)
|
||||
: state_.mining.genproclimit;
|
||||
if (currentThreads > 0 && currentThreads != activeThreads) {
|
||||
if (isPool) {
|
||||
stopPoolMining();
|
||||
startPoolMining(activeThreads);
|
||||
retargetPoolMining(activeThreads);
|
||||
} else {
|
||||
startMining(activeThreads);
|
||||
}
|
||||
@@ -889,6 +890,68 @@ void App::checkIdleMining() {
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Mining thread-count picks — coalesced (one save + at most one miner change per gesture)
|
||||
// ===========================================================================
|
||||
|
||||
namespace {
|
||||
double miningSteadySeconds()
|
||||
{
|
||||
return std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void App::selectMiningThreads(int threads)
|
||||
{
|
||||
if (!settings_) return;
|
||||
threads = std::clamp(threads, 1, std::max(1, (int)std::thread::hardware_concurrency()));
|
||||
if (settings_->getPoolThreads() != threads) { // in memory now; saved once when the pick settles
|
||||
settings_->setPoolThreads(threads);
|
||||
mining_threads_save_pending_ = true;
|
||||
}
|
||||
mining_thread_commit_.select(threads, miningSteadySeconds());
|
||||
}
|
||||
|
||||
void App::tickMiningThreadCommit()
|
||||
{
|
||||
if (shutting_down_ || !settings_) return; // beginShutdown() saves settings itself
|
||||
const double now = miningSteadySeconds();
|
||||
|
||||
// The grid holds the commit while the button is down. The release is noticed HERE, not by the tab, so
|
||||
// a drag that ends while the Mining tab isn't drawn (page switch, focus loss) still commits.
|
||||
bool held = false;
|
||||
if (mining_thread_drag_) {
|
||||
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) held = true;
|
||||
else { mining_thread_drag_ = false; mining_thread_commit_.release(now); }
|
||||
}
|
||||
|
||||
// The benchmark owns the miner's thread count while it runs (its cancel/finish restore it): keep the
|
||||
// pick in settings, drop the miner change.
|
||||
if (ui::IsMiningBenchmarkActive()) {
|
||||
mining_thread_commit_.cancel();
|
||||
mining_solo_threads_pending_ = 0;
|
||||
if (mining_threads_save_pending_) { mining_threads_save_pending_ = false; settings_->save(); }
|
||||
return;
|
||||
}
|
||||
|
||||
int threads = 0;
|
||||
if (mining_thread_commit_.take(now, held, threads)) {
|
||||
if (mining_threads_save_pending_) { mining_threads_save_pending_ = false; settings_->save(); }
|
||||
retargetPoolMining(threads); // restarts xmrig once, only if it is running
|
||||
mining_solo_threads_pending_ = threads; // setgenerate, once no other toggle is in flight
|
||||
} else if (mining_thread_commit_.pending() && !held) {
|
||||
ui::material::RequestProgressFrame(); // keep frames coming until the debounce deadline
|
||||
}
|
||||
|
||||
// Solo: startMining() drops a call while another setgenerate is in flight, so wait for it (its result
|
||||
// wakes the loop). Applies only while solo mining is on, as before.
|
||||
if (mining_solo_threads_pending_ > 0 && !mining_toggle_in_progress_.load(std::memory_order_relaxed)) {
|
||||
const int t = mining_solo_threads_pending_;
|
||||
mining_solo_threads_pending_ = 0;
|
||||
if (state_.mining.generate) startMining(t);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Restart Setup Wizard (from Settings)
|
||||
// ===========================================================================
|
||||
|
||||
Reference in New Issue
Block a user