using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace StardewModdingAPI.Framework.PerformanceCounter
{
internal class PerformanceCounterCollection
{
/// The list of triggered performance counters.
private readonly List TriggeredPerformanceCounters = new List();
/// The stopwatch used to track the invocation time.
private readonly Stopwatch InvocationStopwatch = new Stopwatch();
/// The performance counter manager.
private readonly PerformanceCounterManager PerformanceCounterManager;
/// Holds the time to calculate the average calls per second.
private DateTime CallsPerSecondStart = DateTime.UtcNow;
/// The number of invocations of this collection.
private long CallCount;
public IDictionary PerformanceCounters { get; } = new Dictionary();
/// The name of this collection.
public string Name { get; }
/// Flag if this collection is important (used for the console summary command).
public bool IsImportant { get; }
/// The alert threshold in milliseconds.
public double AlertThresholdMilliseconds { get; set; }
/// If alerting is enabled or not
public bool EnableAlerts { get; set; }
public PerformanceCounterCollection(PerformanceCounterManager performanceCounterManager, string name, bool isImportant)
{
this.Name = name;
this.PerformanceCounterManager = performanceCounterManager;
this.IsImportant = isImportant;
}
public PerformanceCounterCollection(PerformanceCounterManager performanceCounterManager, string name)
{
this.PerformanceCounterManager = performanceCounterManager;
this.Name = name;
}
/// Tracks a single invocation for a named source.
/// The name of the source.
/// The entry.
public void Track(string source, PerformanceCounterEntry entry)
{
if (!this.PerformanceCounters.ContainsKey(source))
this.PerformanceCounters.Add(source, new PerformanceCounter(this, source));
this.PerformanceCounters[source].Add(entry);
if (this.EnableAlerts)
this.TriggeredPerformanceCounters.Add(new AlertContext(source, entry.ElapsedMilliseconds));
}
/// Returns the average execution time for all non-game internal sources.
/// The average execution time in milliseconds
public double GetModsAverageExecutionTime()
{
return this.PerformanceCounters.Where(p =>
p.Key != Constants.GamePerformanceCounterName).Sum(p => p.Value.GetAverage());
}
/// Returns the overall average execution time.
/// The average execution time in milliseconds
public double GetAverageExecutionTime()
{
return this.PerformanceCounters.Sum(p => p.Value.GetAverage());
}
/// Returns the average execution time for game-internal sources.
/// The average execution time in milliseconds
public double GetGameAverageExecutionTime()
{
if (this.PerformanceCounters.TryGetValue(Constants.GamePerformanceCounterName, out PerformanceCounter gameExecTime))
return gameExecTime.GetAverage();
return 0;
}
/// Begins tracking the invocation of this collection.
public void BeginTrackInvocation()
{
if (this.EnableAlerts)
{
this.TriggeredPerformanceCounters.Clear();
this.InvocationStopwatch.Reset();
this.InvocationStopwatch.Start();
}
this.CallCount++;
}
/// Ends tracking the invocation of this collection. Also records an alert if alerting is enabled
/// and the invocation time exceeds the threshold.
public void EndTrackInvocation()
{
if (!this.EnableAlerts) return;
this.InvocationStopwatch.Stop();
if (this.InvocationStopwatch.Elapsed.TotalMilliseconds >= this.AlertThresholdMilliseconds)
this.AddAlert(this.InvocationStopwatch.Elapsed.TotalMilliseconds,
this.AlertThresholdMilliseconds, this.TriggeredPerformanceCounters);
}
/// Adds an alert.
/// The execution time in milliseconds.
/// The configured threshold.
/// The list of alert contexts.
public void AddAlert(double executionTimeMilliseconds, double thresholdMilliseconds, List alerts)
{
this.PerformanceCounterManager.AddAlert(new AlertEntry(this, executionTimeMilliseconds,
thresholdMilliseconds, alerts));
}
/// Adds an alert for a single AlertContext
/// The execution time in milliseconds.
/// The configured threshold.
/// The context
public void AddAlert(double executionTimeMilliseconds, double thresholdMilliseconds, AlertContext alert)
{
this.AddAlert(executionTimeMilliseconds, thresholdMilliseconds, new List() {alert});
}
/// Resets the calls per second counter.
public void ResetCallsPerSecond()
{
this.CallCount = 0;
this.CallsPerSecondStart = DateTime.UtcNow;
}
/// Resets all performance counters in this collection.
public void Reset()
{
foreach (var i in this.PerformanceCounters)
i.Value.Reset();
}
/// Resets the performance counter for a specific source.
/// The source name
public void ResetSource(string source)
{
foreach (var i in this.PerformanceCounters)
if (i.Value.Source.Equals(source, StringComparison.InvariantCultureIgnoreCase))
i.Value.Reset();
}
/// Returns the average calls per second.
/// The average calls per second.
public long GetAverageCallsPerSecond()
{
long runtimeInSeconds = (long) DateTime.UtcNow.Subtract(this.CallsPerSecondStart).TotalSeconds;
if (runtimeInSeconds == 0) return 0;
return this.CallCount / runtimeInSeconds;
}
}
}