• Skip to primary navigation
  • Skip to main content

Josh Withee

  • Home
  • Blog
  • Contact
  • Show Search
Hide Search

C#: Track Performance of Database Calls

Josh Withee · February 24, 2022 · Leave a Comment

Plug-n-play thread-safe C# utility class for determining which database calls are slowing down your application.

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;

namespace Hillsdale.API.TicketSystem
{
  public static class Ticker
  {
    private static ConcurrentDictionary<Guid, Stopwatch> _timers = new ConcurrentDictionary<Guid, Stopwatch>();
    private static ConcurrentDictionary<string, ConcurrentStack<long>> _results = new ConcurrentDictionary<string, ConcurrentStack<long>>();
    private static ConcurrentDictionary<Guid, string> _guidMap = new ConcurrentDictionary<Guid, string>();

    public static Guid StartTimer(string name)
    {
      var g = Guid.NewGuid();
      _timers.TryAdd(g, Stopwatch.StartNew());
      _guidMap.TryAdd(g, name);
      return g;
    }
    public static void Stop(Guid guid, bool printImmediately = false)
    {
      if (_timers.TryRemove(guid, out Stopwatch sw))
      {
        sw.Stop();
        var elapsedTime = sw.ElapsedMilliseconds;
        _guidMap.TryRemove(guid, out string name);
        if (printImmediately)
          Console.WriteLine("Ticker[" + name + "]: " + elapsedTime);
        if (_results.TryGetValue(name, out ConcurrentStack<long> stack))
        {
          stack.Push(elapsedTime);
        }
        else
        {
          var cs = new ConcurrentStack<long>();
          cs.Push(elapsedTime);
          _results.TryAdd(name, cs);
        }
      }
      else
      {
        Console.WriteLine("error removing timer");
      }
    }

    public static void PrintStats()
    {
      var array = _results.AsEnumerable().ToArray();
      var keyPadding = array.Max(x => x.Key.Length);
      var valuePadding = array.Max(x => x.Value.ToArray().Max()).ToString().Length;
      var countPadding = array.Max(x => x.Value.Count).ToString().Length;
      var totalTimeTracked = array.Sum(x => x.Value.ToArray().Sum());
      Console.WriteLine("===================================");
      foreach (var item in array)
      {
        var sum = item.Value.ToArray().Sum();
        var avg = sum / item.Value.Count;
        var max = item.Value.ToArray().Max();
        var percentOfTotalTracked = (100 * sum) / totalTimeTracked;
        Console.WriteLine(
          item.Key.PadLeft(keyPadding) 
          + ":   Count = " + item.Value.Count.ToString().PadLeft(countPadding) 
          + "   AVG = " + avg.ToString().PadLeft(valuePadding) 
          + "   MAX = " + max.ToString().PadLeft(valuePadding) 
          + "  " + new string('█', (int)percentOfTotalTracked)
        );
      }
      Console.WriteLine("===================================");
    }

    public static void Tacker(string name, Action a)
    {
      Guid g = StartTimer(name);
      a();
      Stop(g);
    }

    public static T Tacker<T>(string name, Func<T> f)
    {
      Guid g = StartTimer(name);
      T result = f();
      Stop(g);
      return result;
    }
  }
}

Examples:

public int ExecuteNonQueryProxy(string commandText, CommandType commandType, List<SqlParameter> commandParameters)
       =>  Ticker.Tacker(commandText, () => ExecuteNonQuery(commandText, commandType, commandParameters));

Related

Uncategorized C#, Performance

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Copyright © 2023