From 9973779268f8a1a651790cb67793c6778df5525d Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sun, 1 Nov 2026 16:40:05 +0300 Subject: [PATCH 1/3] [Docs][Tests] Profile the transaction benchmarks with dotTrace --- CHANGELOG.md | 2 + Directory.Packages.props | 1 + README.md | 13 +++ ...nt.BenchmarkDotNet.Revit.Benchmarks.csproj | 1 + .../TransactionBenchmarks.cs | 97 +++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index dce9903..d3fe97c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ public class SelectionBenchmarks : RevitApiUiBenchmark UI benchmarks use the `RevitAPIUI` types, add the `Nice3point.Revit.Api.RevitAPIUI` package reference. +`[DotTraceDiagnoser]` of the `BenchmarkDotNet.Diagnostics.dotTrace` package profiles the Revit process of a UI benchmark. + ## Bug fixes - A benchmark project that references `RevitAPIUI` runs without `FileNotFoundException` https://github.com/Nice3point/RevitBenchmark/issues/57. diff --git a/Directory.Packages.props b/Directory.Packages.props index 88ec223..d4def26 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,6 +25,7 @@ + diff --git a/README.md b/README.md index cd19417..4fc5a00 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,19 @@ 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. +To profile a UI benchmark with dotTrace, add the `BenchmarkDotNet.Diagnostics.dotTrace` package and mark the class with `[DotTraceDiagnoser]`: + +```csharp +using BenchmarkDotNet.Diagnostics.dotTrace; + +[DotTraceDiagnoser] +public class RevitTransactionBenchmarks : RevitApiUiBenchmark +{ +} +``` + +The snapshot of each benchmark is saved to the `BenchmarkDotNet.Artifacts\snapshots` folder. + ## 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: diff --git a/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/Nice3point.BenchmarkDotNet.Revit.Benchmarks.csproj b/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/Nice3point.BenchmarkDotNet.Revit.Benchmarks.csproj index 1ddd189..082324a 100644 --- a/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/Nice3point.BenchmarkDotNet.Revit.Benchmarks.csproj +++ b/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/Nice3point.BenchmarkDotNet.Revit.Benchmarks.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs b/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs new file mode 100644 index 0000000..dad7de1 --- /dev/null +++ b/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs @@ -0,0 +1,97 @@ +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 _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() + .ToList(); + } + + // The API closes no active document, and Revit asks to save a modified document on exit. + 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(); + } + } + + // Every invocation writes a new value, and every transaction commits a change. + private string CreateComment() + { + return (++_revision).ToString(CultureInfo.InvariantCulture); + } +} From 01488e82327ec4e8913bef4e6d65acb99b48e4fd Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sun, 1 Nov 2026 16:48:05 +0300 Subject: [PATCH 2/3] [Docs] Describe the dotTrace profiler of UI benchmarks --- CHANGELOG.md | 37 ++++++++++++++++++- README.md | 4 +- .../TransactionBenchmarks.cs | 2 - 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3fe97c..389fc72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,42 @@ public class SelectionBenchmarks : RevitApiUiBenchmark UI benchmarks use the `RevitAPIUI` types, add the `Nice3point.Revit.Api.RevitAPIUI` package reference. -`[DotTraceDiagnoser]` of the `BenchmarkDotNet.Diagnostics.dotTrace` package profiles the Revit process of a UI benchmark. +## DotTrace profiler + +UI benchmarks support profiling with dotTrace. +Mark the benchmark class with the `[DotTraceDiagnoser]` attribute from the `BenchmarkDotNet.Diagnostics.dotTrace` package: + +```csharp +[DotTraceDiagnoser] +public class TransactionBenchmarks : RevitApiUiBenchmark +{ + [Benchmark] + public void SetCommentsInOneTransaction() + { + 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() + { + 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(); + } + } +} +``` ## Bug fixes diff --git a/README.md b/README.md index 4fc5a00..a374817 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ 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. -To profile a UI benchmark with dotTrace, add the `BenchmarkDotNet.Diagnostics.dotTrace` package and mark the class with `[DotTraceDiagnoser]`: +Profile UI benchmarks with dotTrace using the `[DotTraceDiagnoser]` attribute from the `BenchmarkDotNet.Diagnostics.dotTrace` package: ```csharp using BenchmarkDotNet.Diagnostics.dotTrace; @@ -214,8 +214,6 @@ public class RevitTransactionBenchmarks : RevitApiUiBenchmark } ``` -The snapshot of each benchmark is saved to the `BenchmarkDotNet.Artifacts\snapshots` folder. - ## 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: diff --git a/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs b/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs index dad7de1..d348fe3 100644 --- a/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs +++ b/tests/Nice3point.BenchmarkDotNet.Revit.Benchmarks/TransactionBenchmarks.cs @@ -53,7 +53,6 @@ protected sealed override void OnGlobalSetup() .ToList(); } - // The API closes no active document, and Revit asks to save a modified document on exit. protected sealed override void OnGlobalCleanup() { _document.Save(); @@ -89,7 +88,6 @@ public void SetCommentsInTransactionPerWall() } } - // Every invocation writes a new value, and every transaction commits a change. private string CreateComment() { return (++_revision).ToString(CultureInfo.InvariantCulture); From 3841222cedb3718d4033f17f553137c18ff22a1c Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sun, 1 Nov 2026 17:57:01 +0300 Subject: [PATCH 3/3] [Docs] Describe the dotTrace profiler of all benchmarks --- CHANGELOG.md | 39 ++++++++------------------------------- README.md | 21 ++++++++++++++++----- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ea167..8f7dfd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,41 +42,18 @@ UI benchmarks use the `RevitAPIUI` types, add the `Nice3point.Revit.Api.RevitAPI ## DotTrace profiler -UI benchmarks support profiling with dotTrace. -Mark the benchmark class with the `[DotTraceDiagnoser]` attribute from the `BenchmarkDotNet.Diagnostics.dotTrace` package: +Benchmarks support profiling with dotTrace. +Add the `DotTraceDiagnoser` from the `BenchmarkDotNet.Diagnostics.dotTrace` package to the configuration: ```csharp -[DotTraceDiagnoser] -public class TransactionBenchmarks : RevitApiUiBenchmark -{ - [Benchmark] - public void SetCommentsInOneTransaction() - { - 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() - { - 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(); - } - } -} +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. diff --git a/README.md b/README.md index a374817..fbe9742 100644 --- a/README.md +++ b/README.md @@ -203,17 +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. -Profile UI benchmarks with dotTrace using the `[DotTraceDiagnoser]` attribute from the `BenchmarkDotNet.Diagnostics.dotTrace` package: +## 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; -[DotTraceDiagnoser] -public class RevitTransactionBenchmarks : RevitApiUiBenchmark -{ -} +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: