Cut idle CPU with a busy cluster feed and many windows open
Found by tracing the app with a fake skimmer feed, a fake rigctld and eight tool windows open (about 3,500 spots on the bandmap). - Drop expired spots from the one-second spot timer instead of from each window's refresh on the UI thread. - Bandmap keeps the lists On and All return until the spots change, and Near searches only the band the frequency is in. - The Available window replaces its rows at most every two seconds, the Score window redraws at most once a second, and the Telnet list takes new lines four times a second at most (RateLimited). - The bandmap leader line pen is made once. Idle CPU in that setup went from about 33-70% of one core to about 16-25%. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,13 @@ public sealed class Bandmap
|
||||
private readonly Lock guard = new();
|
||||
private readonly Func<DateTime> clock;
|
||||
|
||||
/// The lists `On` and `All` hand out, kept until the next change. The
|
||||
/// windows ask on every refresh, a refresh can come many times a second
|
||||
/// while the spots change once a second, and each answer is a scan and a
|
||||
/// sort of every spot.
|
||||
private readonly Dictionary<Band, IReadOnlyList<Spot>> onBand = [];
|
||||
private IReadOnlyList<Spot>? everything;
|
||||
|
||||
public Bandmap(TimeSpan? lifetime = null, Func<DateTime>? clock = null)
|
||||
{
|
||||
Lifetime = lifetime ?? TimeSpan.FromHours(1);
|
||||
@@ -43,6 +50,7 @@ public sealed class Bandmap
|
||||
{
|
||||
spots[key] = spot;
|
||||
arrived[key] = clock();
|
||||
Forget();
|
||||
}
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
@@ -61,6 +69,7 @@ public sealed class Bandmap
|
||||
arrived[key] = clock();
|
||||
added++;
|
||||
}
|
||||
Forget();
|
||||
}
|
||||
if (added > 0)
|
||||
{
|
||||
@@ -76,6 +85,7 @@ public sealed class Bandmap
|
||||
{
|
||||
arrived.Remove(key);
|
||||
removed = spots.Remove(key);
|
||||
Forget();
|
||||
}
|
||||
if (removed)
|
||||
{
|
||||
@@ -99,6 +109,10 @@ public sealed class Bandmap
|
||||
arrived.Remove(key);
|
||||
}
|
||||
dropped = stale.Count;
|
||||
if (dropped > 0)
|
||||
{
|
||||
Forget();
|
||||
}
|
||||
}
|
||||
if (dropped > 0)
|
||||
{
|
||||
@@ -119,14 +133,26 @@ public sealed class Bandmap
|
||||
}
|
||||
}
|
||||
|
||||
/// Called under the lock by everything that changes the spots.
|
||||
private void Forget()
|
||||
{
|
||||
onBand.Clear();
|
||||
everything = null;
|
||||
}
|
||||
|
||||
public IReadOnlyList<Spot> On(Band band)
|
||||
{
|
||||
lock (guard)
|
||||
{
|
||||
return spots.Values
|
||||
.Where(s => s.Band == band)
|
||||
.OrderBy(s => s.Frequency.Hertz)
|
||||
.ToList();
|
||||
if (!onBand.TryGetValue(band, out IReadOnlyList<Spot>? found))
|
||||
{
|
||||
found = spots.Values
|
||||
.Where(s => s.Band == band)
|
||||
.OrderBy(s => s.Frequency.Hertz)
|
||||
.ToList();
|
||||
onBand[band] = found;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +160,7 @@ public sealed class Bandmap
|
||||
{
|
||||
lock (guard)
|
||||
{
|
||||
return spots.Values.OrderBy(s => s.Frequency.Hertz).ToList();
|
||||
return everything ??= spots.Values.OrderBy(s => s.Frequency.Hertz).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +172,12 @@ public sealed class Bandmap
|
||||
{
|
||||
Spot? best = null;
|
||||
long bestDistance = long.MaxValue;
|
||||
foreach (Spot spot in spots.Values)
|
||||
// only the band the frequency is in is searched, which is a few
|
||||
// hundred spots rather than all of them
|
||||
IEnumerable<Spot> candidates = Bands.ForFrequency(frequency) is { } band
|
||||
? On(band)
|
||||
: spots.Values;
|
||||
foreach (Spot spot in candidates)
|
||||
{
|
||||
long distance = Math.Abs(spot.Frequency.Hertz - frequency.Hertz);
|
||||
if (distance <= window.Hertz && distance < bestDistance)
|
||||
|
||||
Reference in New Issue
Block a user