feat: thread benchmark, GPU-aware idle mining, thread scaling fix

- Add pool mining thread benchmark: cycles through thread counts with
  20s warmup + 10s measurement to find optimal setting for CPU
- Add GPU-aware idle detection: GPU utilization >= 10% (video, games)
  treats system as active; toggle in mining tab header (default: on)
  Supports AMD sysfs, NVIDIA nvidia-smi, Intel freq ratio; -1 on macOS
- Fix idle thread scaling: use getRequestedThreads() for immediate
  thread count instead of xmrig API threads_active which lags on restart
- Apply active thread count on initial mining start when user is active
- Skip idle mining adjustments while benchmark is running
- Disable thread grid drag-to-select during benchmark
- Add idle_gpu_aware setting with JSON persistence (default: true)
- Add 7 i18n English strings for benchmark and GPU-aware tooltips
This commit is contained in:
dan_s
2026-04-01 17:06:05 -05:00
parent b3d43ba0ad
commit 09f287fbc5
10 changed files with 508 additions and 9 deletions

View File

@@ -19,6 +19,7 @@
#include "ui/schema/ui_schema.h"
#include "ui/theme.h"
#include "ui/effects/imgui_acrylic.h"
#include "ui/windows/mining_tab.h"
#include "util/platform.h"
#include "util/secure_vault.h"
#include "util/perf_log.h"
@@ -439,12 +440,25 @@ void App::checkIdleMining() {
return;
}
// Skip idle mining adjustments while thread benchmark is running
if (ui::IsMiningBenchmarkActive()) return;
int idleSec = util::Platform::getSystemIdleSeconds();
int delay = settings_->getMineIdleDelay();
bool isPool = settings_->getPoolMode();
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())
@@ -461,7 +475,7 @@ void App::checkIdleMining() {
if (activeThreads <= 0) activeThreads = std::max(1, maxThreads / 2);
if (idleThreads <= 0) idleThreads = maxThreads;
if (idleSec >= delay) {
if (systemIdle) {
// System is idle — scale up to idle thread count
if (!idle_scaled_to_idle_) {
idle_scaled_to_idle_ = true;
@@ -474,7 +488,7 @@ void App::checkIdleMining() {
DEBUG_LOGF("[App] Idle thread scaling: %d -> %d threads (idle)\n", activeThreads, idleThreads);
}
} else {
// User is active — scale down to active thread count
// User is active (or GPU busy) — scale down to active thread count
if (idle_scaled_to_idle_) {
idle_scaled_to_idle_ = false;
if (isPool) {
@@ -484,11 +498,26 @@ void App::checkIdleMining() {
startMining(activeThreads);
}
DEBUG_LOGF("[App] Idle thread scaling: %d -> %d threads (active)\n", idleThreads, activeThreads);
} else {
// Mining just started while user is active — ensure active
// thread count is applied (grid selection may differ).
int currentThreads = isPool
? xmrig_manager_->getStats().threads_active
: state_.mining.genproclimit;
if (currentThreads > 0 && currentThreads != activeThreads) {
if (isPool) {
stopPoolMining();
startPoolMining(activeThreads);
} else {
startMining(activeThreads);
}
DEBUG_LOGF("[App] Idle thread scaling: initial %d -> %d threads (active)\n", currentThreads, activeThreads);
}
}
}
} else {
// --- Start/Stop mode (original behavior) ---
if (idleSec >= delay) {
if (systemIdle) {
// System is idle — start mining if not already running
if (!miningActive && !idle_mining_active_ && !mining_toggle_in_progress_.load()) {
// For solo mining, need daemon connected and synced