1+ using System . Diagnostics ;
2+ using Microsoft . Extensions . Logging ;
3+
4+ #warning "Not recommended for production use. Just for benchmarking purposes."
5+
6+ namespace SharedKernel . Helpers ;
7+
8+ /// <summary>
9+ /// Tracks and logs timing statistics of method executions for benchmarking.
10+ /// </summary>
11+ /// <remarks>
12+ /// Not recommended for production usage. Use only for performance diagnostics or quick benchmarks.
13+ /// </remarks>
14+ public class MethodTimingStatistics
15+ {
16+ public required string MethodName { get ; init ; }
17+ private int InvocationCount { get ; set ; }
18+ private double TotalElapsedMilliseconds { get ; set ; }
19+ private double AverageElapsedMilliseconds { get ; set ; }
20+
21+ private static readonly List < MethodTimingStatistics > CollectedStats = [ ] ;
22+
23+ /// <summary>
24+ /// Updates or adds statistics for a particular method based on the start timestamp.
25+ /// </summary>
26+ /// <param name="methodName">The name of the method being tracked.</param>
27+ /// <param name="startTimestamp">A timestamp (via <see cref="Stopwatch.GetTimestamp"/>) captured before the method execution.</param>
28+ public static void RecordExecution ( string methodName , long startTimestamp )
29+ {
30+ var elapsedMs = Stopwatch . GetElapsedTime ( startTimestamp )
31+ . TotalMilliseconds ;
32+
33+ var existingStat = CollectedStats . FirstOrDefault ( s => s . MethodName == methodName ) ;
34+ if ( existingStat == null )
35+ {
36+ CollectedStats . Add ( new MethodTimingStatistics
37+ {
38+ MethodName = methodName ,
39+ InvocationCount = 1 ,
40+ TotalElapsedMilliseconds = elapsedMs ,
41+ AverageElapsedMilliseconds = elapsedMs
42+ } ) ;
43+ }
44+ else
45+ {
46+ existingStat . InvocationCount ++ ;
47+ existingStat . TotalElapsedMilliseconds += elapsedMs ;
48+ existingStat . AverageElapsedMilliseconds =
49+ existingStat . TotalElapsedMilliseconds / existingStat . InvocationCount ;
50+ }
51+ }
52+
53+ /// <summary>
54+ /// Logs the accumulated statistics for all tracked methods.
55+ /// </summary>
56+ /// <param name="logger">An <see cref="ILogger"/> instance used for logging the statistics.</param>
57+ public static void LogAll ( ILogger logger )
58+ {
59+ foreach ( var stat in CollectedStats )
60+ {
61+ var avgFormatted = FormatDuration ( stat . AverageElapsedMilliseconds ) ;
62+ var totalFormatted = FormatDuration ( stat . TotalElapsedMilliseconds ) ;
63+
64+ logger . LogInformation (
65+ "Method '{MethodName}' statistics => Called {Count} times, Average duration: {Avg}, Total elapsed: {Total}." ,
66+ stat . MethodName ,
67+ stat . InvocationCount ,
68+ avgFormatted ,
69+ totalFormatted ) ;
70+ }
71+ }
72+
73+ /// <summary>
74+ /// Clears the statistics for a specific method or for all methods if none is specified.
75+ /// </summary>
76+ /// <param name="methodName">Optional method name to clear; if null or empty, clears all statistics.</param>
77+ public static void ClearStatistics ( string ? methodName = null )
78+ {
79+ if ( ! string . IsNullOrEmpty ( methodName ) )
80+ {
81+ CollectedStats . RemoveAll ( s => s . MethodName == methodName ) ;
82+ }
83+ else
84+ {
85+ CollectedStats . Clear ( ) ;
86+ }
87+ }
88+ private static string FormatDuration ( double milliseconds )
89+ {
90+ switch ( milliseconds )
91+ {
92+ // Less than 1 ms => microseconds
93+ case < 1 :
94+ {
95+ var microseconds = milliseconds * 1000.0 ;
96+ return $ "{ microseconds : F3} µs";
97+ }
98+ // 1 ms up to 1000 ms
99+ case < 1000 :
100+ return $ "{ milliseconds : F3} ms";
101+ }
102+
103+ // Convert to seconds
104+ var seconds = milliseconds / 1000.0 ;
105+
106+ // If < 60 seconds => show "X s Y ms"
107+ if ( seconds < 60 )
108+ {
109+ var wholeSeconds = ( int ) seconds ;
110+ var remainderMs = milliseconds - ( wholeSeconds * 1000.0 ) ;
111+ return $ "{ wholeSeconds } s { remainderMs : F3} ms";
112+ }
113+
114+ // Otherwise => show "X min Y s" (limit to minutes)
115+ var minutes = ( int ) ( seconds / 60 ) ;
116+ var remainSeconds = ( int ) ( seconds % 60 ) ;
117+ return $ "{ minutes } m { remainSeconds } s";
118+ }
119+ }
0 commit comments