Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,6 @@ FodyWeavers.xsd
# Project extensions
.dotnet/
.CodeCoverage/

## repository customizations
*.lnk
6 changes: 3 additions & 3 deletions Measure-Coverage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ try {
}

# remove old test report
Get-ChildItem -Directory src/*.Test*/TestResults/* | Remove-Item -Recurse
Get-ChildItem -Directory tests/*.Test*/TestResults/* | Remove-Item -Recurse

# rebuild target project to generate source generator files
dotnet build PathBench.slnx --no-incremental --property:EmitCompilerGeneratedFiles=true

# test and measure coverage
dotnet test PathBench.slnx --collect:"XPlat Code Coverage" --settings src/etc/coverlet.runsettings
dotnet test PathBench.slnx --collect:"XPlat Code Coverage" --settings tests/etc/coverlet.runsettings

# export HTML coverage report
Get-ChildItem src/*.Test*/TestResults/*/coverage.cobertura.xml `
Get-ChildItem tests/*.Test*/TestResults/*/coverage.cobertura.xml `
| ForEach-Object {
$name = $_.FullName -replace '^.+[/\\](.+?)[/\\]TestResults[/\\].+[/\\]coverage.cobertura.xml$', '$1'
&./.dotnet/reportgenerator -reports:"$_" -targetdir:".CodeCoverage/$name" -reporttypes:Html
Expand Down
15 changes: 14 additions & 1 deletion PathBench.slnx
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
<Solution>
<Folder Name="/etc/" />
<Folder Name="/etc/">
<File Path=".editorconfig" />
<File Path=".gitignore" />
<File Path="Directory.Build.props" />
<File Path="global.json" />
<File Path="LICENSE.txt" />
<File Path="Measure-Coverage.ps1" />
<File Path="README.md" />
</Folder>
<Folder Name="/etc/resources/">
<File Path="resources/GraphvizSample.svg" />
</Folder>
<Folder Name="/src/">
<Project Path="src/PathBench/PathBench.csproj" Id="69a8b74a-08f8-4934-80d8-229c5248a29a" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/PathBench.Benchmark/PathBench.Benchmark.csproj" Id="6ce93543-91f0-4dae-8cf1-969708140bba" />
<Project Path="tests/PathBench.Sample/PathBench.Sample.csproj" Id="bb79d7c3-187b-428c-8b10-1e932f95b53c" />
<Project Path="tests/PathBench.Test/PathBench.Test.csproj" Id="7fd1eb49-af7b-4162-b3de-ac73f22b2af2" />
</Folder>
</Solution>
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,91 @@
# PathBench

Code path performance monitoring tool.

----

## Features

*PathBench* is a tool that measures method execution time to identify performance bottlenecks. It provides the following features:

- **Method execution time statistics**: Measures method execution times and provides statistical information such as the mean, standard deviation, minimum, and maximum.
- **Checkpoint recording**: Allows you to set checkpoints at arbitrary locations within a method and measure the execution time between checkpoints.
- **Result visualization**: Outputs measurement results in Graphviz format and visualizes them as a directed graph between checkpoints.

## Usage

1. **Installation**: Add *PathBench* to your project from NuGet.
2. **Code changes**
1. Create a `CodePathCounter` instance in the class that contains the method you want to monitor.
2. At the beginning of the monitored method, call `counter.StartMeasurement()` to create a measurement instance and start the measurement.
3. Dispose the measurement instance to end the measurement.
4. Call the `CreateProfileReports()` method on the `CodePathCounter` instance to produce measurement report instances.
5. Pass the measurement report instances to an appropriate formatter to output the results.

```csharp
using PathBench;

invokeTest();

static void invokeTest()
{
for (var i = 0; i < 1000; ++i)
{
SampleClass.SimulatedWork(i);
}

var reports = SampleClass.Profiler.CreateProfileReports();
var sw = new StringWriter();
MethodProfileReportFormatter.DefaultGraphvizStyle.Format(
reports[nameof(SampleClass.SimulatedWork)],
writer: sw);
Console.WriteLine(sw.ToString());
}

static class SampleClass
{
public static readonly CodePathProfiler Profiler = CodePathProfiler.Create();

public static void SimulatedWork(int seed)
{
using var counter = Profiler.StartMeasurement(argumentsExpressionProvider: $"seed={seed}");
if (seed % 2 == 0)
{
counter.MarkCheckpoint("EvenSeed");
Wait(2);
}
else
{
counter.MarkCheckpoint("OddSeed");
Wait(0);
}
for (var i = 0; i < seed; ++i)
{
counter.MarkCheckpoint("LoopBegin", i);
if (seed % 3 == 0)
{
counter.MarkCheckpoint("DivisibleBy3");
Wait(3);
}
if (seed % 5 == 0)
{
counter.MarkCheckpoint("DivisibleBy5");
Wait(5);
}
if (seed % 7 == 0)
{
counter.MarkCheckpoint("DivisibleBy7");
Wait(7);
}
counter.MarkCheckpoint("LoopEnd");
}
}

private static void Wait(int value)
{
Thread.SpinWait(value * 100);
}
}
```

![graphviz sample](resources/GraphvizSample.svg)
Loading