From 96ac1ee65da31055704e0a14cafacc33914d7e86 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 30 Sep 2026 19:56:48 +0200 Subject: perf(dashboard): draw at 0.1s only while the dashboard is visible Conky gives Lua no way to request one redraw, so a scroll showed up to 2s late. Pointer enter and leave, which Hyprland sends on every toggle of the special workspace, now switch conky_set_update_interval between 0.1s and the config's 2s. At that rate the CPU and network counters would sample over 0.1s windows and the chart would fill 20x faster, so both now sample through data.new_gate on the /proc/uptime clock, about every 2s, and redraw their last values in between. The samplers move to texeci: execi runs in the main loop and a 0.7s nvtable or an Immich download stalled the scroll. Measured: about 12% of one core while visible, 0.5% while hidden. Co-Authored-By: Claude Opus 5.5 --- widgets/system.lua | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'widgets/system.lua') diff --git a/widgets/system.lua b/widgets/system.lua index 98e0ae1..c1cdf67 100644 --- a/widgets/system.lua +++ b/widgets/system.lua @@ -27,6 +27,11 @@ local TEMPS = { local counters = {} local total_counter = data.new_cpu_counter() +-- Sampled every ~2s whatever the draw rate; the values between samples are +-- the last ones read. 1.9 not 2: draws jitter by a few ms either way. +local gate = data.new_gate(1.9) +local load, pcts = nil, {} + -- Read once, not per frame. Neither can change without a reboot, and -- re-reading /proc/cpuinfo every two seconds for a constant is waste: the file -- repeats its model-name line once per thread. @@ -51,11 +56,15 @@ function M.draw(cr, rect, colors) local inner = card.card(cr, rect, colors) local function clamp(v, lo, hi) return math.max(lo, math.min(hi, v)) end - local stat = data.slurp('/proc/stat') - local agg_total, agg_idle = data.cpu_times(stat or '') - local load = total_counter:sample(agg_total, agg_idle) + if gate() then + local stat = data.slurp('/proc/stat') or '' + load = total_counter:sample(data.cpu_times(stat)) + for i, c in ipairs(data.per_cpu_times(stat)) do + counters[i] = counters[i] or data.new_cpu_counter() + pcts[i] = counters[i]:sample(c.total, c.idle) or 0 + end + end local load_txt = load and string.format('%d%%', math.floor(load + 0.5)) or '--' - local cores = data.per_cpu_times(stat or '') local temp_n = #TEMPS local cpu_txt, board_txt = hardware() @@ -137,16 +146,14 @@ function M.draw(cr, rect, colors) -- legitimate outcome: the guard below skips the band when it has no height. local eh = clamp((inner.y + inner.h) - eq_top - (S * 1.13 + temp_n * step), 0, math.huge) - local n = #cores + local n = #pcts if n > 0 and eh >= 8 then local gap = math.max(2, inner.w * 0.006) local bw = (inner.w - gap * (n - 1)) / n -- Below about 3px a row of sixteen bars is an illegible smear; drop to the -- aggregate bar instead of drawing one. if bw >= 3 then - for i, c in ipairs(cores) do - counters[i] = counters[i] or data.new_cpu_counter() - local pct = counters[i]:sample(c.total, c.idle) or 0 + for i, pct in ipairs(pcts) do card.vbar(cr, inner.x + (i - 1) * (bw + gap), eq_top + eh, bw, eh, pct / 100, card.threshold(pct, 25, 75, colors), colors) end -- cgit v1.2.3