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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ public class SelectionBenchmarks : RevitApiUiBenchmark

UI benchmarks use the `RevitAPIUI` types, add the `Nice3point.Revit.Api.RevitAPIUI` package reference.

## DotTrace profiler

Benchmarks support profiling with dotTrace.
Add the `DotTraceDiagnoser` from the `BenchmarkDotNet.Diagnostics.dotTrace` package to the configuration:

```csharp
var configuration = ManualConfig.Create()
.AddJob(Job.Default.WithCurrentConfiguration())
.AddDiagnoser(new DotTraceDiagnoser());
```

The diagnoser profiles `RevitApiBenchmark` and `RevitApiUiBenchmark` classes alike.
To profile a single class, mark it with the `[DotTraceDiagnoser]` attribute.

## Bug fixes

- A benchmark project that references `RevitAPIUI` runs without `FileNotFoundException` https://github.com/Nice3point/RevitBenchmark/issues/57.
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<!-- Core dependencies -->
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8"/>
<PackageVersion Include="BenchmarkDotNet.Diagnostics.dotTrace" Version="0.15.8"/>
<PackageVersion Include="PolyHook2.NET" Version="1.1.9"/>
<PackageVersion Include="System.Text.Json" Version="10.0.12" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'"/>

Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,28 @@ public class RevitSelectionBenchmarks : RevitApiUiBenchmark
UI benchmarks share one Revit instance, which starts with the first UI benchmark and closes when the run ends.
Asynchronous benchmark methods and the job settings of the benchmark process, such as the runtime and the GC mode, are not supported.

## DotTrace profiler

Profile your benchmarks with dotTrace by adding the `DotTraceDiagnoser` from the `BenchmarkDotNet.Diagnostics.dotTrace` package to the configuration:

```csharp
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnostics.dotTrace;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using Nice3point.BenchmarkDotNet.Revit;

var configuration = ManualConfig.Create()
.AddJob(Job.Default.WithCurrentConfiguration())
.AddDiagnoser(new DotTraceDiagnoser());

BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly)
.Run(args, configuration);
```

The diagnoser profiles `RevitApiBenchmark` and `RevitApiUiBenchmark` classes alike.
To profile a single class, mark it with the `[DotTraceDiagnoser]` attribute.

## Benchmark configuration

BenchmarkDotNet initializes Revit with the `English - United States` language, the `C:\Program Files\Autodesk\Revit {version}` installation path, and an add-in with a random ID. To override these defaults:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<PackageReference Include="Nice3point.Revit.Api.RevitAPI"/>
<PackageReference Include="Nice3point.Revit.Api.RevitAPIUI"/>
<PackageReference Include="Nice3point.Revit.Injector"/>
<PackageReference Include="BenchmarkDotNet.Diagnostics.dotTrace"/>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System.Globalization;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnostics.dotTrace;

namespace Nice3point.BenchmarkDotNet.Revit.Benchmarks;

// BenchmarkDotNet v0.15.8, Windows 11 (10.0.28000.2704/26H1/2026Update)
// AMD Ryzen 9 9950X3D 4.30GHz, 1 CPU, 32 logical and 16 physical cores
// .NET SDK 10.0.112
// [Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4
// Job-YLFTCN : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4
//
// BuildConfiguration=Release.R27 Toolchain=RevitUi
//
// | Method | Mean | Error | StdDev | Allocated |
// |-------------------------------- |----------:|---------:|---------:|-----------:|
// | SetCommentsInOneTransaction | 48.24 ms | 0.951 ms | 0.934 ms | 72.34 KB |
// | SetCommentsInTransactionPerWall | 332.45 ms | 6.470 ms | 7.451 ms | 1678.73 KB |

[DotTraceDiagnoser]
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public class TransactionBenchmarks : RevitApiUiBenchmark
{
private Document _document = null!;
private List<Wall> _walls = null!;
private int _revision;

protected sealed override void OnGlobalSetup()
{
var modelPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.rvt");
var document = UiApplication.Application.NewProjectDocument(UnitSystem.Metric);

using (var transaction = new Transaction(document, "Seed model"))
{
transaction.Start();

var level = Level.Create(document, 0);
for (var i = 0; i < 100; i++)
{
Wall.Create(document, Line.CreateBound(new XYZ(i, 0, 0), new XYZ(i + 1, 0, 0)), level.Id, false);
}

transaction.Commit();
}

document.SaveAs(modelPath);
document.Close(false);

_document = UiApplication.OpenAndActivateDocument(modelPath).Document;
_walls = new FilteredElementCollector(_document)
.OfClass(typeof(Wall))
.Cast<Wall>()
.ToList();
}

protected sealed override void OnGlobalCleanup()
{
_document.Save();
}

[Benchmark]
public void SetCommentsInOneTransaction()
{
var comment = CreateComment();

using var transaction = new Transaction(_document, "Set comments");
transaction.Start();

foreach (var wall in _walls)
{
wall.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).Set(comment);
}

transaction.Commit();
}

[Benchmark]
public void SetCommentsInTransactionPerWall()
{
var comment = CreateComment();

foreach (var wall in _walls)
{
using var transaction = new Transaction(_document, "Set comment");
transaction.Start();
wall.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).Set(comment);
transaction.Commit();
}
}

private string CreateComment()
{
return (++_revision).ToString(CultureInfo.InvariantCulture);
}
}
Loading