Skip to content

Commit 8e8f2fe

Browse files
authored
Merge pull request #36 from PandaTechAM/development
Mini benchmarking MethodTimingStatistics added
2 parents 4a5d2eb + e72b73b commit 8e8f2fe

File tree

3 files changed

+125
-6
lines changed

3 files changed

+125
-6
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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+
}

src/SharedKernel/SharedKernel.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
<PackageReadmeFile>Readme.md</PackageReadmeFile>
99
<Authors>Pandatech</Authors>
1010
<Copyright>MIT</Copyright>
11-
<Version>1.2.6</Version>
11+
<Version>1.2.7</Version>
1212
<PackageId>Pandatech.SharedKernel</PackageId>
1313
<Title>Pandatech Shared Kernel Library</Title>
1414
<PackageTags>Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar</PackageTags>
1515
<Description>Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-sharedkernel</RepositoryUrl>
17-
<PackageReleaseNotes>Updated Pandatech.ResponseCrafter to 5.1.6</PackageReleaseNotes>
17+
<PackageReleaseNotes>Mini benchmarking MethodTimingStatistics added</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>
@@ -32,7 +32,7 @@
3232
<PackageReference Include="Elastic.CommonSchema.Serilog" Version="8.12.3" />
3333
<PackageReference Include="FluentDateTime" Version="3.0.0" />
3434
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
35-
<PackageReference Include="HtmlSanitizer" Version="9.0.873" />
35+
<PackageReference Include="HtmlSanitizer" Version="9.0.876" />
3636
<PackageReference Include="MediatR" Version="12.4.1" />
3737
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.1" />
3838
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.1" />
@@ -53,7 +53,7 @@
5353
<PackageReference Include="Pandatech.PandaVaultClient" Version="4.0.3" />
5454
<PackageReference Include="Pandatech.RegexBox" Version="3.0.0" />
5555
<PackageReference Include="Pandatech.ResponseCrafter" Version="5.1.6" />
56-
<PackageReference Include="Scalar.AspNetCore" Version="2.0.7" />
56+
<PackageReference Include="Scalar.AspNetCore" Version="2.0.12" />
5757
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
5858
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="7.2.0" />
5959
</ItemGroup>

test/SharedKernel.Tests/SharedKernel.Tests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
1414
<PackageReference Include="xunit" Version="2.9.3" />
15-
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
15+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
<PrivateAssets>all</PrivateAssets>
1818
</PackageReference>

0 commit comments

Comments
 (0)