{"version":3,"file":"index.cjs","names":[],"sources":["../../src/utils/worker-performance-tracker.ts"],"sourcesContent":["import { PerformanceTimeEntry } from '@awesome-ecs/abstract/utils';\r\nimport {\r\n  WorkerMetricEntry,\r\n  WorkerMetricsSummary,\r\n  WorkerSnapshot\r\n} from '../abstract/worker-performance';\r\n\r\ntype AccEntry = {\r\n  count: number;\r\n  totalMs: number;\r\n  minMs: number;\r\n  maxMs: number;\r\n  lastMs: number;\r\n};\r\n\r\n/**\r\n * Tracks performance metrics for worker operations, allowing for aggregation and analysis of execution times across different worker tasks.\r\n * Metrics are sampled at regular intervals and can be retrieved in summary form for overall or per-worker insights.\r\n *\r\n * @injectable\r\n */\r\nexport class WorkerPerformanceTracker {\r\n  static readonly MAX_SNAPSHOTS = 120;\r\n  static readonly SAMPLE_INTERVAL_MS = 250;\r\n\r\n  private readonly _snapshots: WorkerSnapshot[] = [];\r\n  private readonly _pendingMetrics = new Map<string, WorkerMetricEntry>();\r\n  private readonly _perWorkerPending = new Map<string, Map<string, WorkerMetricEntry>>();\r\n\r\n  private readonly _aggregate = new Map<string, AccEntry>();\r\n  private readonly _perWorkerAggregate = new Map<string, Map<string, AccEntry>>();\r\n\r\n  private _pendingMaxQueueSize = 0;\r\n  private _pendingStalledCount = 0;\r\n  private _pendingWaitTimes: number[] = [];\r\n  private _lastSampleTime = 0;\r\n  private _enabled = false;\r\n\r\n  private _aggregateDirty = true;\r\n  private _cachedSummaries: WorkerMetricsSummary[] = [];\r\n  private _perWorkerDirty = true;\r\n  private _cachedPerWorker = new Map<string, WorkerMetricsSummary[]>();\r\n\r\n  get snapshots(): ReadonlyArray<WorkerSnapshot> {\r\n    return this._snapshots;\r\n  }\r\n\r\n  get enabled(): boolean {\r\n    return this._enabled;\r\n  }\r\n\r\n  set enabled(value: boolean) {\r\n    this._enabled = value;\r\n  }\r\n\r\n  get queueHistory(): number[] {\r\n    return this._snapshots.map((s) => s.queueSize);\r\n  }\r\n\r\n  get stalledHistory(): number[] {\r\n    return this._snapshots.map((s) => s.stalledCount);\r\n  }\r\n\r\n  get waitTimeHistory(): number[] {\r\n    return this._snapshots.map((s) => s.avgWaitTimeMs);\r\n  }\r\n\r\n  get eventTypeHistories(): Map<string, number[]> {\r\n    const allNames = new Set<string>();\r\n    for (const snap of this._snapshots) {\r\n      for (const m of snap.metrics) allNames.add(m.name);\r\n    }\r\n\r\n    const result = new Map<string, number[]>();\r\n    for (const name of allNames) {\r\n      result.set(\r\n        name,\r\n        this._snapshots.map((snap) => {\r\n          const m = snap.metrics.find((e) => e.name === name);\r\n          return m && m.count > 0 ? m.totalMs / m.count : 0;\r\n        })\r\n      );\r\n    }\r\n    return result;\r\n  }\r\n\r\n  get summaries(): WorkerMetricsSummary[] {\r\n    if (!this._aggregateDirty) return this._cachedSummaries;\r\n\r\n    const result: WorkerMetricsSummary[] = [];\r\n    for (const [name, data] of this._aggregate) {\r\n      result.push({\r\n        name,\r\n        count: data.count,\r\n        totalMs: data.totalMs,\r\n        avgMs: data.count > 0 ? data.totalMs / data.count : 0,\r\n        minMs: data.minMs,\r\n        maxMs: data.maxMs,\r\n        lastMs: data.lastMs\r\n      });\r\n    }\r\n    result.sort((a, b) => b.totalMs - a.totalMs);\r\n    this._cachedSummaries = result;\r\n    this._aggregateDirty = false;\r\n    return result;\r\n  }\r\n\r\n  get perWorkerSummaries(): Map<string, WorkerMetricsSummary[]> {\r\n    if (!this._perWorkerDirty) return this._cachedPerWorker;\r\n\r\n    const result = new Map<string, WorkerMetricsSummary[]>();\r\n    for (const [uid, aggMap] of this._perWorkerAggregate) {\r\n      const entries: WorkerMetricsSummary[] = [];\r\n      for (const [name, data] of aggMap) {\r\n        entries.push({\r\n          name,\r\n          count: data.count,\r\n          totalMs: data.totalMs,\r\n          avgMs: data.count > 0 ? data.totalMs / data.count : 0,\r\n          minMs: data.minMs,\r\n          maxMs: data.maxMs,\r\n          lastMs: data.lastMs\r\n        });\r\n      }\r\n      entries.sort((a, b) => b.totalMs - a.totalMs);\r\n      result.set(uid, entries);\r\n    }\r\n    this._cachedPerWorker = result;\r\n    this._perWorkerDirty = false;\r\n    return result;\r\n  }\r\n\r\n  recordQueueSize(size: number): void {\r\n    if (!this._enabled) return;\r\n    this._pendingMaxQueueSize = Math.max(this._pendingMaxQueueSize, size);\r\n  }\r\n\r\n  recordStalledCount(count: number): void {\r\n    if (!this._enabled) return;\r\n    this._pendingStalledCount = Math.max(this._pendingStalledCount, count);\r\n  }\r\n\r\n  recordWaitTimes(times: number[]): void {\r\n    if (!this._enabled) return;\r\n    for (const t of times) {\r\n      this._pendingWaitTimes.push(t);\r\n    }\r\n  }\r\n\r\n  addMetric(workerUid: string, entry: PerformanceTimeEntry): void {\r\n    if (!this._enabled) return;\r\n    const ms = entry.msPassed ?? 0;\r\n    const name = entry.name;\r\n\r\n    // Aggregate pending\r\n    const existing = this._pendingMetrics.get(name);\r\n    if (existing) {\r\n      existing.totalMs += ms;\r\n      existing.count += 1;\r\n    } else {\r\n      this._pendingMetrics.set(name, { name, totalMs: ms, count: 1 });\r\n    }\r\n\r\n    // Per-worker pending\r\n    let workerMap = this._perWorkerPending.get(workerUid);\r\n    if (!workerMap) {\r\n      workerMap = new Map();\r\n      this._perWorkerPending.set(workerUid, workerMap);\r\n    }\r\n    const workerExisting = workerMap.get(name);\r\n    if (workerExisting) {\r\n      workerExisting.totalMs += ms;\r\n      workerExisting.count += 1;\r\n    } else {\r\n      workerMap.set(name, { name, totalMs: ms, count: 1 });\r\n    }\r\n  }\r\n\r\n  addMetrics(workerUid: string, entries: PerformanceTimeEntry[]): void {\r\n    for (const entry of entries) {\r\n      this.addMetric(workerUid, entry);\r\n    }\r\n  }\r\n\r\n  sample(now?: number): void {\r\n    if (!this._enabled) return;\r\n    const timestamp = now ?? performance.now();\r\n\r\n    if (\r\n      this._lastSampleTime !== 0 &&\r\n      timestamp - this._lastSampleTime < WorkerPerformanceTracker.SAMPLE_INTERVAL_MS\r\n    ) {\r\n      return;\r\n    }\r\n\r\n    const metrics = Array.from(this._pendingMetrics.values());\r\n\r\n    const avgWaitTimeMs =\r\n      this._pendingWaitTimes.length > 0\r\n        ? this._pendingWaitTimes.reduce((s, t) => s + t, 0) / this._pendingWaitTimes.length\r\n        : 0;\r\n\r\n    const snapshot: WorkerSnapshot = {\r\n      timestamp,\r\n      queueSize: this._pendingMaxQueueSize,\r\n      stalledCount: this._pendingStalledCount,\r\n      avgWaitTimeMs,\r\n      metrics\r\n    };\r\n\r\n    this._snapshots.push(snapshot);\r\n    if (this._snapshots.length > WorkerPerformanceTracker.MAX_SNAPSHOTS) {\r\n      this._snapshots.shift();\r\n    }\r\n\r\n    // Merge into aggregate summaries\r\n    this.mergeIntoAggregate(metrics);\r\n    this.mergePerWorkerAggregate();\r\n\r\n    // Reset pending\r\n    this._pendingMetrics.clear();\r\n    this._perWorkerPending.clear();\r\n    this._pendingMaxQueueSize = 0;\r\n    this._pendingStalledCount = 0;\r\n    this._pendingWaitTimes = [];\r\n    this._lastSampleTime = timestamp;\r\n  }\r\n\r\n  clear(): void {\r\n    this._snapshots.length = 0;\r\n    this._pendingMetrics.clear();\r\n    this._perWorkerPending.clear();\r\n    this._aggregate.clear();\r\n    this._perWorkerAggregate.clear();\r\n    this._pendingMaxQueueSize = 0;\r\n    this._pendingStalledCount = 0;\r\n    this._pendingWaitTimes = [];\r\n    this._lastSampleTime = 0;\r\n    this._aggregateDirty = true;\r\n    this._perWorkerDirty = true;\r\n    this._cachedSummaries = [];\r\n    this._cachedPerWorker = new Map();\r\n  }\r\n\r\n  private mergeIntoAggregate(metrics: WorkerMetricEntry[]): void {\r\n    for (const m of metrics) {\r\n      this.mergeEntry(this._aggregate, m.name, m);\r\n    }\r\n    this._aggregateDirty = true;\r\n  }\r\n\r\n  private mergePerWorkerAggregate(): void {\r\n    for (const [uid, workerMap] of this._perWorkerPending) {\r\n      const aggMap = this.getOrCreateWorkerAgg(uid);\r\n      for (const [name, m] of workerMap) {\r\n        this.mergeEntry(aggMap, name, m);\r\n      }\r\n    }\r\n    this._perWorkerDirty = true;\r\n  }\r\n\r\n  private getOrCreateWorkerAgg(uid: string): Map<string, AccEntry> {\r\n    let aggMap = this._perWorkerAggregate.get(uid);\r\n    if (!aggMap) {\r\n      aggMap = new Map();\r\n      this._perWorkerAggregate.set(uid, aggMap);\r\n    }\r\n    return aggMap;\r\n  }\r\n\r\n  private mergeEntry(aggMap: Map<string, AccEntry>, name: string, m: WorkerMetricEntry): void {\r\n    const avg = m.count > 0 ? m.totalMs / m.count : 0;\r\n    const existing = aggMap.get(name);\r\n    if (existing) {\r\n      existing.count += m.count;\r\n      existing.totalMs += m.totalMs;\r\n      existing.minMs = Math.min(avg, existing.minMs);\r\n      existing.maxMs = Math.max(avg, existing.maxMs);\r\n      existing.lastMs = avg;\r\n    } else {\r\n      aggMap.set(name, { count: m.count, totalMs: m.totalMs, minMs: avg, maxMs: avg, lastMs: avg });\r\n    }\r\n  }\r\n}\r\n"],"mappings":"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"}