From 0db5c786e0e20a6ebb5826444529a8d703862460 Mon Sep 17 00:00:00 2001 From: dhaatrik <90041791+dhaatrik@users.noreply.github.com> Date: Tue, 21 Apr 2026 20:12:14 +0000 Subject: [PATCH] Refactor TelemetrySystem to use a ring buffer Replaced `Array.prototype.push()` and `Array.prototype.shift()` with a circular array and `dataHead`/`dataCount` pointers to eliminate O(N) shift operations and reduce garbage collection pressure. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/ui/Telemetry.ts | 46 ++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/ui/Telemetry.ts b/src/ui/Telemetry.ts index 093a76e..0227896 100644 --- a/src/ui/Telemetry.ts +++ b/src/ui/Telemetry.ts @@ -15,12 +15,14 @@ export class TelemetrySystem { /** 2D rendering context */ private ctx: CanvasRenderingContext2D | null; - /** Recorded data points */ - private data: TelemetryDataPoint[] = []; - /** Maximum number of data points to store */ private readonly maxDataPoints: number = 300; + /** Recorded data points (Circular Buffer) */ + private data: TelemetryDataPoint[] = new Array(this.maxDataPoints); + private dataHead: number = 0; + private dataCount: number = 0; + /** Last sample time */ private lastSample: number = 0; @@ -54,16 +56,20 @@ export class TelemetrySystem { this.maxAltQueue.push(alt); this.maxVelQueue.push(vel); - this.data.push({ t: time, alt, vel }); - - // Limit data size - if (this.data.length > this.maxDataPoints) { - const removed = this.data.shift(); - + if (this.dataCount === this.maxDataPoints) { + const removed = this.data[this.dataHead]; if (removed) { this.maxAltQueue.pop(removed.alt); this.maxVelQueue.pop(removed.vel); } + // Overwrite the oldest + this.data[this.dataHead] = { t: time, alt, vel }; + this.dataHead = (this.dataHead + 1) % this.maxDataPoints; + } else { + // Add new + const index = (this.dataHead + this.dataCount) % this.maxDataPoints; + this.data[index] = { t: time, alt, vel }; + this.dataCount++; } // Update cached max values @@ -86,7 +92,7 @@ export class TelemetrySystem { this.ctx.clearRect(0, 0, w, h); - const len = this.data.length; + const len = this.dataCount; if (len < 2) return; const maxAlt = this.maxAlt; @@ -99,7 +105,8 @@ export class TelemetrySystem { const velPath = new Path2D(); for (let i = 0; i < len; i++) { - const d = this.data[i]; + const index = (this.dataHead + i) % this.maxDataPoints; + const d = this.data[index]; if (!d) continue; const x = i * xStep; @@ -129,7 +136,9 @@ export class TelemetrySystem { * Clear all recorded data */ clear(): void { - this.data = []; + // Keep the pre-allocated array, just reset counters + this.dataHead = 0; + this.dataCount = 0; this.maxAltQueue.clear(); this.maxVelQueue.clear(); this.lastSample = 0; @@ -139,15 +148,22 @@ export class TelemetrySystem { /** * Get current data + * Returns an array in correct chronological order */ - getData(): readonly TelemetryDataPoint[] { - return this.data; + getData(): TelemetryDataPoint[] { + const result = new Array(this.dataCount); + for (let i = 0; i < this.dataCount; i++) { + result[i] = this.data[(this.dataHead + i) % this.maxDataPoints]; + } + return result; } /** * Get latest data point */ getLatest(): TelemetryDataPoint | undefined { - return this.data[this.data.length - 1]; + if (this.dataCount === 0) return undefined; + const tailIndex = (this.dataHead + this.dataCount - 1) % this.maxDataPoints; + return this.data[tailIndex]; } }