using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace Swan.Diagnostics { /// /// A simple benchmarking class. /// /// /// The following code demonstrates how to create a simple benchmark. /// /// namespace Examples.Benchmark.Simple /// { /// using Swan.Diagnostics; /// /// public class SimpleBenchmark /// { /// public static void Main() /// { /// using (Benchmark.Start("Test")) /// { /// // do some logic in here /// } /// /// // dump results into a string /// var results = Benchmark.Dump(); /// } /// } /// /// } /// /// public static partial class Benchmark { private static readonly Object SyncLock = new Object(); private static readonly Dictionary> Measures = new Dictionary>(); /// /// Starts measuring with the given identifier. /// /// The identifier. /// A disposable object that when disposed, adds a benchmark result. public static IDisposable Start(String identifier) => new BenchmarkUnit(identifier); /// /// Outputs the benchmark statistics. /// /// A string containing human-readable statistics. public static String Dump() { StringBuilder builder = new StringBuilder(); lock(SyncLock) { foreach(KeyValuePair> kvp in Measures) { _ = builder.Append($"BID: {kvp.Key,-30} | ").Append($"CNT: {kvp.Value.Count,6} | ").Append($"AVG: {kvp.Value.Average(t => t.TotalMilliseconds),8:0.000} ms. | ").Append($"MAX: {kvp.Value.Max(t => t.TotalMilliseconds),8:0.000} ms. | ").Append($"MIN: {kvp.Value.Min(t => t.TotalMilliseconds),8:0.000} ms. | ").Append(Environment.NewLine); } } return builder.ToString().TrimEnd(); } /// /// Measures the elapsed time of the given action as a TimeSpan /// This method uses a high precision Stopwatch if it is available. /// /// The target. /// /// A time interval that represents a specified time, where the specification is in units of ticks. /// /// target. public static TimeSpan BenchmarkAction(Action target) { if(target == null) { throw new ArgumentNullException(nameof(target)); } Stopwatch sw = Stopwatch.IsHighResolution ? new HighResolutionTimer() : new Stopwatch(); try { sw.Start(); target.Invoke(); } catch { // swallow } finally { sw.Stop(); } return TimeSpan.FromTicks(sw.ElapsedTicks); } /// /// Adds the specified result to the given identifier. /// /// The identifier. /// The elapsed. private static void Add(String identifier, TimeSpan elapsed) { lock(SyncLock) { if(Measures.ContainsKey(identifier) == false) { Measures[identifier] = new List(1024 * 1024); } Measures[identifier].Add(elapsed); } } } }