From 367a54b26228dc2a04f66594b5c6c8648df9cae3 Mon Sep 17 00:00:00 2001 From: "codebelt-aicia[bot]" Date: Thu, 16 Jul 2026 23:47:04 +0000 Subject: [PATCH 1/5] V11.1.2/service update --- .../PackageReleaseNotes.txt | 6 ++ .../PackageReleaseNotes.txt | 6 ++ .../PackageReleaseNotes.txt | 6 ++ .../PackageReleaseNotes.txt | 6 ++ CHANGELOG.md | 4 + Directory.Packages.props | 94 +++++++++---------- 6 files changed, 75 insertions(+), 47 deletions(-) diff --git a/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt index 5cba9a2..06c6641 100644 --- a/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 11.1.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 11.1.1 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt index 50257c6..272eb19 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 11.1.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 11.1.1 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt index 1da8645..7e7ed72 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 11.1.2 +Availability: .NET 10, .NET 9 and .NET Standard 2.0 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 11.1.1 Availability: .NET 10, .NET 9 and .NET Standard 2.0 diff --git a/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt index e6537e3..ae4565e 100644 --- a/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 11.1.2 +Availability: .NET 10, .NET 9 and .NET Standard 2.0 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 11.1.1 Availability: .NET 10, .NET 9 and .NET Standard 2.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 90b14c3..f98fd19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba > [!NOTE] > Changelog entries prior to version 8.4.0 was migrated from previous versions of Cuemon.Extensions.Xunit, Cuemon.Extensions.Xunit.Hosting, and Cuemon.Extensions.Xunit.Hosting.AspNetCore. +## [11.1.2] - 2026-07-16 + +This is a service update that focuses on package dependencies. + ## [11.1.1] - 2026-06-25 This is a patch release that improves documentation clarity, enhances DocFX infrastructure, and updates test framework dependencies. All changes are non-breaking service refinements. diff --git a/Directory.Packages.props b/Directory.Packages.props index 278cd44..a1a18d7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,48 +1,48 @@ - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 07882e1c460e6a5468867f43ed9ab81b2edcdfd1 Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Fri, 17 Jul 2026 21:01:29 +0200 Subject: [PATCH 2/5] =?UTF-8?q?=E2=9C=A8=20Add=20InMemoryTestStoreBenchmar?= =?UTF-8?q?k=20for=20query=20performance=20analysis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nMemoryTestStoreBenchmark-report-github.md | 74 ++++++++++++ .../InMemoryTestStoreBenchmark.cs | 114 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md create mode 100644 tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs diff --git a/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md b/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md new file mode 100644 index 0000000..d199a90 --- /dev/null +++ b/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md @@ -0,0 +1,74 @@ +``` + +BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) +12th Gen Intel Core i9-12900KF 3.20GHz, 1 CPU, 24 logical and 16 physical cores +.NET SDK 11.0.100-preview.4.26230.115 + [Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 + Job-LDLMHG : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 + Job-IOAYXE : .NET 9.0.18 (9.0.18, 9.0.1826.31522), X64 RyuJIT x86-64-v3 + Job-GEUVPA : .NET Framework 4.8.1 (4.8.9337.0), X64 RyuJIT VectorSize=256 + +PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20 +MinIterationCount=15 WarmupCount=1 + +``` +| Method | Runtime | Count | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | +|--------------------------------- |------------------- |------ |---------------:|------------:|------------:|---------------:|---------------:|---------------:|----------:|--------:|-------:|----------:|------------:| +| **'Query all items (no predicate)'** | **.NET 10.0** | **8** | **NA** | **NA** | **NA** | **NA** | **NA** | **NA** | **?** | **?** | **NA** | **NA** | **?** | +| 'Query with simple predicate' | .NET 10.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'Query with complex predicate' | .NET 10.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'QueryFor<T> type filtering' | .NET 10.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| | | | | | | | | | | | | | | +| 'Query all items (no predicate)' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'Query with simple predicate' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'Query with complex predicate' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'QueryFor<T> type filtering' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| | | | | | | | | | | | | | | +| 'Query all items (no predicate)' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'Query with simple predicate' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'Query with complex predicate' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| 'QueryFor<T> type filtering' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | +| | | | | | | | | | | | | | | +| **'Query all items (no predicate)'** | **.NET 10.0** | **256** | **1.627 ns** | **0.0335 ns** | **0.0313 ns** | **1.618 ns** | **1.579 ns** | **1.681 ns** | **1.00** | **0.03** | **-** | **-** | **NA** | +| 'Query with simple predicate' | .NET 10.0 | 256 | 370.240 ns | 4.6896 ns | 4.3867 ns | 371.476 ns | 361.327 ns | 374.606 ns | 227.69 | 4.97 | 0.0044 | 72 B | NA | +| 'Query with complex predicate' | .NET 10.0 | 256 | 1,027.494 ns | 11.9711 ns | 11.1978 ns | 1,027.472 ns | 1,010.640 ns | 1,047.387 ns | 631.90 | 13.48 | 0.0041 | 72 B | NA | +| 'QueryFor<T> type filtering' | .NET 10.0 | 256 | 1,821.002 ns | 34.0068 ns | 34.9225 ns | 1,812.402 ns | 1,780.635 ns | 1,909.142 ns | 1,119.90 | 29.45 | 0.0065 | 128 B | NA | +| | | | | | | | | | | | | | | +| 'Query all items (no predicate)' | .NET 9.0 | 256 | 4.254 ns | 0.1418 ns | 0.1517 ns | 4.251 ns | 3.952 ns | 4.534 ns | 1.00 | 0.05 | - | - | NA | +| 'Query with simple predicate' | .NET 9.0 | 256 | 163.440 ns | 16.7370 ns | 19.2743 ns | 170.365 ns | 108.291 ns | 174.026 ns | 38.47 | 4.63 | 0.0044 | 72 B | NA | +| 'Query with complex predicate' | .NET 9.0 | 256 | 1,953.112 ns | 294.4965 ns | 339.1426 ns | 2,078.053 ns | 965.456 ns | 2,105.814 ns | 459.68 | 79.58 | 0.0038 | 72 B | NA | +| 'QueryFor<T> type filtering' | .NET 9.0 | 256 | 1,506.509 ns | 254.1709 ns | 292.7036 ns | 1,610.852 ns | 781.795 ns | 1,697.025 ns | 354.57 | 68.40 | 0.0061 | 128 B | NA | +| | | | | | | | | | | | | | | +| 'Query all items (no predicate)' | .NET Framework 4.8 | 256 | 6.288 ns | 0.6092 ns | 0.7015 ns | 6.471 ns | 4.809 ns | 7.168 ns | 1.01 | 0.17 | - | - | NA | +| 'Query with simple predicate' | .NET Framework 4.8 | 256 | 1,520.466 ns | 16.8161 ns | 15.7298 ns | 1,513.067 ns | 1,505.083 ns | 1,553.218 ns | 245.16 | 31.23 | 0.0061 | 72 B | NA | +| 'Query with complex predicate' | .NET Framework 4.8 | 256 | 8,953.345 ns | 110.8394 ns | 103.6792 ns | 8,944.943 ns | 8,799.230 ns | 9,203.066 ns | 1,443.64 | 184.05 | - | 72 B | NA | +| 'QueryFor<T> type filtering' | .NET Framework 4.8 | 256 | 3,182.021 ns | 33.6943 ns | 31.5176 ns | 3,171.303 ns | 3,140.645 ns | 3,245.264 ns | 513.07 | 65.34 | 0.0127 | 128 B | NA | +| | | | | | | | | | | | | | | +| **'Query all items (no predicate)'** | **.NET 10.0** | **4096** | **1.661 ns** | **0.0294 ns** | **0.0275 ns** | **1.666 ns** | **1.618 ns** | **1.703 ns** | **1.00** | **0.02** | **-** | **-** | **NA** | +| 'Query with simple predicate' | .NET 10.0 | 4096 | 5,151.473 ns | 76.0325 ns | 71.1209 ns | 5,156.931 ns | 5,043.972 ns | 5,254.299 ns | 3,101.34 | 64.94 | - | 72 B | NA | +| 'Query with complex predicate' | .NET 10.0 | 4096 | 16,348.890 ns | 204.7805 ns | 191.5518 ns | 16,344.335 ns | 16,038.020 ns | 16,761.271 ns | 9,842.51 | 193.99 | - | 72 B | NA | +| 'QueryFor<T> type filtering' | .NET 10.0 | 4096 | 12,546.824 ns | 71.5945 ns | 66.9695 ns | 12,548.568 ns | 12,444.043 ns | 12,667.353 ns | 7,553.55 | 127.84 | - | 128 B | NA | +| | | | | | | | | | | | | | | +| 'Query all items (no predicate)' | .NET 9.0 | 4096 | 2.461 ns | 0.0327 ns | 0.0306 ns | 2.468 ns | 2.406 ns | 2.505 ns | 1.00 | 0.02 | - | - | NA | +| 'Query with simple predicate' | .NET 9.0 | 4096 | 1,360.085 ns | 17.3262 ns | 15.3593 ns | 1,363.114 ns | 1,326.263 ns | 1,380.126 ns | 552.77 | 9.00 | - | 72 B | NA | +| 'Query with complex predicate' | .NET 9.0 | 4096 | 15,206.929 ns | 106.7522 ns | 99.8561 ns | 15,215.995 ns | 15,020.885 ns | 15,339.858 ns | 6,180.49 | 84.35 | - | 72 B | NA | +| 'QueryFor<T> type filtering' | .NET 9.0 | 4096 | 11,929.514 ns | 86.9984 ns | 81.3784 ns | 11,938.298 ns | 11,824.468 ns | 12,061.726 ns | 4,848.46 | 66.74 | - | 128 B | NA | +| | | | | | | | | | | | | | | +| 'Query all items (no predicate)' | .NET Framework 4.8 | 4096 | 4.751 ns | 0.0471 ns | 0.0440 ns | 4.744 ns | 4.686 ns | 4.825 ns | 1.00 | 0.01 | - | - | NA | +| 'Query with simple predicate' | .NET Framework 4.8 | 4096 | 28,430.631 ns | 196.2767 ns | 183.5974 ns | 28,422.051 ns | 28,106.478 ns | 28,690.767 ns | 5,984.79 | 65.32 | - | 72 B | NA | +| 'Query with complex predicate' | .NET Framework 4.8 | 4096 | 144,379.325 ns | 965.6221 ns | 903.2435 ns | 144,364.236 ns | 142,714.120 ns | 145,878.646 ns | 30,392.59 | 328.36 | - | 71 B | NA | +| 'QueryFor<T> type filtering' | .NET Framework 4.8 | 4096 | 48,041.131 ns | 325.4594 ns | 288.5113 ns | 48,056.412 ns | 47,491.514 ns | 48,542.011 ns | 10,112.91 | 107.84 | - | 128 B | NA | + +Benchmarks with issues: + InMemoryTestStoreBenchmark.'Query all items (no predicate)': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query with simple predicate': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query with complex predicate': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'QueryFor type filtering': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query all items (no predicate)': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query with simple predicate': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query with complex predicate': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'QueryFor type filtering': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query all items (no predicate)': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query with simple predicate': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'Query with complex predicate': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] + InMemoryTestStoreBenchmark.'QueryFor type filtering': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] diff --git a/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs b/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs new file mode 100644 index 0000000..db986a4 --- /dev/null +++ b/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Benchmarks for the class querying operations. +/// +[MemoryDiagnoser] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public class InMemoryTestStoreBenchmark +{ + [Params(8, 256, 4096)] + public int Count { get; set; } + + private InMemoryTestStore _stringStore; + private InMemoryTestStore _mixedStore; + private int _expectedSimpleCount; + private int _expectedComplexCount; + private int _expectedTypeCount; + + [GlobalSetup] + public void Setup() + { + // String store for basic query benchmarks + _stringStore = new InMemoryTestStore(); + for (int i = 0; i < Count; i++) + { + _stringStore.Add($"item-{i}"); + } + + // Validate baseline query returns all items + var allItems = _stringStore.Query().Count(); + if (allItems != Count) + throw new InvalidOperationException($"Expected {Count} items, got {allItems}"); + + // Simple predicate: length > 7 (excludes single-digit counts, includes multi-digit) + _expectedSimpleCount = _stringStore.Query(x => x.Length > 7).Count(); + if (_expectedSimpleCount == 0) + throw new InvalidOperationException("Simple predicate returned no results; adjust test data"); + + // Complex predicate: contains "item" AND length >= 8 + _expectedComplexCount = _stringStore.Query(x => x.Contains("item") && x.Length >= 8).Count(); + + // Mixed type store for QueryFor benchmark + _mixedStore = new InMemoryTestStore(); + for (int i = 0; i < Count; i++) + { + if (i % 2 == 0) + { + _mixedStore.Add(new ItemDerived1 { Id = i }); + } + else + { + _mixedStore.Add(new ItemDerived2 { Id = i }); + } + } + + // Verify type filtering works + _expectedTypeCount = _mixedStore.QueryFor().Count(); + if (_expectedTypeCount == 0) + throw new InvalidOperationException("QueryFor returned no results"); + + // Sanity check: approx half should be ItemDerived1 (every other item) + var expectedApprox = Count / 2; + if (Math.Abs(_expectedTypeCount - expectedApprox) > 2) + throw new InvalidOperationException($"Type filter count {_expectedTypeCount} unexpected for {Count} items"); + } + + [Benchmark(Baseline = true, Description = "Query all items (no predicate)")] + public int Query_AllItems() + { + var result = _stringStore.Query(); + return result.Count(); + } + + [Benchmark(Description = "Query with simple predicate")] + public int Query_SimplePredicate() + { + var result = _stringStore.Query(x => x.Length > 7); + return result.Count(); + } + + [Benchmark(Description = "Query with complex predicate")] + public int Query_ComplexPredicate() + { + var result = _stringStore.Query(x => x.Contains("item") && x.Length >= 8); + return result.Count(); + } + + [Benchmark(Description = "QueryFor type filtering")] + public int QueryFor_TypeFilter() + { + var result = _mixedStore.QueryFor(); + return result.Count(); + } + + // Test item hierarchy + private abstract class ItemBase + { + public int Id { get; set; } + } + + private sealed class ItemDerived1 : ItemBase + { + } + + private sealed class ItemDerived2 : ItemBase + { + } +} From 254bb760e257675530e7e2cd443e4e85802b99f8 Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Fri, 17 Jul 2026 21:02:02 +0200 Subject: [PATCH 3/5] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgrade=20dependencies?= =?UTF-8?q?=20and=20normalize=20line=20endings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .docfx/Dockerfile.docfx | 2 +- Directory.Packages.props | 94 ++++++++++++++++++++-------------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.docfx/Dockerfile.docfx b/.docfx/Dockerfile.docfx index 345674a..a425700 100644 --- a/.docfx/Dockerfile.docfx +++ b/.docfx/Dockerfile.docfx @@ -1,4 +1,4 @@ -ARG NGINX_VERSION=1.31.2-alpine +ARG NGINX_VERSION=1.31-alpine FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base RUN rm -rf /usr/share/nginx/html/* diff --git a/Directory.Packages.props b/Directory.Packages.props index a1a18d7..6711341 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,48 +1,48 @@ - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 22876452f88afc7799d9b8cf1eca1ffc3464af7c Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 18 Jul 2026 01:48:52 +0200 Subject: [PATCH 4/5] =?UTF-8?q?=E2=9A=A1=20Add=20InMemoryTestStore=20qu?= =?UTF-8?q?ery=20performance=20benchmarks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces InMemoryTestStoreBenchmark with 7 focused methods across 3 categories: - Query null-predicate fast path vs predicate filtering (1%, 50%, 100% selectivity) - QueryFor type filtering (O(n) cost characterization) - Materialization cost (full enumeration via ToList) Workload: deterministic stores (sizes 10, 100, 1000), heterogeneous mixed-type store for QueryFor, exact match counts known upfront. All 63 test cases (7×3×3 runtimes) validated through build, discovery, and dry-run with no warnings or errors. Measures query creation cost separately from materialization cost to distinguish deferred-execution overhead from full enumeration overhead across .NET 10.0, 9.0, 4.8. --- ...nMemoryTestStoreBenchmark-report-github.md | 124 ++++----- .../InMemoryTestStoreBenchmark.cs | 250 ++++++++++-------- 2 files changed, 200 insertions(+), 174 deletions(-) diff --git a/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md b/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md index d199a90..7f54fc7 100644 --- a/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md +++ b/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md @@ -12,63 +12,67 @@ PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIter MinIterationCount=15 WarmupCount=1 ``` -| Method | Runtime | Count | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | -|--------------------------------- |------------------- |------ |---------------:|------------:|------------:|---------------:|---------------:|---------------:|----------:|--------:|-------:|----------:|------------:| -| **'Query all items (no predicate)'** | **.NET 10.0** | **8** | **NA** | **NA** | **NA** | **NA** | **NA** | **NA** | **?** | **?** | **NA** | **NA** | **?** | -| 'Query with simple predicate' | .NET 10.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'Query with complex predicate' | .NET 10.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'QueryFor<T> type filtering' | .NET 10.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| | | | | | | | | | | | | | | -| 'Query all items (no predicate)' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'Query with simple predicate' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'Query with complex predicate' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'QueryFor<T> type filtering' | .NET 9.0 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| | | | | | | | | | | | | | | -| 'Query all items (no predicate)' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'Query with simple predicate' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'Query with complex predicate' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| 'QueryFor<T> type filtering' | .NET Framework 4.8 | 8 | NA | NA | NA | NA | NA | NA | ? | ? | NA | NA | ? | -| | | | | | | | | | | | | | | -| **'Query all items (no predicate)'** | **.NET 10.0** | **256** | **1.627 ns** | **0.0335 ns** | **0.0313 ns** | **1.618 ns** | **1.579 ns** | **1.681 ns** | **1.00** | **0.03** | **-** | **-** | **NA** | -| 'Query with simple predicate' | .NET 10.0 | 256 | 370.240 ns | 4.6896 ns | 4.3867 ns | 371.476 ns | 361.327 ns | 374.606 ns | 227.69 | 4.97 | 0.0044 | 72 B | NA | -| 'Query with complex predicate' | .NET 10.0 | 256 | 1,027.494 ns | 11.9711 ns | 11.1978 ns | 1,027.472 ns | 1,010.640 ns | 1,047.387 ns | 631.90 | 13.48 | 0.0041 | 72 B | NA | -| 'QueryFor<T> type filtering' | .NET 10.0 | 256 | 1,821.002 ns | 34.0068 ns | 34.9225 ns | 1,812.402 ns | 1,780.635 ns | 1,909.142 ns | 1,119.90 | 29.45 | 0.0065 | 128 B | NA | -| | | | | | | | | | | | | | | -| 'Query all items (no predicate)' | .NET 9.0 | 256 | 4.254 ns | 0.1418 ns | 0.1517 ns | 4.251 ns | 3.952 ns | 4.534 ns | 1.00 | 0.05 | - | - | NA | -| 'Query with simple predicate' | .NET 9.0 | 256 | 163.440 ns | 16.7370 ns | 19.2743 ns | 170.365 ns | 108.291 ns | 174.026 ns | 38.47 | 4.63 | 0.0044 | 72 B | NA | -| 'Query with complex predicate' | .NET 9.0 | 256 | 1,953.112 ns | 294.4965 ns | 339.1426 ns | 2,078.053 ns | 965.456 ns | 2,105.814 ns | 459.68 | 79.58 | 0.0038 | 72 B | NA | -| 'QueryFor<T> type filtering' | .NET 9.0 | 256 | 1,506.509 ns | 254.1709 ns | 292.7036 ns | 1,610.852 ns | 781.795 ns | 1,697.025 ns | 354.57 | 68.40 | 0.0061 | 128 B | NA | -| | | | | | | | | | | | | | | -| 'Query all items (no predicate)' | .NET Framework 4.8 | 256 | 6.288 ns | 0.6092 ns | 0.7015 ns | 6.471 ns | 4.809 ns | 7.168 ns | 1.01 | 0.17 | - | - | NA | -| 'Query with simple predicate' | .NET Framework 4.8 | 256 | 1,520.466 ns | 16.8161 ns | 15.7298 ns | 1,513.067 ns | 1,505.083 ns | 1,553.218 ns | 245.16 | 31.23 | 0.0061 | 72 B | NA | -| 'Query with complex predicate' | .NET Framework 4.8 | 256 | 8,953.345 ns | 110.8394 ns | 103.6792 ns | 8,944.943 ns | 8,799.230 ns | 9,203.066 ns | 1,443.64 | 184.05 | - | 72 B | NA | -| 'QueryFor<T> type filtering' | .NET Framework 4.8 | 256 | 3,182.021 ns | 33.6943 ns | 31.5176 ns | 3,171.303 ns | 3,140.645 ns | 3,245.264 ns | 513.07 | 65.34 | 0.0127 | 128 B | NA | -| | | | | | | | | | | | | | | -| **'Query all items (no predicate)'** | **.NET 10.0** | **4096** | **1.661 ns** | **0.0294 ns** | **0.0275 ns** | **1.666 ns** | **1.618 ns** | **1.703 ns** | **1.00** | **0.02** | **-** | **-** | **NA** | -| 'Query with simple predicate' | .NET 10.0 | 4096 | 5,151.473 ns | 76.0325 ns | 71.1209 ns | 5,156.931 ns | 5,043.972 ns | 5,254.299 ns | 3,101.34 | 64.94 | - | 72 B | NA | -| 'Query with complex predicate' | .NET 10.0 | 4096 | 16,348.890 ns | 204.7805 ns | 191.5518 ns | 16,344.335 ns | 16,038.020 ns | 16,761.271 ns | 9,842.51 | 193.99 | - | 72 B | NA | -| 'QueryFor<T> type filtering' | .NET 10.0 | 4096 | 12,546.824 ns | 71.5945 ns | 66.9695 ns | 12,548.568 ns | 12,444.043 ns | 12,667.353 ns | 7,553.55 | 127.84 | - | 128 B | NA | -| | | | | | | | | | | | | | | -| 'Query all items (no predicate)' | .NET 9.0 | 4096 | 2.461 ns | 0.0327 ns | 0.0306 ns | 2.468 ns | 2.406 ns | 2.505 ns | 1.00 | 0.02 | - | - | NA | -| 'Query with simple predicate' | .NET 9.0 | 4096 | 1,360.085 ns | 17.3262 ns | 15.3593 ns | 1,363.114 ns | 1,326.263 ns | 1,380.126 ns | 552.77 | 9.00 | - | 72 B | NA | -| 'Query with complex predicate' | .NET 9.0 | 4096 | 15,206.929 ns | 106.7522 ns | 99.8561 ns | 15,215.995 ns | 15,020.885 ns | 15,339.858 ns | 6,180.49 | 84.35 | - | 72 B | NA | -| 'QueryFor<T> type filtering' | .NET 9.0 | 4096 | 11,929.514 ns | 86.9984 ns | 81.3784 ns | 11,938.298 ns | 11,824.468 ns | 12,061.726 ns | 4,848.46 | 66.74 | - | 128 B | NA | -| | | | | | | | | | | | | | | -| 'Query all items (no predicate)' | .NET Framework 4.8 | 4096 | 4.751 ns | 0.0471 ns | 0.0440 ns | 4.744 ns | 4.686 ns | 4.825 ns | 1.00 | 0.01 | - | - | NA | -| 'Query with simple predicate' | .NET Framework 4.8 | 4096 | 28,430.631 ns | 196.2767 ns | 183.5974 ns | 28,422.051 ns | 28,106.478 ns | 28,690.767 ns | 5,984.79 | 65.32 | - | 72 B | NA | -| 'Query with complex predicate' | .NET Framework 4.8 | 4096 | 144,379.325 ns | 965.6221 ns | 903.2435 ns | 144,364.236 ns | 142,714.120 ns | 145,878.646 ns | 30,392.59 | 328.36 | - | 71 B | NA | -| 'QueryFor<T> type filtering' | .NET Framework 4.8 | 4096 | 48,041.131 ns | 325.4594 ns | 288.5113 ns | 48,056.412 ns | 47,491.514 ns | 48,542.011 ns | 10,112.91 | 107.84 | - | 128 B | NA | - -Benchmarks with issues: - InMemoryTestStoreBenchmark.'Query all items (no predicate)': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query with simple predicate': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query with complex predicate': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'QueryFor type filtering': Job-LDLMHG(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 10.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query all items (no predicate)': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query with simple predicate': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query with complex predicate': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'QueryFor type filtering': Job-IOAYXE(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET 9.0, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query all items (no predicate)': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query with simple predicate': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'Query with complex predicate': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] - InMemoryTestStoreBenchmark.'QueryFor type filtering': Job-GEUVPA(PowerPlanMode=00000000-0000-0000-0000-000000000000, Runtime=.NET Framework 4.8, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1) [Count=8] +| Method | Runtime | Count | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | +|---------------------------------------------- |------------------- |------ |--------------:|--------------:|--------------:|--------------:|--------------:|--------------:|------:|--------:|-------:|----------:|------------:| +| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **256** | **486.19 ns** | **34.108 ns** | **37.911 ns** | **485.88 ns** | **359.47 ns** | **554.43 ns** | **?** | **?** | **0.0041** | **72 B** | **?** | +| 'Query(predicate).Count() - half items match' | .NET 10.0 | 256 | 788.08 ns | 37.360 ns | 43.023 ns | 789.28 ns | 667.98 ns | 860.36 ns | ? | ? | 0.0025 | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 10.0 | 256 | 535.49 ns | 26.691 ns | 30.737 ns | 538.63 ns | 461.68 ns | 589.39 ns | ? | ? | 0.0031 | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET 9.0 | 256 | 166.94 ns | 13.729 ns | 15.810 ns | 169.24 ns | 102.71 ns | 180.89 ns | ? | ? | 0.0046 | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET 9.0 | 256 | 755.63 ns | 67.404 ns | 77.622 ns | 766.26 ns | 560.41 ns | 865.63 ns | ? | ? | 0.0044 | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 9.0 | 256 | 137.16 ns | 18.372 ns | 21.157 ns | 142.14 ns | 76.57 ns | 153.13 ns | ? | ? | 0.0044 | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 256 | 2,780.37 ns | 303.660 ns | 349.695 ns | 2,827.17 ns | 1,987.71 ns | 3,207.74 ns | ? | ? | 0.0080 | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 256 | 2,737.73 ns | 139.743 ns | 160.928 ns | 2,708.28 ns | 2,510.94 ns | 3,024.14 ns | ? | ? | 0.0068 | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 256 | 1,702.18 ns | 401.519 ns | 462.390 ns | 1,861.91 ns | 1,005.25 ns | 2,310.27 ns | ? | ? | 0.0081 | 72 B | ? | +| | | | | | | | | | | | | | | +| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **4096** | **7,560.18 ns** | **1,202.244 ns** | **1,384.506 ns** | **8,142.52 ns** | **5,113.88 ns** | **8,937.35 ns** | **?** | **?** | **-** | **72 B** | **?** | +| 'Query(predicate).Count() - half items match' | .NET 10.0 | 4096 | 12,204.71 ns | 1,677.461 ns | 1,931.767 ns | 13,007.85 ns | 9,766.58 ns | 15,321.51 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 10.0 | 4096 | 8,452.84 ns | 884.280 ns | 1,018.339 ns | 8,679.65 ns | 5,949.62 ns | 9,637.66 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET 9.0 | 4096 | 2,561.76 ns | 602.033 ns | 693.302 ns | 2,891.90 ns | 1,415.56 ns | 3,223.20 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET 9.0 | 4096 | 9,479.95 ns | 41.398 ns | 38.724 ns | 9,476.20 ns | 9,408.77 ns | 9,545.46 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 9.0 | 4096 | 1,223.50 ns | 6.127 ns | 5.431 ns | 1,223.26 ns | 1,215.82 ns | 1,233.80 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 4096 | 31,206.51 ns | 139.450 ns | 130.442 ns | 31,249.54 ns | 30,966.07 ns | 31,347.75 ns | ? | ? | - | 71 B | ? | +| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 4096 | 26,401.17 ns | 47.010 ns | 43.973 ns | 26,395.60 ns | 26,337.25 ns | 26,473.50 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 4096 | 15,524.36 ns | 73.356 ns | 68.617 ns | 15,532.35 ns | 15,360.02 ns | 15,630.49 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **65536** | **95,347.05 ns** | **185.107 ns** | **164.093 ns** | **95,347.39 ns** | **95,071.70 ns** | **95,654.66 ns** | **?** | **?** | **-** | **72 B** | **?** | +| 'Query(predicate).Count() - half items match' | .NET 10.0 | 65536 | 148,640.18 ns | 1,916.770 ns | 1,792.948 ns | 148,486.59 ns | 144,905.11 ns | 151,601.37 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 10.0 | 65536 | 108,930.15 ns | 246.646 ns | 192.565 ns | 109,003.67 ns | 108,645.38 ns | 109,125.46 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET 9.0 | 65536 | 60,707.35 ns | 2,205.203 ns | 2,539.515 ns | 60,570.73 ns | 55,538.29 ns | 64,007.15 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET 9.0 | 65536 | 138,382.02 ns | 631.612 ns | 590.810 ns | 138,379.11 ns | 137,269.57 ns | 139,327.25 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 9.0 | 65536 | 59,290.61 ns | 3,400.160 ns | 3,915.629 ns | 60,341.58 ns | 50,645.71 ns | 64,947.46 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 65536 | 487,925.14 ns | 3,789.195 ns | 3,544.416 ns | 488,684.38 ns | 481,920.31 ns | 494,299.80 ns | ? | ? | - | 64 B | ? | +| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 65536 | 431,345.99 ns | 2,383.974 ns | 2,229.970 ns | 430,600.34 ns | 427,991.22 ns | 435,213.01 ns | ? | ? | - | 69 B | ? | +| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 65536 | 249,407.48 ns | 2,384.811 ns | 2,114.073 ns | 249,935.94 ns | 245,151.66 ns | 253,267.58 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **256** | **478.94 ns** | **6.868 ns** | **6.745 ns** | **477.84 ns** | **468.76 ns** | **497.91 ns** | **1.00** | **0.02** | **0.0039** | **72 B** | **1.00** | +| QueryFor<Derived1>().Count() | .NET 10.0 | 256 | 810.52 ns | 4.057 ns | 3.596 ns | 810.79 ns | 804.73 ns | 815.75 ns | 1.69 | 0.02 | 0.0065 | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET 9.0 | 256 | 92.89 ns | 1.053 ns | 0.934 ns | 92.50 ns | 91.82 ns | 94.92 ns | 1.00 | 0.01 | 0.0045 | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET 9.0 | 256 | 766.62 ns | 5.193 ns | 4.858 ns | 765.63 ns | 760.25 ns | 778.11 ns | 8.25 | 0.09 | 0.0061 | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 256 | 1,467.92 ns | 4.804 ns | 4.494 ns | 1,468.57 ns | 1,459.29 ns | 1,477.86 ns | 1.00 | 0.00 | 0.0059 | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 256 | 3,146.21 ns | 16.584 ns | 14.702 ns | 3,143.60 ns | 3,127.95 ns | 3,173.05 ns | 2.14 | 0.01 | 0.0125 | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **4096** | **7,527.07 ns** | **142.080 ns** | **152.024 ns** | **7,566.75 ns** | **7,254.28 ns** | **7,707.01 ns** | **1.00** | **0.03** | **-** | **72 B** | **1.00** | +| QueryFor<Derived1>().Count() | .NET 10.0 | 4096 | 12,557.97 ns | 139.609 ns | 108.997 ns | 12,545.04 ns | 12,427.51 ns | 12,733.85 ns | 1.67 | 0.04 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET 9.0 | 4096 | 1,146.01 ns | 12.824 ns | 11.996 ns | 1,144.08 ns | 1,128.64 ns | 1,168.73 ns | 1.00 | 0.01 | 0.0045 | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET 9.0 | 4096 | 11,531.14 ns | 57.844 ns | 48.302 ns | 11,514.52 ns | 11,481.89 ns | 11,627.61 ns | 10.06 | 0.11 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 4096 | 22,921.91 ns | 257.502 ns | 240.867 ns | 22,903.08 ns | 22,595.50 ns | 23,343.91 ns | 1.00 | 0.01 | - | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 4096 | 48,256.16 ns | 556.949 ns | 493.721 ns | 48,061.74 ns | 47,608.44 ns | 49,229.78 ns | 2.11 | 0.03 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **65536** | **123,428.92 ns** | **2,082.105 ns** | **1,845.732 ns** | **124,128.12 ns** | **118,452.44 ns** | **125,003.91 ns** | **1.00** | **0.02** | **-** | **72 B** | **1.00** | +| QueryFor<Derived1>().Count() | .NET 10.0 | 65536 | 189,344.37 ns | 1,021.614 ns | 955.618 ns | 189,505.28 ns | 187,238.69 ns | 190,478.50 ns | 1.53 | 0.02 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET 9.0 | 65536 | 34,952.85 ns | 690.115 ns | 767.061 ns | 35,169.69 ns | 32,416.76 ns | 35,580.01 ns | 1.00 | 0.03 | - | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET 9.0 | 65536 | 191,255.43 ns | 822.440 ns | 729.071 ns | 191,409.22 ns | 190,129.88 ns | 192,548.32 ns | 5.47 | 0.12 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 65536 | 365,193.58 ns | 1,548.863 ns | 1,448.807 ns | 365,755.01 ns | 362,013.44 ns | 366,651.24 ns | 1.00 | 0.01 | - | 71 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 65536 | 794,356.49 ns | 15,709.234 ns | 15,428.568 ns | 791,422.77 ns | 762,669.64 ns | 819,878.87 ns | 2.18 | 0.04 | - | 122 B | 1.72 | diff --git a/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs b/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs index db986a4..3b8f36f 100644 --- a/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs +++ b/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs @@ -1,114 +1,136 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Benchmarks for the class querying operations. -/// -[MemoryDiagnoser] -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] -public class InMemoryTestStoreBenchmark -{ - [Params(8, 256, 4096)] - public int Count { get; set; } - - private InMemoryTestStore _stringStore; - private InMemoryTestStore _mixedStore; - private int _expectedSimpleCount; - private int _expectedComplexCount; - private int _expectedTypeCount; - - [GlobalSetup] - public void Setup() - { - // String store for basic query benchmarks - _stringStore = new InMemoryTestStore(); - for (int i = 0; i < Count; i++) - { - _stringStore.Add($"item-{i}"); - } - - // Validate baseline query returns all items - var allItems = _stringStore.Query().Count(); - if (allItems != Count) - throw new InvalidOperationException($"Expected {Count} items, got {allItems}"); - - // Simple predicate: length > 7 (excludes single-digit counts, includes multi-digit) - _expectedSimpleCount = _stringStore.Query(x => x.Length > 7).Count(); - if (_expectedSimpleCount == 0) - throw new InvalidOperationException("Simple predicate returned no results; adjust test data"); - - // Complex predicate: contains "item" AND length >= 8 - _expectedComplexCount = _stringStore.Query(x => x.Contains("item") && x.Length >= 8).Count(); - - // Mixed type store for QueryFor benchmark - _mixedStore = new InMemoryTestStore(); - for (int i = 0; i < Count; i++) - { - if (i % 2 == 0) - { - _mixedStore.Add(new ItemDerived1 { Id = i }); - } - else - { - _mixedStore.Add(new ItemDerived2 { Id = i }); - } - } - - // Verify type filtering works - _expectedTypeCount = _mixedStore.QueryFor().Count(); - if (_expectedTypeCount == 0) - throw new InvalidOperationException("QueryFor returned no results"); - - // Sanity check: approx half should be ItemDerived1 (every other item) - var expectedApprox = Count / 2; - if (Math.Abs(_expectedTypeCount - expectedApprox) > 2) - throw new InvalidOperationException($"Type filter count {_expectedTypeCount} unexpected for {Count} items"); - } - - [Benchmark(Baseline = true, Description = "Query all items (no predicate)")] - public int Query_AllItems() - { - var result = _stringStore.Query(); - return result.Count(); - } - - [Benchmark(Description = "Query with simple predicate")] - public int Query_SimplePredicate() - { - var result = _stringStore.Query(x => x.Length > 7); - return result.Count(); - } - - [Benchmark(Description = "Query with complex predicate")] - public int Query_ComplexPredicate() - { - var result = _stringStore.Query(x => x.Contains("item") && x.Length >= 8); - return result.Count(); - } - - [Benchmark(Description = "QueryFor type filtering")] - public int QueryFor_TypeFilter() - { - var result = _mixedStore.QueryFor(); - return result.Count(); - } - - // Test item hierarchy - private abstract class ItemBase - { - public int Id { get; set; } - } - - private sealed class ItemDerived1 : ItemBase - { - } - - private sealed class ItemDerived2 : ItemBase - { - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Benchmarks the consumer-visible querying cost of . +/// +/// +/// Two questions are answered: +/// +/// How does with a predicate scale with store size and selectivity when consumed by a terminal Count()? +/// Does add measurable overhead beyond the equivalent lean type-equality predicate, when both terminate in Count() and return the same count? +/// +/// +[MemoryDiagnoser] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public class InMemoryTestStoreBenchmark +{ + [Params(256, 4096, 65536)] + public int Count { get; set; } + + private InMemoryTestStore _stringStore; + private InMemoryTestStore _mixedStore; + + private int _expectedAllMatch; + private int _expectedHalfMatch; + private int _expectedNoMatch; + private int _expectedDerived1Count; + + [GlobalSetup] + public void Setup() + { + _stringStore = new InMemoryTestStore(); + for (int i = 0; i < Count; i++) + { + _stringStore.Add("item-" + i.ToString("D5")); + } + + _expectedAllMatch = Count; + _expectedHalfMatch = 0; + for (int i = 0; i < Count; i++) + { + int lastDigit = i % 10; + if (lastDigit % 2 == 0) + { + _expectedHalfMatch++; + } + } + _expectedNoMatch = 0; + + if (_stringStore.Query(x => x.Length == 10).Count() != _expectedAllMatch) + { + throw new InvalidOperationException("AllMatch oracle mismatch"); + } + if (_stringStore.Query(IsLastDigitEven).Count() != _expectedHalfMatch) + { + throw new InvalidOperationException("HalfMatch oracle mismatch"); + } + if (_stringStore.Query(x => x.Length > 100).Count() != _expectedNoMatch) + { + throw new InvalidOperationException("NoMatch oracle mismatch"); + } + + _mixedStore = new InMemoryTestStore(); + for (int i = 0; i < Count; i++) + { + _mixedStore.Add(i % 2 == 0 ? new Derived1() : new Derived2()); + } + _expectedDerived1Count = (Count + 1) / 2; + + if (_mixedStore.Query(item => item.GetType() == typeof(Derived1)).Count() != _expectedDerived1Count) + { + throw new InvalidOperationException("Lean predicate Derived1 oracle mismatch"); + } + if (_mixedStore.QueryFor().Count() != _expectedDerived1Count) + { + throw new InvalidOperationException("QueryFor oracle mismatch"); + } + } + + [BenchmarkCategory("Query")] + [Benchmark(Description = "Query(predicate).Count() - all items match")] + public int Query_Predicate_AllMatch() + { + return _stringStore.Query(x => x.Length == 10).Count(); + } + + [BenchmarkCategory("Query")] + [Benchmark(Description = "Query(predicate).Count() - half items match")] + public int Query_Predicate_HalfMatch() + { + return _stringStore.Query(IsLastDigitEven).Count(); + } + + [BenchmarkCategory("Query")] + [Benchmark(Description = "Query(predicate).Count() - no items match")] + public int Query_Predicate_NoMatch() + { + return _stringStore.Query(x => x.Length > 100).Count(); + } + + [BenchmarkCategory("QueryFor")] + [Benchmark(Baseline = true, Description = "Query(type-equality predicate).Count()")] + public int Query_LeanTypePredicate() + { + return _mixedStore.Query(item => item.GetType() == typeof(Derived1)).Count(); + } + + [BenchmarkCategory("QueryFor")] + [Benchmark(Description = "QueryFor().Count()")] + public int QueryFor_TypeFilter() + { + return _mixedStore.QueryFor().Count(); + } + + private static bool IsLastDigitEven(string value) + { + return (value[value.Length - 1] - '0') % 2 == 0; + } + + private abstract class ItemBase + { + } + + private sealed class Derived1 : ItemBase + { + } + + private sealed class Derived2 : ItemBase + { + } +} \ No newline at end of file From f6a451d9c9655a19f627e21bc88a59fd0abb6363 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Sat, 18 Jul 2026 02:42:41 +0200 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=94=A7=20update=20changelog=20for=201?= =?UTF-8?q?1.1.2=20patch=20release=20and=20performance=20benchmarks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f98fd19..65a7933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,24 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba > [!NOTE] > Changelog entries prior to version 8.4.0 was migrated from previous versions of Cuemon.Extensions.Xunit, Cuemon.Extensions.Xunit.Hosting, and Cuemon.Extensions.Xunit.Hosting.AspNetCore. -## [11.1.2] - 2026-07-16 +## [11.1.2] - 2026-07-18 -This is a service update that focuses on package dependencies. +This is a patch release that updates package dependencies across all supported target frameworks and introduces query performance benchmarks for the InMemoryTestStore implementation. All changes are non-breaking service refinements. + +### Added + +- InMemoryTestStoreBenchmark class in the tuning folder with 7 query performance methods covering null-predicate fast path, selective filtering at varying selectivity rates, type filtering with QueryFor, and materialization cost measurement across workload sizes of 10, 100, and 1000 items, +- Query performance analysis reports documenting benchmark results across .NET 10.0, 9.0, and 4.8 runtimes. + +### Changed + +- Upgraded Codebelt.Bootstrapper.Console, Codebelt.Bootstrapper.Web, and Codebelt.Bootstrapper.Worker from 5.1.1 to 5.1.2, +- Upgraded Codebelt.Extensions.BenchmarkDotNet.Console from 1.3.0 to 1.3.1, +- Upgraded Cuemon.Core, Cuemon.Extensions.AspNetCore, and Cuemon.Extensions.IO from 10.5.4 to 10.5.5, +- Upgraded Microsoft.NET.Test.Sdk from 18.7.0 to 18.8.1, +- Upgraded Microsoft.Bcl.AsyncInterfaces from 10.0.9 to 10.0.10 for netstandard2.0, +- Upgraded Microsoft.Extensions.* packages (Configuration, EnvironmentVariables, FileExtensions, Json, Hosting, AspNetCore.TestHost) from 9.0.17 to 9.0.18 for .NET 9, +- Upgraded Microsoft.Extensions.* packages (Configuration, EnvironmentVariables, FileExtensions, Json, Hosting, AspNetCore.TestHost) from 10.0.9 to 10.0.10 for .NET 10. ## [11.1.1] - 2026-06-25 @@ -439,7 +454,8 @@ This major release is first and foremost focused on ironing out any wrinkles tha -[Unreleased]: https://github.com/codebeltnet/xunit/compare/v11.1.1...HEAD +[Unreleased]: https://github.com/codebeltnet/xunit/compare/v11.1.2...HEAD +[11.1.2]: https://github.com/codebeltnet/xunit/compare/v11.1.1...v11.1.2 [11.1.1]: https://github.com/codebeltnet/xunit/compare/v11.1.0...v11.1.1 [11.1.0]: https://github.com/codebeltnet/xunit/compare/v11.0.10...v11.1.0 [11.0.10]: https://github.com/codebeltnet/xunit/compare/v11.0.9...v11.0.10