perf(mining): run the mine-when-idle check at 1 Hz, probe the GPU sparingly
checkIdleMining ran every frame: an X11 idle query and, with GPU-aware idle detection, an nvidia-smi launch on the UI thread per frame. - The check runs once a second, since its decisions work on a seconds scale (the idle delay is tens of seconds or more). A miner it started or scaled up still yields on the first frame that sees the user back: that path skips the gate, because frames can be seconds apart while the app idles. - The GPU is probed only when input is idle and the result can change a decision, at most every 5 s, with hysteresis (busy at 10%+, clear below 5%) so a GPU hovering near 10% can't keep restarting the miner. - Thread scaling returns early when no miner is running or a toggle is in flight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -966,7 +966,7 @@ void App::update()
|
||||
checkAutoLock();
|
||||
}
|
||||
|
||||
// Mine-when-idle check (runs every frame, internally rate-limited by idle detection)
|
||||
// Mine-when-idle check (cheap on most frames: it gates itself to 1 Hz)
|
||||
checkIdleMining();
|
||||
|
||||
// P8: Dedup rebuildAddressList — only rebuild once per frame
|
||||
|
||||
@@ -1604,6 +1604,9 @@ private:
|
||||
|
||||
// Mine-when-idle: auto-start/stop mining based on system idle state
|
||||
bool idle_mining_active_ = false; // true when mining was auto-started by idle detection
|
||||
std::chrono::steady_clock::time_point idle_mining_next_check_{}; // checkIdleMining runs at 1 Hz
|
||||
std::chrono::steady_clock::time_point idle_gpu_next_sample_{}; // GPU probe at most every 5 s
|
||||
bool idle_gpu_busy_ = false; // last GPU-aware verdict (with hysteresis)
|
||||
bool idle_scaled_to_idle_ = false; // true when threads have been scaled up for idle
|
||||
|
||||
// Private methods - rendering
|
||||
|
||||
+38
-13
@@ -752,6 +752,17 @@ void App::checkIdleMining() {
|
||||
// Skip idle mining adjustments while thread benchmark is running
|
||||
if (ui::IsMiningBenchmarkActive()) return;
|
||||
|
||||
// The decisions below work on a seconds scale (the idle delay is tens of seconds or more), so check
|
||||
// at 1 Hz. This used to run every frame: an X11 idle query and, with GPU-aware idle, an nvidia-smi
|
||||
// launch on the UI thread per frame. Steady clock rather than ImGui::GetTime(), whose clock stops
|
||||
// while minimized (update() still runs then).
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
// ...except while a miner this feature started (or scaled up) is running: it must yield on the first
|
||||
// frame that sees the user back, and frames can be seconds apart when the app idles, so a gated frame
|
||||
// may be the only one carrying that input. The idle query is cheap; the GPU probe has its own cache.
|
||||
if (!(idle_mining_active_ || idle_scaled_to_idle_) && now < idle_mining_next_check_) return;
|
||||
idle_mining_next_check_ = now + std::chrono::seconds(1);
|
||||
|
||||
int idleSec = util::Platform::getSystemIdleSeconds();
|
||||
int delay = settings_->getMineIdleDelay();
|
||||
bool isPool = settings_->getPoolMode() || !supportsSoloMining();
|
||||
@@ -759,26 +770,40 @@ void App::checkIdleMining() {
|
||||
bool threadScaling = settings_->getIdleThreadScaling();
|
||||
int maxThreads = std::max(1, (int)std::thread::hardware_concurrency());
|
||||
|
||||
// GPU-aware idle detection: if enabled, treat GPU utilization >= 10%
|
||||
// as "user active" (e.g. watching a video). Disabled = unrestricted
|
||||
// mode that only looks at keyboard/mouse input.
|
||||
bool gpuBusy = false;
|
||||
if (settings_->getIdleGpuAware()) {
|
||||
int gpuUtil = util::Platform::getGpuUtilization();
|
||||
gpuBusy = (gpuUtil >= 10);
|
||||
}
|
||||
bool systemIdle = (idleSec >= delay) && !gpuBusy;
|
||||
|
||||
// Check if mining is already running (manually started by user)
|
||||
bool miningActive = isPool
|
||||
? (xmrig_manager_ && xmrig_manager_->isRunning())
|
||||
: state_.mining.generate;
|
||||
|
||||
// Thread scaling only adjusts a miner the user started: with none running there is nothing to
|
||||
// decide, or to probe for.
|
||||
if (threadScaling && (!miningActive || mining_toggle_in_progress_.load())) return;
|
||||
|
||||
// GPU-aware idle detection: if enabled, treat GPU utilization >= 10% as "user active" (e.g. watching
|
||||
// a video). Disabled = unrestricted mode that only looks at keyboard/mouse input. The probe can only
|
||||
// matter once input is idle and a decision can change (start/stop mode leaves a miner the user
|
||||
// started alone), so it runs only then, at most every 5 s, with hysteresis (busy at >= 10%, clear
|
||||
// below 5%) so a GPU hovering near 10% can't keep restarting the miner. Unknown (-1) reads as not busy.
|
||||
const bool inputIdle = idleSec >= delay;
|
||||
const bool decisionPossible = threadScaling || !miningActive || idle_mining_active_;
|
||||
bool gpuBusy = false;
|
||||
if (inputIdle && decisionPossible && settings_->getIdleGpuAware()) {
|
||||
if (now >= idle_gpu_next_sample_) {
|
||||
const int gpuUtil = util::Platform::getGpuUtilization();
|
||||
if (gpuUtil >= 10) idle_gpu_busy_ = true;
|
||||
else if (gpuUtil < 5) idle_gpu_busy_ = false;
|
||||
idle_gpu_next_sample_ = now + std::chrono::seconds(5);
|
||||
}
|
||||
gpuBusy = idle_gpu_busy_;
|
||||
} else if (!inputIdle) {
|
||||
idle_gpu_busy_ = false; // input activity decides now; sample afresh once idle again
|
||||
idle_gpu_next_sample_ = {};
|
||||
}
|
||||
bool systemIdle = inputIdle && !gpuBusy;
|
||||
|
||||
if (threadScaling) {
|
||||
// --- Thread scaling mode ---
|
||||
// Mining must already be running (started by user). We just adjust threads.
|
||||
if (!miningActive || mining_toggle_in_progress_.load()) return;
|
||||
|
||||
// Mining is already running (started by user; checked above). We just adjust threads.
|
||||
int activeThreads = settings_->getIdleThreadsActive();
|
||||
int idleThreads = settings_->getIdleThreadsIdle();
|
||||
// Resolve auto values: active defaults to half, idle defaults to all
|
||||
|
||||
Reference in New Issue
Block a user