perf(mining): memoize the earnings card on tx_revision
The Mining tab's earnings card walked all N wallet transactions on every frame to total Today / Yesterday / All time and pick the 4 most recent rows: four string compares per transaction plus a memo search on receives. It also ran localtime and mktime every frame to find local midnight. The pass is now a pure function behind a one-slot cache keyed on WalletState::tx_revision, the transaction count, the filter pill, the Solo/Pool mode, solo support and local midnight, with a 2 s maximum age as a safety net. It reruns only when one of those changes or the 2 s age runs out, instead of every frame. The totals are bit-identical (same loop, same summation order), and crossing local midnight still moves Today into Yesterday at once. Local midnight is recomputed only when the clock second changes, through the shared util::localTime; a failed localtime now yields a zeroed struct instead of an uninitialised one. The "x ago" labels and Est. Daily are still computed per frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,9 @@
|
||||
#include "imgui_internal.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
@@ -43,66 +45,28 @@ void RenderMiningEarnings(App* app, const WalletState& state, const MiningInfo&
|
||||
{
|
||||
const bool s_pool_mode = poolMode; // alias: keep the moved body's identifier verbatim
|
||||
char buf[128]; // section-local scratch (was shared in the parent)
|
||||
// Gather mining transactions from state
|
||||
double minedToday = 0.0, minedYesterday = 0.0, minedAllTime = 0.0;
|
||||
int minedTodayCount = 0, minedYesterdayCount = 0, minedAllTimeCount = 0;
|
||||
// Mining earnings totals + the 4 most recent rows, memoised (MiningEarningsCache): the O(transactions)
|
||||
// pass reruns only when the tx list (WalletState::tx_revision), the filter pill, the Solo/Pool mode, the
|
||||
// solo capability or the local day changes, or at most every 2 s as a safety net - not every frame.
|
||||
// `now` stays per frame (the "x ago" labels below use it).
|
||||
int64_t now = (int64_t)std::time(nullptr);
|
||||
|
||||
// Calendar-day boundaries (local time)
|
||||
time_t nowT = (time_t)now;
|
||||
struct tm local;
|
||||
#ifdef _WIN32
|
||||
localtime_s(&local, &nowT);
|
||||
#else
|
||||
localtime_r(&nowT, &local);
|
||||
#endif
|
||||
local.tm_hour = 0;
|
||||
local.tm_min = 0;
|
||||
local.tm_sec = 0;
|
||||
int64_t todayStart = (int64_t)mktime(&local);
|
||||
int64_t yesterdayStart = todayStart - 86400;
|
||||
|
||||
struct MinedTx {
|
||||
int64_t timestamp;
|
||||
double amount;
|
||||
int confirmations;
|
||||
bool mature;
|
||||
std::string txid;
|
||||
bool isPoolPayout;
|
||||
};
|
||||
std::vector<MinedTx> recentMined;
|
||||
|
||||
for (const auto& tx : state.transactions) {
|
||||
bool isSoloMined = (tx.type == "generate" || tx.type == "immature" || tx.type == "mined");
|
||||
bool isPoolPayout = (tx.type == "receive"
|
||||
&& !tx.memo.empty()
|
||||
&& tx.memo.find("Mining Pool payout") != std::string::npos);
|
||||
if (isSoloMined || isPoolPayout) {
|
||||
// Apply earnings filter
|
||||
if (!app->supportsSoloMining()) {
|
||||
if (s_earnings_filter == 1 && !isPoolPayout) continue;
|
||||
} else {
|
||||
if (s_earnings_filter == 1 && !isSoloMined) continue;
|
||||
if (s_earnings_filter == 2 && !isPoolPayout) continue;
|
||||
}
|
||||
|
||||
double amt = std::abs(tx.amount);
|
||||
minedAllTime += amt;
|
||||
minedAllTimeCount++;
|
||||
if (tx.timestamp >= todayStart) {
|
||||
minedToday += amt;
|
||||
minedTodayCount++;
|
||||
} else if (tx.timestamp >= yesterdayStart) {
|
||||
minedYesterday += amt;
|
||||
minedYesterdayCount++;
|
||||
}
|
||||
// Separate solo blocks from pool payouts based on current mode
|
||||
bool showInCurrentMode = s_pool_mode ? isPoolPayout : isSoloMined;
|
||||
if (showInCurrentMode && recentMined.size() < 4) {
|
||||
recentMined.push_back({tx.timestamp, amt, tx.confirmations, tx.confirmations >= 100, tx.txid, isPoolPayout});
|
||||
}
|
||||
}
|
||||
}
|
||||
static MiningEarningsCache s_earningsCache;
|
||||
static LocalDayStartMemo s_dayStart;
|
||||
MiningEarningsKey earnKey;
|
||||
earnKey.txRevision = state.tx_revision;
|
||||
earnKey.txCount = state.transactions.size();
|
||||
earnKey.filter = s_earnings_filter;
|
||||
earnKey.soloSupported = app->supportsSoloMining();
|
||||
earnKey.poolMode = s_pool_mode;
|
||||
earnKey.todayStart = s_dayStart.get(now);
|
||||
const int64_t nowMs = (int64_t)std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
const MiningEarningsTotals& earn = s_earningsCache.get(earnKey, state.transactions, nowMs);
|
||||
// The names the (unchanged) rendering code below already uses.
|
||||
const double minedToday = earn.today, minedYesterday = earn.yesterday, minedAllTime = earn.allTime;
|
||||
const int minedTodayCount = earn.todayCount, minedYesterdayCount = earn.yesterdayCount,
|
||||
minedAllTimeCount = earn.allTimeCount;
|
||||
const std::vector<MiningEarningsRecentTx>& recentMined = earn.recent;
|
||||
|
||||
// Est. Daily = expected reward for YOUR own hashrate share of the network. In pool mode this uses
|
||||
// your local miner rate (pool_mining.hashrate_10s), NOT the pool's aggregate rate: pool payouts are
|
||||
|
||||
Reference in New Issue
Block a user