using System; using System.Diagnostics; using System.Linq; using Cyotek.Collections.Generic; using StardewModdingAPI.Framework.Utilities; namespace StardewModdingAPI.Framework.PerformanceCounter { internal class PerformanceCounter { private const int MAX_ENTRIES = 16384; public string Source { get; } public static Stopwatch Stopwatch = new Stopwatch(); public static long TotalNumEventsLogged; public double MonitorThresholdMilliseconds { get; set; } public bool Monitor { get; set; } private readonly PerformanceCounterCollection ParentCollection; private readonly CircularBuffer _counter; private PerformanceCounterEntry? PeakPerformanceCounterEntry; public PerformanceCounter(PerformanceCounterCollection parentCollection, string source) { this.ParentCollection = parentCollection; this.Source = source; this._counter = new CircularBuffer(PerformanceCounter.MAX_ENTRIES); } public void Reset() { this._counter.Clear(); this.PeakPerformanceCounterEntry = null; } public int GetAverageCallsPerSecond() { var x = this._counter.GroupBy( p => (int) p.EventTime.Subtract( new DateTime(1970, 1, 1) ).TotalSeconds); return x.Last().Count(); } public void Add(PerformanceCounterEntry entry) { PerformanceCounter.Stopwatch.Start(); this._counter.Put(entry); if (this.Monitor && entry.Elapsed.TotalMilliseconds > this.MonitorThresholdMilliseconds) { this.ParentCollection.AddAlert(entry.Elapsed.TotalMilliseconds, this.MonitorThresholdMilliseconds, new AlertContext(this.Source, entry.Elapsed.TotalMilliseconds)); } if (this.PeakPerformanceCounterEntry == null) { this.PeakPerformanceCounterEntry = entry; } else { if (entry.Elapsed.TotalMilliseconds > this.PeakPerformanceCounterEntry.Value.Elapsed.TotalMilliseconds) { this.PeakPerformanceCounterEntry = entry; } } PerformanceCounter.Stopwatch.Stop(); PerformanceCounter.TotalNumEventsLogged++; } public PerformanceCounterEntry? GetPeak() { return this.PeakPerformanceCounterEntry; } public void ResetPeak() { this.PeakPerformanceCounterEntry = null; } public PerformanceCounterEntry? GetLastEntry() { if (this._counter.IsEmpty) { return null; } return this._counter.PeekLast(); } public double GetAverage() { if (this._counter.IsEmpty) { return 0; } return this._counter.Average(p => p.Elapsed.TotalMilliseconds); } public double GetAverage(TimeSpan range) { if (this._counter.IsEmpty) { return 0; } var lastTime = this._counter.Max(x => x.EventTime); var start = lastTime.Subtract(range); var entries = this._counter.Where(x => (x.EventTime >= start) && (x.EventTime <= lastTime)); return entries.Average(x => x.Elapsed.TotalMilliseconds); } } }