UNPKG

@awesome-ecs/workers

Version:

The Workers package for Awesome ECS. Provides basic implementation for the Worker communication using ECS.

214 lines (213 loc) 6.67 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); //#region src/utils/worker-performance-tracker.ts /** * Tracks performance metrics for worker operations, allowing for aggregation and analysis of execution times across different worker tasks. * Metrics are sampled at regular intervals and can be retrieved in summary form for overall or per-worker insights. * * @injectable */ var WorkerPerformanceTracker = class WorkerPerformanceTracker { static MAX_SNAPSHOTS = 120; static SAMPLE_INTERVAL_MS = 250; _snapshots = []; _pendingMetrics = /* @__PURE__ */ new Map(); _perWorkerPending = /* @__PURE__ */ new Map(); _aggregate = /* @__PURE__ */ new Map(); _perWorkerAggregate = /* @__PURE__ */ new Map(); _pendingMaxQueueSize = 0; _pendingStalledCount = 0; _pendingWaitTimes = []; _lastSampleTime = 0; _enabled = false; _aggregateDirty = true; _cachedSummaries = []; _perWorkerDirty = true; _cachedPerWorker = /* @__PURE__ */ new Map(); get snapshots() { return this._snapshots; } get enabled() { return this._enabled; } set enabled(value) { this._enabled = value; } get queueHistory() { return this._snapshots.map((s) => s.queueSize); } get stalledHistory() { return this._snapshots.map((s) => s.stalledCount); } get waitTimeHistory() { return this._snapshots.map((s) => s.avgWaitTimeMs); } get eventTypeHistories() { const allNames = /* @__PURE__ */ new Set(); for (const snap of this._snapshots) for (const m of snap.metrics) allNames.add(m.name); const result = /* @__PURE__ */ new Map(); for (const name of allNames) result.set(name, this._snapshots.map((snap) => { const m = snap.metrics.find((e) => e.name === name); return m && m.count > 0 ? m.totalMs / m.count : 0; })); return result; } get summaries() { if (!this._aggregateDirty) return this._cachedSummaries; const result = []; for (const [name, data] of this._aggregate) result.push({ name, count: data.count, totalMs: data.totalMs, avgMs: data.count > 0 ? data.totalMs / data.count : 0, minMs: data.minMs, maxMs: data.maxMs, lastMs: data.lastMs }); result.sort((a, b) => b.totalMs - a.totalMs); this._cachedSummaries = result; this._aggregateDirty = false; return result; } get perWorkerSummaries() { if (!this._perWorkerDirty) return this._cachedPerWorker; const result = /* @__PURE__ */ new Map(); for (const [uid, aggMap] of this._perWorkerAggregate) { const entries = []; for (const [name, data] of aggMap) entries.push({ name, count: data.count, totalMs: data.totalMs, avgMs: data.count > 0 ? data.totalMs / data.count : 0, minMs: data.minMs, maxMs: data.maxMs, lastMs: data.lastMs }); entries.sort((a, b) => b.totalMs - a.totalMs); result.set(uid, entries); } this._cachedPerWorker = result; this._perWorkerDirty = false; return result; } recordQueueSize(size) { if (!this._enabled) return; this._pendingMaxQueueSize = Math.max(this._pendingMaxQueueSize, size); } recordStalledCount(count) { if (!this._enabled) return; this._pendingStalledCount = Math.max(this._pendingStalledCount, count); } recordWaitTimes(times) { if (!this._enabled) return; for (const t of times) this._pendingWaitTimes.push(t); } addMetric(workerUid, entry) { if (!this._enabled) return; const ms = entry.msPassed ?? 0; const name = entry.name; const existing = this._pendingMetrics.get(name); if (existing) { existing.totalMs += ms; existing.count += 1; } else this._pendingMetrics.set(name, { name, totalMs: ms, count: 1 }); let workerMap = this._perWorkerPending.get(workerUid); if (!workerMap) { workerMap = /* @__PURE__ */ new Map(); this._perWorkerPending.set(workerUid, workerMap); } const workerExisting = workerMap.get(name); if (workerExisting) { workerExisting.totalMs += ms; workerExisting.count += 1; } else workerMap.set(name, { name, totalMs: ms, count: 1 }); } addMetrics(workerUid, entries) { for (const entry of entries) this.addMetric(workerUid, entry); } sample(now) { if (!this._enabled) return; const timestamp = now ?? performance.now(); if (this._lastSampleTime !== 0 && timestamp - this._lastSampleTime < WorkerPerformanceTracker.SAMPLE_INTERVAL_MS) return; const metrics = Array.from(this._pendingMetrics.values()); const avgWaitTimeMs = this._pendingWaitTimes.length > 0 ? this._pendingWaitTimes.reduce((s, t) => s + t, 0) / this._pendingWaitTimes.length : 0; const snapshot = { timestamp, queueSize: this._pendingMaxQueueSize, stalledCount: this._pendingStalledCount, avgWaitTimeMs, metrics }; this._snapshots.push(snapshot); if (this._snapshots.length > WorkerPerformanceTracker.MAX_SNAPSHOTS) this._snapshots.shift(); this.mergeIntoAggregate(metrics); this.mergePerWorkerAggregate(); this._pendingMetrics.clear(); this._perWorkerPending.clear(); this._pendingMaxQueueSize = 0; this._pendingStalledCount = 0; this._pendingWaitTimes = []; this._lastSampleTime = timestamp; } clear() { this._snapshots.length = 0; this._pendingMetrics.clear(); this._perWorkerPending.clear(); this._aggregate.clear(); this._perWorkerAggregate.clear(); this._pendingMaxQueueSize = 0; this._pendingStalledCount = 0; this._pendingWaitTimes = []; this._lastSampleTime = 0; this._aggregateDirty = true; this._perWorkerDirty = true; this._cachedSummaries = []; this._cachedPerWorker = /* @__PURE__ */ new Map(); } mergeIntoAggregate(metrics) { for (const m of metrics) this.mergeEntry(this._aggregate, m.name, m); this._aggregateDirty = true; } mergePerWorkerAggregate() { for (const [uid, workerMap] of this._perWorkerPending) { const aggMap = this.getOrCreateWorkerAgg(uid); for (const [name, m] of workerMap) this.mergeEntry(aggMap, name, m); } this._perWorkerDirty = true; } getOrCreateWorkerAgg(uid) { let aggMap = this._perWorkerAggregate.get(uid); if (!aggMap) { aggMap = /* @__PURE__ */ new Map(); this._perWorkerAggregate.set(uid, aggMap); } return aggMap; } mergeEntry(aggMap, name, m) { const avg = m.count > 0 ? m.totalMs / m.count : 0; const existing = aggMap.get(name); if (existing) { existing.count += m.count; existing.totalMs += m.totalMs; existing.minMs = Math.min(avg, existing.minMs); existing.maxMs = Math.max(avg, existing.maxMs); existing.lastMs = avg; } else aggMap.set(name, { count: m.count, totalMs: m.totalMs, minMs: avg, maxMs: avg, lastMs: avg }); } }; //#endregion exports.WorkerPerformanceTracker = WorkerPerformanceTracker; //# sourceMappingURL=index.cjs.map