- Add refresh scheduler and network refresh service boundaries for typed refresh results, ordered RPC collectors, applicators, and price parsing. - Add daemon lifecycle and wallet security workflow helpers while preserving App-owned command RPC, decrypt, cancellation, and UI handoff behavior. - Split balance, console, mining, amount formatting, and async task logic into focused modules with expanded Phase 4 test coverage. - Fix market price loading by triggering price refresh immediately, avoiding queue-pressure drops, tracking loading/error state, and adding translations. - Polish send, explorer, peers, settings, theme/schema, and related tab UI. - Replace checked-in generated language headers with build-generated resources. - Document the cleanup audit, UI static-state guidance, and architecture updates.
212 lines
7.2 KiB
C++
212 lines
7.2 KiB
C++
#include "mining_benchmark.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
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<float>(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<int>(candidates.size());
|
|
if (count <= 0) return 0.0f;
|
|
float completedTime = total_warmup_secs
|
|
+ static_cast<float>(current_index) * (MEASURE_SECS + COOLDOWN_SECS);
|
|
int remaining = count - current_index;
|
|
float remainingTime = static_cast<float>(remaining) * (avgWarmupSecs() + MEASURE_SECS)
|
|
+ static_cast<float>(std::max(0, remaining - 1)) * COOLDOWN_SECS;
|
|
return completedTime + remainingTime;
|
|
}
|
|
|
|
float ThreadBenchmark::elapsedSecs() const
|
|
{
|
|
float completed = total_warmup_secs
|
|
+ static_cast<float>(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<int>(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<int>(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
|