-
-
Notifications
You must be signed in to change notification settings - Fork 945
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DotMemoryDiagnoser implementation (#2549)
* DotMemoryDiagnoser implementation * Typo fix, less alloc in test * attempt to fix reinit logic for tool * Simplify implementation
- Loading branch information
1 parent
4ab69be
commit c7b7abf
Showing
18 changed files
with
567 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--- | ||
uid: BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser | ||
--- | ||
|
||
## Sample: IntroDotMemoryDiagnoser | ||
|
||
If you want to get a memory allocation profile of your benchmarks, just add the `[DotMemoryDiagnoser]` attribute, as shown below. | ||
As a result, BenchmarkDotNet performs bonus benchmark runs using attached | ||
[dotMemory Command-Line Profiler](https://www.jetbrains.com/help/dotmemory/Working_with_dotMemory_Command-Line_Profiler.html). | ||
The obtained dotMemory workspaces are saved to the `artifacts` folder. | ||
These dotMemory workspaces can be opened using the [standalone dotMemory](https://www.jetbrains.com/dotmemory/), | ||
or [dotMemory in Rider](https://www.jetbrains.com/help/rider/Memory_profiling_of_.NET_code.html). | ||
|
||
### Source code | ||
|
||
[!code-csharp[IntroDotMemoryDiagnoser.cs](../../../samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs)] | ||
|
||
### Links | ||
|
||
* The permanent link to this sample: @BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser | ||
|
||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
using BenchmarkDotNet.Attributes; | ||
using BenchmarkDotNet.Diagnostics.dotMemory; | ||
using System.Collections.Generic; | ||
|
||
namespace BenchmarkDotNet.Samples | ||
{ | ||
// Enables dotMemory profiling for all jobs | ||
[DotMemoryDiagnoser] | ||
// Adds the default "external-process" job | ||
// Profiling is performed using dotMemory Command-Line Profiler | ||
// See: https://www.jetbrains.com/help/dotmemory/Working_with_dotMemory_Command-Line_Profiler.html | ||
[SimpleJob] | ||
// Adds an "in-process" job | ||
// Profiling is performed using dotMemory SelfApi | ||
// NuGet reference: https://www.nuget.org/packages/JetBrains.Profiler.SelfApi | ||
[InProcess] | ||
public class IntroDotMemoryDiagnoser | ||
{ | ||
[Params(1024)] | ||
public int Size; | ||
|
||
private byte[] dataArray; | ||
private IEnumerable<byte> dataEnumerable; | ||
|
||
[GlobalSetup] | ||
public void Setup() | ||
{ | ||
dataArray = new byte[Size]; | ||
dataEnumerable = dataArray; | ||
} | ||
|
||
[Benchmark] | ||
public int IterateArray() | ||
{ | ||
var count = 0; | ||
foreach (var _ in dataArray) | ||
count++; | ||
|
||
return count; | ||
} | ||
|
||
[Benchmark] | ||
public int IterateEnumerable() | ||
{ | ||
var count = 0; | ||
foreach (var _ in dataEnumerable) | ||
count++; | ||
|
||
return count; | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<Import Project="..\..\build\common.props" /> | ||
<PropertyGroup> | ||
<TargetFrameworks>net6.0;net462;netcoreapp3.1</TargetFrameworks> | ||
<NoWarn>$(NoWarn);1591</NoWarn> | ||
<AssemblyTitle>BenchmarkDotNet.Diagnostics.dotMemory</AssemblyTitle> | ||
<AssemblyName>BenchmarkDotNet.Diagnostics.dotMemory</AssemblyName> | ||
<PackageId>BenchmarkDotNet.Diagnostics.dotMemory</PackageId> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\BenchmarkDotNet\BenchmarkDotNet.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="JetBrains.Profiler.SelfApi" Version="2.5.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
156 changes: 156 additions & 0 deletions
156
src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using BenchmarkDotNet.Analysers; | ||
using BenchmarkDotNet.Diagnosers; | ||
using BenchmarkDotNet.Engines; | ||
using BenchmarkDotNet.Exporters; | ||
using BenchmarkDotNet.Jobs; | ||
using BenchmarkDotNet.Loggers; | ||
using BenchmarkDotNet.Portability; | ||
using BenchmarkDotNet.Reports; | ||
using BenchmarkDotNet.Running; | ||
using BenchmarkDotNet.Validators; | ||
using RunMode = BenchmarkDotNet.Diagnosers.RunMode; | ||
|
||
namespace BenchmarkDotNet.Diagnostics.dotMemory | ||
{ | ||
public class DotMemoryDiagnoser : IProfiler | ||
{ | ||
private readonly Uri? nugetUrl; | ||
private readonly string? toolsDownloadFolder; | ||
|
||
private DotMemoryTool? tool; | ||
|
||
public DotMemoryDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null) | ||
{ | ||
this.nugetUrl = nugetUrl; | ||
this.toolsDownloadFolder = toolsDownloadFolder; | ||
} | ||
|
||
public IEnumerable<string> Ids => new[] { "DotMemory" }; | ||
public string ShortName => "dotMemory"; | ||
|
||
public RunMode GetRunMode(BenchmarkCase benchmarkCase) | ||
{ | ||
return IsSupported(benchmarkCase.Job.Environment.GetRuntime().RuntimeMoniker) ? RunMode.ExtraRun : RunMode.None; | ||
} | ||
|
||
private readonly List<string> snapshotFilePaths = new (); | ||
|
||
public void Handle(HostSignal signal, DiagnoserActionParameters parameters) | ||
{ | ||
var logger = parameters.Config.GetCompositeLogger(); | ||
var job = parameters.BenchmarkCase.Job; | ||
|
||
var runtimeMoniker = job.Environment.GetRuntime().RuntimeMoniker; | ||
if (!IsSupported(runtimeMoniker)) | ||
{ | ||
logger.WriteLineError($"Runtime '{runtimeMoniker}' is not supported by dotMemory"); | ||
return; | ||
} | ||
|
||
switch (signal) | ||
{ | ||
case HostSignal.BeforeAnythingElse: | ||
if (tool is not null) | ||
throw new InvalidOperationException("DotMemory tool is already initialized"); | ||
tool = new DotMemoryTool(logger, nugetUrl, downloadTo: toolsDownloadFolder); | ||
tool.Init(); | ||
break; | ||
case HostSignal.BeforeActualRun: | ||
if (tool is null) | ||
throw new InvalidOperationException("DotMemory tool is not initialized"); | ||
snapshotFilePaths.Add(tool.Start(parameters)); | ||
break; | ||
case HostSignal.AfterActualRun: | ||
if (tool is null) | ||
throw new InvalidOperationException("DotMemory tool is not initialized"); | ||
tool.Stop(); | ||
tool = null; | ||
break; | ||
} | ||
} | ||
|
||
public IEnumerable<IExporter> Exporters => Enumerable.Empty<IExporter>(); | ||
public IEnumerable<IAnalyser> Analysers => Enumerable.Empty<IAnalyser>(); | ||
|
||
public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters) | ||
{ | ||
var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct(); | ||
foreach (var runtimeMoniker in runtimeMonikers) | ||
{ | ||
if (!IsSupported(runtimeMoniker)) | ||
yield return new ValidationError(true, $"Runtime '{runtimeMoniker}' is not supported by dotMemory"); | ||
} | ||
} | ||
|
||
internal static bool IsSupported(RuntimeMoniker runtimeMoniker) | ||
{ | ||
switch (runtimeMoniker) | ||
{ | ||
case RuntimeMoniker.HostProcess: | ||
case RuntimeMoniker.Net461: | ||
case RuntimeMoniker.Net462: | ||
case RuntimeMoniker.Net47: | ||
case RuntimeMoniker.Net471: | ||
case RuntimeMoniker.Net472: | ||
case RuntimeMoniker.Net48: | ||
case RuntimeMoniker.Net481: | ||
case RuntimeMoniker.Net50: | ||
case RuntimeMoniker.Net60: | ||
case RuntimeMoniker.Net70: | ||
case RuntimeMoniker.Net80: | ||
case RuntimeMoniker.Net90: | ||
return true; | ||
case RuntimeMoniker.NotRecognized: | ||
case RuntimeMoniker.Mono: | ||
case RuntimeMoniker.NativeAot60: | ||
case RuntimeMoniker.NativeAot70: | ||
case RuntimeMoniker.NativeAot80: | ||
case RuntimeMoniker.NativeAot90: | ||
case RuntimeMoniker.Wasm: | ||
case RuntimeMoniker.WasmNet50: | ||
case RuntimeMoniker.WasmNet60: | ||
case RuntimeMoniker.WasmNet70: | ||
case RuntimeMoniker.WasmNet80: | ||
case RuntimeMoniker.WasmNet90: | ||
case RuntimeMoniker.MonoAOTLLVM: | ||
case RuntimeMoniker.MonoAOTLLVMNet60: | ||
case RuntimeMoniker.MonoAOTLLVMNet70: | ||
case RuntimeMoniker.MonoAOTLLVMNet80: | ||
case RuntimeMoniker.MonoAOTLLVMNet90: | ||
case RuntimeMoniker.Mono60: | ||
case RuntimeMoniker.Mono70: | ||
case RuntimeMoniker.Mono80: | ||
case RuntimeMoniker.Mono90: | ||
#pragma warning disable CS0618 // Type or member is obsolete | ||
case RuntimeMoniker.NetCoreApp50: | ||
#pragma warning restore CS0618 // Type or member is obsolete | ||
return false; | ||
case RuntimeMoniker.NetCoreApp20: | ||
case RuntimeMoniker.NetCoreApp21: | ||
case RuntimeMoniker.NetCoreApp22: | ||
return RuntimeInformation.IsWindows(); | ||
case RuntimeMoniker.NetCoreApp30: | ||
case RuntimeMoniker.NetCoreApp31: | ||
return RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux(); | ||
default: | ||
throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported"); | ||
} | ||
} | ||
|
||
public IEnumerable<Metric> ProcessResults(DiagnoserResults results) => ImmutableArray<Metric>.Empty; | ||
|
||
public void DisplayResults(ILogger logger) | ||
{ | ||
if (snapshotFilePaths.Any()) | ||
{ | ||
logger.WriteLineInfo("The following dotMemory snapshots were generated:"); | ||
foreach (string snapshotFilePath in snapshotFilePaths) | ||
logger.WriteLineInfo($"* {snapshotFilePath}"); | ||
} | ||
} | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using System; | ||
using BenchmarkDotNet.Configs; | ||
|
||
namespace BenchmarkDotNet.Diagnostics.dotMemory | ||
{ | ||
[AttributeUsage(AttributeTargets.Class)] | ||
public class DotMemoryDiagnoserAttribute : Attribute, IConfigSource | ||
{ | ||
public IConfig Config { get; } | ||
|
||
public DotMemoryDiagnoserAttribute() | ||
{ | ||
Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser()); | ||
} | ||
|
||
public DotMemoryDiagnoserAttribute(Uri? nugetUrl = null, string? toolsDownloadFolder = null) | ||
{ | ||
Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser(nugetUrl, toolsDownloadFolder)); | ||
} | ||
} | ||
} |
Oops, something went wrong.