diff options
Diffstat (limited to 'src/SMAPI/Framework/PerformanceCounter')
5 files changed, 227 insertions, 0 deletions
diff --git a/src/SMAPI/Framework/PerformanceCounter/EventPerformanceCounterCategory.cs b/src/SMAPI/Framework/PerformanceCounter/EventPerformanceCounterCategory.cs new file mode 100644 index 00000000..14f74317 --- /dev/null +++ b/src/SMAPI/Framework/PerformanceCounter/EventPerformanceCounterCategory.cs @@ -0,0 +1,16 @@ +namespace StardewModdingAPI.Framework.Utilities +{ + public class EventPerformanceCounterCategory + { + public IPerformanceCounterEvent Event { get; } + public double MonitorThreshold { get; } + public bool IsImportant { get; } + public bool Monitor { get; } + + public EventPerformanceCounterCategory(IPerformanceCounterEvent @event, bool isImportant) + { + this.Event = @event; + this.IsImportant = isImportant; + } + } +} diff --git a/src/SMAPI/Framework/PerformanceCounter/IPerformanceCounterEvent.cs b/src/SMAPI/Framework/PerformanceCounter/IPerformanceCounterEvent.cs new file mode 100644 index 00000000..6b83586d --- /dev/null +++ b/src/SMAPI/Framework/PerformanceCounter/IPerformanceCounterEvent.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace StardewModdingAPI.Framework.Utilities +{ + public interface IPerformanceCounterEvent + { + string GetEventName(); + long GetAverageCallsPerSecond(); + IDictionary<string, PerformanceCounter.PerformanceCounter> PerformanceCounters { get; } + + double GetGameAverageExecutionTime(); + double GetModsAverageExecutionTime(); + double GetAverageExecutionTime(); + } +} diff --git a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounter.cs b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounter.cs new file mode 100644 index 00000000..04e0f5f5 --- /dev/null +++ b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounter.cs @@ -0,0 +1,103 @@ +using System; +using System.Diagnostics; +using System.Linq; +using Cyotek.Collections.Generic; +using StardewModdingAPI.Framework.Utilities; + +namespace StardewModdingAPI.Framework.PerformanceCounter +{ + public class PerformanceCounter + { + private const int MAX_ENTRIES = 16384; + + public string Name { get; } + public static Stopwatch Stopwatch = new Stopwatch(); + public static long TotalNumEventsLogged; + + + private readonly CircularBuffer<PerformanceCounterEntry> _counter; + + private PerformanceCounterEntry? PeakPerformanceCounterEntry; + + public PerformanceCounter(string name) + { + this.Name = name; + this._counter = new CircularBuffer<PerformanceCounterEntry>(PerformanceCounter.MAX_ENTRIES); + } + + 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.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); + } + } +} diff --git a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterEntry.cs b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterEntry.cs new file mode 100644 index 00000000..8e156a32 --- /dev/null +++ b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterEntry.cs @@ -0,0 +1,10 @@ +using System; + +namespace StardewModdingAPI.Framework.Utilities +{ + public struct PerformanceCounterEntry + { + public DateTime EventTime; + public TimeSpan Elapsed; + } +} diff --git a/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterManager.cs b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterManager.cs new file mode 100644 index 00000000..e2200e74 --- /dev/null +++ b/src/SMAPI/Framework/PerformanceCounter/PerformanceCounterManager.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using StardewModdingAPI.Framework.Events; +using StardewModdingAPI.Framework.Utilities; + +namespace StardewModdingAPI.Framework.PerformanceCounter +{ + internal class PerformanceCounterManager + { + public HashSet<EventPerformanceCounterCategory> PerformanceCounterEvents = new HashSet<EventPerformanceCounterCategory>(); + + private readonly EventManager EventManager; + + public PerformanceCounterManager(EventManager eventManager) + { + this.EventManager = eventManager; + this.InitializePerformanceCounterEvents(); + } + + private void InitializePerformanceCounterEvents() + { + this.PerformanceCounterEvents = new HashSet<EventPerformanceCounterCategory>() + { + new EventPerformanceCounterCategory(this.EventManager.MenuChanged, false), + + // Rendering Events + new EventPerformanceCounterCategory(this.EventManager.Rendering, true), + new EventPerformanceCounterCategory(this.EventManager.Rendered, true), + new EventPerformanceCounterCategory(this.EventManager.RenderingWorld, true), + new EventPerformanceCounterCategory(this.EventManager.RenderedWorld, true), + new EventPerformanceCounterCategory(this.EventManager.RenderingActiveMenu, true), + new EventPerformanceCounterCategory(this.EventManager.RenderedActiveMenu, true), + new EventPerformanceCounterCategory(this.EventManager.RenderingHud, true), + new EventPerformanceCounterCategory(this.EventManager.RenderedHud, true), + + new EventPerformanceCounterCategory(this.EventManager.WindowResized, false), + new EventPerformanceCounterCategory(this.EventManager.GameLaunched, false), + new EventPerformanceCounterCategory(this.EventManager.UpdateTicking, true), + new EventPerformanceCounterCategory(this.EventManager.UpdateTicked, true), + new EventPerformanceCounterCategory(this.EventManager.OneSecondUpdateTicking, true), + new EventPerformanceCounterCategory(this.EventManager.OneSecondUpdateTicked, true), + + new EventPerformanceCounterCategory(this.EventManager.SaveCreating, false), + new EventPerformanceCounterCategory(this.EventManager.SaveCreated, false), + new EventPerformanceCounterCategory(this.EventManager.Saving, false), + new EventPerformanceCounterCategory(this.EventManager.Saved, false), + + new EventPerformanceCounterCategory(this.EventManager.DayStarted, false), + new EventPerformanceCounterCategory(this.EventManager.DayEnding, false), + + new EventPerformanceCounterCategory(this.EventManager.TimeChanged, true), + + new EventPerformanceCounterCategory(this.EventManager.ReturnedToTitle, false), + + new EventPerformanceCounterCategory(this.EventManager.ButtonPressed, true), + new EventPerformanceCounterCategory(this.EventManager.ButtonReleased, true), + new EventPerformanceCounterCategory(this.EventManager.CursorMoved, true), + new EventPerformanceCounterCategory(this.EventManager.MouseWheelScrolled, true), + + new EventPerformanceCounterCategory(this.EventManager.PeerContextReceived, true), + new EventPerformanceCounterCategory(this.EventManager.ModMessageReceived, true), + new EventPerformanceCounterCategory(this.EventManager.PeerDisconnected, true), + new EventPerformanceCounterCategory(this.EventManager.InventoryChanged, true), + new EventPerformanceCounterCategory(this.EventManager.LevelChanged, true), + new EventPerformanceCounterCategory(this.EventManager.Warped, true), + + new EventPerformanceCounterCategory(this.EventManager.LocationListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.BuildingListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.LocationListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.DebrisListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.LargeTerrainFeatureListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.NpcListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.ObjectListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.ChestInventoryChanged, true), + new EventPerformanceCounterCategory(this.EventManager.TerrainFeatureListChanged, true), + new EventPerformanceCounterCategory(this.EventManager.LoadStageChanged, false), + new EventPerformanceCounterCategory(this.EventManager.UnvalidatedUpdateTicking, true), + new EventPerformanceCounterCategory(this.EventManager.UnvalidatedUpdateTicked, true), + + }; + } + } +} |