namespace Unosquare.Swan.Components { using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; /// /// A simple benchmarking class. /// /// /// The following code demonstrates how to create a simple benchmark. /// /// namespace Examples.Benchmark.Simple /// { /// using Unosquare.Swan.Components; /// /// 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 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() { var builder = new StringBuilder(); lock (SyncLock) { foreach (var 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(); } /// /// 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); } } /// /// Represents a disposable benchmark unit. /// /// private sealed class BenchmarkUnit : IDisposable { private readonly string _identifier; private bool _isDisposed; // To detect redundant calls private Stopwatch _stopwatch = new Stopwatch(); /// /// Initializes a new instance of the class. /// /// The identifier. public BenchmarkUnit(string identifier) { _identifier = identifier; _stopwatch.Start(); } /// public void Dispose() => Dispose(true); /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. private void Dispose(bool alsoManaged) { if (_isDisposed) return; if (alsoManaged) { Add(_identifier, _stopwatch.Elapsed); _stopwatch?.Stop(); } _stopwatch = null; _isDisposed = true; } } } }