From 516d2c06a9d8a4cfff05dc75448aa4d0bfc721a0 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Sat, 23 May 2026 16:37:38 -0500 Subject: [PATCH] perf: add application workflow benchmark results --- README.md | 6 ++++ .../Application/DataMapperBenchmarks.cs | 29 +++++++++++++++++ .../Application/MaterializedViewBenchmarks.cs | 29 +++++++++++++++++ .../Application/UnitOfWorkBenchmarks.cs | 32 +++++++++++++++++++ docs/guides/benchmark-results.md | 6 ++++ docs/guides/benchmarks.md | 6 ++++ 6 files changed, 108 insertions(+) create mode 100644 benchmarks/PatternKit.Benchmarks/Application/DataMapperBenchmarks.cs create mode 100644 benchmarks/PatternKit.Benchmarks/Application/MaterializedViewBenchmarks.cs create mode 100644 benchmarks/PatternKit.Benchmarks/Application/UnitOfWorkBenchmarks.cs diff --git a/README.md b/README.md index 34111b5..69d23ed 100644 --- a/README.md +++ b/README.md @@ -468,8 +468,12 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide | Ambassador | Execution | 87.92 ns | 624 B | 93.72 ns | 624 B | Same allocation; fluent was slightly faster in this path. | | Cache-Aside | Construction | 19.91 ns | 200 B | 19.85 ns | 200 B | Effectively equivalent for this microbenchmark. | | Cache-Aside | Execution | 216.50 ns | 1,048 B | 208.60 ns | 1,048 B | Same allocation; generated was slightly faster for the miss-then-hit workflow. | +| Data Mapper | Construction | 40.56 ns | 288 B | 12.87 ns | 112 B | Generated reduced construction time and allocation in this microbenchmark. | +| Data Mapper | Execution | 188.09 ns | 1,104 B | 97.71 ns | 672 B | Generated reduced execution time and allocation for the map-store-load workflow. | | Leader Election | Construction | 14.28 ns | 104 B | 15.91 ns | 104 B | Same allocation; fluent was slightly faster in this microbenchmark. | | Leader Election | Execution | 43.62 ns | 360 B | 144.37 ns | 312 B | Generated allocated about 13% less memory, while fluent was faster in this path. | +| Materialized View | Construction | 140.9 ns | 1.05 KB | 147.4 ns | 1.05 KB | Same allocation; fluent was slightly faster in this microbenchmark. | +| Materialized View | Execution | 389.5 ns | 2.02 KB | 386.0 ns | 2.02 KB | Effectively equivalent for this scenario. | | Message Routing | Construction | 23.42 ns | 224 B | 23.33 ns | 224 B | Effectively equivalent for this microbenchmark. | | Message Routing | Execution | 707.34 ns | 4,744 B | 679.97 ns | 4,632 B | Generated reduced execution time and allocation for the route/split/aggregate workflow. | | Message Translator | Construction | 39.49 ns | 424 B | 39.65 ns | 424 B | Effectively equivalent for this microbenchmark. | @@ -488,6 +492,8 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide | Specification | Execution | 111.25 ns | 344 B | 93.30 ns | 344 B | Same allocation; generated was faster for loan-application evaluation. | | Table Data Gateway | Construction | 9.740 ns | 120 B | 9.698 ns | 120 B | Effectively equivalent for this microbenchmark. | | Table Data Gateway | Execution | 90.51 ns | 600 B | 96.35 ns | 600 B | Same allocation; fluent was slightly faster for the insert-update-query workflow. | +| Unit Of Work | Construction | 49.50 ns | 304 B | 46.91 ns | 304 B | Same allocation; generated was slightly faster in this microbenchmark. | +| Unit Of Work | Execution | 121.03 ns | 824 B | 96.91 ns | 520 B | Generated reduced execution time and allocation for the checkout commit workflow. | Run the benchmarks on target hardware before making final route decisions: diff --git a/benchmarks/PatternKit.Benchmarks/Application/DataMapperBenchmarks.cs b/benchmarks/PatternKit.Benchmarks/Application/DataMapperBenchmarks.cs new file mode 100644 index 0000000..1316bb2 --- /dev/null +++ b/benchmarks/PatternKit.Benchmarks/Application/DataMapperBenchmarks.cs @@ -0,0 +1,29 @@ +using BenchmarkDotNet.Attributes; +using PatternKit.Application.DataMapping; +using PatternKit.Examples.DataMapperDemo; + +namespace PatternKit.Benchmarks.Application; + +[BenchmarkCategory("ApplicationArchitecture", "DataMapper")] +public class DataMapperBenchmarks +{ + [Benchmark(Baseline = true, Description = "Fluent: create data mapper")] + [BenchmarkCategory("Fluent", "Construction")] + public DataMapper Fluent_CreateMapper() + => OrderDataMapperPolicies.CreateFluentMapper(); + + [Benchmark(Description = "Generated: create data mapper")] + [BenchmarkCategory("Generated", "Construction")] + public IDataMapper Generated_CreateMapper() + => GeneratedOrderDataMapper.CreateMapper(); + + [Benchmark(Description = "Fluent: map store and load order")] + [BenchmarkCategory("Fluent", "Execution")] + public ValueTask Fluent_MapStoreAndLoadOrder() + => OrderDataMapperDemo.RunFluentAsync(); + + [Benchmark(Description = "Generated: map store and load order")] + [BenchmarkCategory("Generated", "Execution")] + public ValueTask Generated_MapStoreAndLoadOrder() + => OrderDataMapperDemo.RunGeneratedAsync(); +} diff --git a/benchmarks/PatternKit.Benchmarks/Application/MaterializedViewBenchmarks.cs b/benchmarks/PatternKit.Benchmarks/Application/MaterializedViewBenchmarks.cs new file mode 100644 index 0000000..061ab75 --- /dev/null +++ b/benchmarks/PatternKit.Benchmarks/Application/MaterializedViewBenchmarks.cs @@ -0,0 +1,29 @@ +using BenchmarkDotNet.Attributes; +using PatternKit.Application.MaterializedViews; +using PatternKit.Examples.MaterializedViewDemo; + +namespace PatternKit.Benchmarks.Application; + +[BenchmarkCategory("ApplicationArchitecture", "MaterializedView")] +public class MaterializedViewBenchmarks +{ + [Benchmark(Baseline = true, Description = "Fluent: create materialized view")] + [BenchmarkCategory("Fluent", "Construction")] + public MaterializedView Fluent_CreateView() + => OrderMaterializedViewPolicies.CreateFluentView(); + + [Benchmark(Description = "Generated: create materialized view")] + [BenchmarkCategory("Generated", "Construction")] + public IMaterializedView Generated_CreateView() + => GeneratedOrderMaterializedView.CreateView(); + + [Benchmark(Description = "Fluent: project order read model")] + [BenchmarkCategory("Fluent", "Execution")] + public ValueTask Fluent_ProjectOrderReadModel() + => OrderMaterializedViewDemo.RunFluentAsync(); + + [Benchmark(Description = "Generated: project order read model")] + [BenchmarkCategory("Generated", "Execution")] + public ValueTask Generated_ProjectOrderReadModel() + => OrderMaterializedViewDemo.RunGeneratedAsync(); +} diff --git a/benchmarks/PatternKit.Benchmarks/Application/UnitOfWorkBenchmarks.cs b/benchmarks/PatternKit.Benchmarks/Application/UnitOfWorkBenchmarks.cs new file mode 100644 index 0000000..7c472e6 --- /dev/null +++ b/benchmarks/PatternKit.Benchmarks/Application/UnitOfWorkBenchmarks.cs @@ -0,0 +1,32 @@ +using BenchmarkDotNet.Attributes; +using PatternKit.Application.UnitOfWork; +using PatternKit.Examples.UnitOfWorkDemo; + +namespace PatternKit.Benchmarks.Application; + +[BenchmarkCategory("ApplicationArchitecture", "UnitOfWork")] +public class UnitOfWorkBenchmarks +{ + [Benchmark(Baseline = true, Description = "Fluent: create unit of work")] + [BenchmarkCategory("Fluent", "Construction")] + public UnitOfWork Fluent_CreateUnitOfWork() + => UnitOfWork.Create() + .Enlist("reserve-inventory", static _ => default, static _ => default) + .Enlist("capture-payment", static _ => default, static _ => default) + .Build(); + + [Benchmark(Description = "Generated: create unit of work")] + [BenchmarkCategory("Generated", "Construction")] + public UnitOfWork Generated_CreateUnitOfWork() + => GeneratedCheckoutUnitOfWork.Create(); + + [Benchmark(Description = "Fluent: commit checkout unit")] + [BenchmarkCategory("Fluent", "Execution")] + public ValueTask Fluent_CommitCheckoutUnit() + => CheckoutUnitOfWorkDemo.RunFluentAsync(); + + [Benchmark(Description = "Generated: commit checkout unit")] + [BenchmarkCategory("Generated", "Execution")] + public ValueTask Generated_CommitCheckoutUnit() + => CheckoutUnitOfWorkDemo.RunGeneratedAsync(); +} diff --git a/docs/guides/benchmark-results.md b/docs/guides/benchmark-results.md index c7a1ad7..f2740b0 100644 --- a/docs/guides/benchmark-results.md +++ b/docs/guides/benchmark-results.md @@ -15,8 +15,12 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149 | Ambassador | Execution | 87.92 ns | 624 B | 93.72 ns | 624 B | Same allocation; fluent was slightly faster in this path. | | Cache-Aside | Construction | 19.91 ns | 200 B | 19.85 ns | 200 B | Effectively equivalent for this microbenchmark. | | Cache-Aside | Execution | 216.50 ns | 1,048 B | 208.60 ns | 1,048 B | Same allocation; generated was slightly faster for the miss-then-hit workflow. | +| Data Mapper | Construction | 40.56 ns | 288 B | 12.87 ns | 112 B | Generated reduced construction time and allocation in this microbenchmark. | +| Data Mapper | Execution | 188.09 ns | 1,104 B | 97.71 ns | 672 B | Generated reduced execution time and allocation for the map-store-load workflow. | | Leader Election | Construction | 14.28 ns | 104 B | 15.91 ns | 104 B | Same allocation; fluent was slightly faster in this microbenchmark. | | Leader Election | Execution | 43.62 ns | 360 B | 144.37 ns | 312 B | Generated allocated about 13% less memory, while fluent was faster in this path. | +| Materialized View | Construction | 140.9 ns | 1.05 KB | 147.4 ns | 1.05 KB | Same allocation; fluent was slightly faster in this microbenchmark. | +| Materialized View | Execution | 389.5 ns | 2.02 KB | 386.0 ns | 2.02 KB | Effectively equivalent for this scenario. | | Message Routing | Construction | 23.42 ns | 224 B | 23.33 ns | 224 B | Effectively equivalent for this microbenchmark. | | Message Routing | Execution | 707.34 ns | 4,744 B | 679.97 ns | 4,632 B | Generated reduced execution time and allocation for the route/split/aggregate workflow. | | Message Translator | Construction | 39.49 ns | 424 B | 39.65 ns | 424 B | Effectively equivalent for this microbenchmark. | @@ -35,6 +39,8 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149 | Specification | Execution | 111.25 ns | 344 B | 93.30 ns | 344 B | Same allocation; generated was faster for loan-application evaluation. | | Table Data Gateway | Construction | 9.740 ns | 120 B | 9.698 ns | 120 B | Effectively equivalent for this microbenchmark. | | Table Data Gateway | Execution | 90.51 ns | 600 B | 96.35 ns | 600 B | Same allocation; fluent was slightly faster for the insert-update-query workflow. | +| Unit Of Work | Construction | 49.50 ns | 304 B | 46.91 ns | 304 B | Same allocation; generated was slightly faster in this microbenchmark. | +| Unit Of Work | Execution | 121.03 ns | 824 B | 96.91 ns | 520 B | Generated reduced execution time and allocation for the checkout commit workflow. | ## Coverage Matrix Summary diff --git a/docs/guides/benchmarks.md b/docs/guides/benchmarks.md index 20575e8..b6859ce 100644 --- a/docs/guides/benchmarks.md +++ b/docs/guides/benchmarks.md @@ -32,8 +32,12 @@ The following numbers were captured on Windows 11, Intel Core i9-14900K, .NET SD | Ambassador | Execution | 87.92 ns | 624 B | 93.72 ns | 624 B | Same allocation; fluent was slightly faster in this path. | | Cache-Aside | Construction | 19.91 ns | 200 B | 19.85 ns | 200 B | Effectively equivalent for this microbenchmark. | | Cache-Aside | Execution | 216.50 ns | 1,048 B | 208.60 ns | 1,048 B | Same allocation; generated was slightly faster for the miss-then-hit workflow. | +| Data Mapper | Construction | 40.56 ns | 288 B | 12.87 ns | 112 B | Generated reduced construction time and allocation in this microbenchmark. | +| Data Mapper | Execution | 188.09 ns | 1,104 B | 97.71 ns | 672 B | Generated reduced execution time and allocation for the map-store-load workflow. | | Leader Election | Construction | 14.28 ns | 104 B | 15.91 ns | 104 B | Same allocation; fluent was slightly faster in this microbenchmark. | | Leader Election | Execution | 43.62 ns | 360 B | 144.37 ns | 312 B | Generated allocated about 13% less memory, while fluent was faster in this path. | +| Materialized View | Construction | 140.9 ns | 1.05 KB | 147.4 ns | 1.05 KB | Same allocation; fluent was slightly faster in this microbenchmark. | +| Materialized View | Execution | 389.5 ns | 2.02 KB | 386.0 ns | 2.02 KB | Effectively equivalent for this scenario. | | Message Routing | Construction | 23.42 ns | 224 B | 23.33 ns | 224 B | Effectively equivalent for this microbenchmark. | | Message Routing | Execution | 707.34 ns | 4,744 B | 679.97 ns | 4,632 B | Generated reduced execution time and allocation for the route/split/aggregate workflow. | | Message Translator | Construction | 39.49 ns | 424 B | 39.65 ns | 424 B | Effectively equivalent for this microbenchmark. | @@ -52,6 +56,8 @@ The following numbers were captured on Windows 11, Intel Core i9-14900K, .NET SD | Specification | Execution | 111.25 ns | 344 B | 93.30 ns | 344 B | Same allocation; generated was faster for loan-application evaluation. | | Table Data Gateway | Construction | 9.740 ns | 120 B | 9.698 ns | 120 B | Effectively equivalent for this microbenchmark. | | Table Data Gateway | Execution | 90.51 ns | 600 B | 96.35 ns | 600 B | Same allocation; fluent was slightly faster for the insert-update-query workflow. | +| Unit Of Work | Construction | 49.50 ns | 304 B | 46.91 ns | 304 B | Same allocation; generated was slightly faster in this microbenchmark. | +| Unit Of Work | Execution | 121.03 ns | 824 B | 96.91 ns | 520 B | Generated reduced execution time and allocation for the checkout commit workflow. | The coverage matrix is separate from the scenario timings. Matrix benchmarks prove every catalog pattern and every generator source file has a reportable BenchmarkDotNet route; pattern-specific scenario benchmarks provide the fluent-vs-generated construction and execution numbers shown above. See [Benchmark Results](benchmark-results.md) for the full pattern and generator matrix.