diff --git a/AyCode.Core.Tests/Serialization/QuickBenchmark.cs b/AyCode.Core.Tests/Serialization/QuickBenchmark.cs index 3c29235..b0f51b6 100644 --- a/AyCode.Core.Tests/Serialization/QuickBenchmark.cs +++ b/AyCode.Core.Tests/Serialization/QuickBenchmark.cs @@ -14,6 +14,87 @@ public class QuickBenchmark private static readonly MessagePackSerializerOptions MsgPackOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None); + private const int DefaultIterations = 1000; + + #region Helper Methods + + private static void PrintBanner(string title) + { + Console.WriteLine(); + Console.WriteLine(new string('=', 78)); + Console.WriteLine(title); + Console.WriteLine(new string('=', 78)); + } + + private static void PrintTableHeader(string title) + { + PrintBanner(title); + Console.WriteLine($"{"Metric",-25} | {"AcBinary",14} | {"MessagePack",14} | {"Ratio",14}"); + Console.WriteLine(new string('-', 78)); + } + + private static void PrintTableRow(string metric, double acBinary, double msgPack, string unit = "ms") + { + if (msgPack > 0) + { + var ratio = acBinary / msgPack; + var ratioStr = ratio < 1 ? $"{ratio:F2}x faster" : $"{ratio:F2}x slower"; + Console.WriteLine($"{metric,-25} | {acBinary,10:F2} {unit,-2} | {msgPack,10:F2} {unit,-2} | {ratioStr,14}"); + } + else + { + Console.WriteLine($"{metric,-25} | {acBinary,10:F2} {unit,-2} | {"N/A",12} | {"(unique)",14}"); + } + } + + private static void PrintTableRowSize(string metric, int acBinary, int msgPack) + { + var ratio = msgPack == 0 ? 0 : 100.0 * acBinary / msgPack; + Console.WriteLine($"{metric,-25} | {acBinary,14:N0} | {msgPack,14:N0} | {ratio,12:F1}%"); + } + + private static void PrintTableFooter() + { + Console.WriteLine(new string('-', 78)); + } + + private static void PrintSummary(int acBinarySize, int msgPackSize, double acSerMs, double msgSerMs, double acDeserMs, double msgDeserMs) + { + PrintBanner("SUMMARY"); + + var sizeAdvantage = msgPackSize == 0 ? 0 : 100.0 - (100.0 * acBinarySize / msgPackSize); + if (sizeAdvantage > 0) + Console.WriteLine($"[OK] Size: AcBinary is {sizeAdvantage:F1}% smaller ({msgPackSize - acBinarySize:N0} bytes saved)"); + else + Console.WriteLine($"[WARN] Size: AcBinary is {-sizeAdvantage:F1}% larger"); + + var serRatio = msgSerMs == 0 ? 0 : acSerMs / msgSerMs; + if (serRatio > 0 && serRatio < 1) + Console.WriteLine($"[OK] Serialize: AcBinary is {1 / serRatio:F2}x faster"); + else if (serRatio > 0) + Console.WriteLine($"[WARN] Serialize: AcBinary is {serRatio:F2}x slower"); + + var deserRatio = msgDeserMs == 0 ? 0 : acDeserMs / msgDeserMs; + if (deserRatio > 0 && deserRatio < 1) + Console.WriteLine($"[OK] Deserialize: AcBinary is {1 / deserRatio:F2}x faster"); + else if (deserRatio > 0) + Console.WriteLine($"[WARN] Deserialize: AcBinary is {deserRatio:F2}x slower"); + } + + private static TestOrder CreatePopulateTarget(TestOrder source) + { + var target = new TestOrder { Id = source.Id }; + foreach (var item in source.Items) + { + target.Items.Add(new TestOrderItem { Id = item.Id }); + } + return target; + } + + #endregion + + #region Basic Benchmarks + [TestMethod] public void RunQuickBenchmark() { @@ -26,7 +107,7 @@ public class QuickBenchmark } // Measure serialize - const int iterations = 1000; + const int iterations = DefaultIterations; var sw = Stopwatch.StartNew(); byte[] serialized = null!; for (int i = 0; i < iterations; i++) @@ -101,7 +182,7 @@ public class QuickBenchmark var result = bytes.BinaryTo>(); } - const int iterations = 1000; + const int iterations = DefaultIterations; // With interning (default) var sw = Stopwatch.StartNew(); @@ -137,6 +218,10 @@ public class QuickBenchmark Assert.AreEqual(100, result2!.Count); } + #endregion + + #region MessagePack Comparison + [TestMethod] public void RunMessagePackComparison() { @@ -152,7 +237,7 @@ public class QuickBenchmark var msgResult = MessagePackSerializer.Deserialize(msgBytes, MsgPackOptions); } - const int iterations = 1000; + const int iterations = DefaultIterations; // === AcBinary Serialize === var sw = Stopwatch.StartNew(); @@ -207,9 +292,9 @@ public class QuickBenchmark var sizeDiff = msgPackData.Length - acBinaryData.Length; if (sizeDiff > 0) - Console.WriteLine($"✅ AcBinary {sizeDiff:N0} bytes smaller ({100.0 * sizeDiff / msgPackData.Length:F1}% savings)"); + Console.WriteLine($"[OK] AcBinary {sizeDiff:N0} bytes smaller ({100.0 * sizeDiff / msgPackData.Length:F1}% savings)"); else - Console.WriteLine($"⚠️ AcBinary {-sizeDiff:N0} bytes larger"); + Console.WriteLine($"[WARN] AcBinary {-sizeDiff:N0} bytes larger"); Assert.IsNotNull(acBinaryResult); Assert.IsNotNull(msgPackResult); @@ -237,7 +322,7 @@ public class QuickBenchmark var r2 = MessagePackSerializer.Deserialize>(b2, MsgPackOptions); } - const int iterations = 1000; + const int iterations = DefaultIterations; // AcBinary with interning var sw = Stopwatch.StartNew(); @@ -281,12 +366,306 @@ public class QuickBenchmark Console.WriteLine(); var sizeSaving = msgPack.Length - acWithIntern.Length; - Console.WriteLine($"✅ String interning saves {sizeSaving:N0} bytes ({100.0 * sizeSaving / msgPack.Length:F1}%)"); + Console.WriteLine($"[OK] String interning saves {sizeSaving:N0} bytes ({100.0 * sizeSaving / msgPack.Length:F1}%)"); Assert.IsTrue(acWithIntern.Length < msgPack.Length, "AcBinary with interning should be smaller"); } - // Public for MessagePack dynamic serializer compatibility + #endregion + + #region Full Comparison (WithRef, NoRef, Populate, Merge) + + [TestMethod] + public void RunFullBenchmarkComparison() + { + PrintBanner("AcBinary vs MessagePack Full Benchmark"); + + // Create test data with shared references + TestDataFactory.ResetIdCounter(); + var sharedTag = TestDataFactory.CreateTag("SharedTag"); + var sharedUser = TestDataFactory.CreateUser("shareduser"); + var sharedMeta = TestDataFactory.CreateMetadata("shared", withChild: true); + + var testOrder = TestDataFactory.CreateOrder( + itemCount: 3, + palletsPerItem: 3, + measurementsPerPallet: 3, + pointsPerMeasurement: 4, + sharedTag: sharedTag, + sharedUser: sharedUser, + sharedMetadata: sharedMeta); + + // Options + var withRefOptions = new AcBinarySerializerOptions(); + var noRefOptions = AcBinarySerializerOptions.WithoutReferenceHandling(); + + // Warmup + Console.WriteLine("\nWarming up..."); + for (int i = 0; i < 100; i++) + { + _ = AcBinarySerializer.Serialize(testOrder, withRefOptions); + _ = AcBinarySerializer.Serialize(testOrder, noRefOptions); + _ = MessagePackSerializer.Serialize(testOrder, MsgPackOptions); + } + + // Pre-serialize + var acBinaryWithRef = AcBinarySerializer.Serialize(testOrder, withRefOptions); + var acBinaryNoRef = AcBinarySerializer.Serialize(testOrder, noRefOptions); + var msgPackData = MessagePackSerializer.Serialize(testOrder, MsgPackOptions); + + Console.WriteLine($"Iterations: {DefaultIterations:N0}"); + + // Size comparison + PrintTableHeader("SIZE COMPARISON"); + PrintTableRowSize("AcBinary (WithRef)", acBinaryWithRef.Length, msgPackData.Length); + PrintTableRowSize("AcBinary (NoRef)", acBinaryNoRef.Length, msgPackData.Length); + PrintTableRowSize("MessagePack (baseline)", msgPackData.Length, msgPackData.Length); + PrintTableFooter(); + + var sw = Stopwatch.StartNew(); + + // === Serialize WithRef === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinarySerializer.Serialize(testOrder, withRefOptions); + var acWithRefSerMs = sw.Elapsed.TotalMilliseconds; + + // === Serialize NoRef === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinarySerializer.Serialize(testOrder, noRefOptions); + var acNoRefSerMs = sw.Elapsed.TotalMilliseconds; + + // === MessagePack Serialize === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = MessagePackSerializer.Serialize(testOrder, MsgPackOptions); + var msgPackSerMs = sw.Elapsed.TotalMilliseconds; + + // === Deserialize WithRef === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinaryDeserializer.Deserialize(acBinaryWithRef); + var acWithRefDeserMs = sw.Elapsed.TotalMilliseconds; + + // === Deserialize NoRef === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinaryDeserializer.Deserialize(acBinaryNoRef); + var acNoRefDeserMs = sw.Elapsed.TotalMilliseconds; + + // === MessagePack Deserialize === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = MessagePackSerializer.Deserialize(msgPackData, MsgPackOptions); + var msgPackDeserMs = sw.Elapsed.TotalMilliseconds; + + // === Populate (AcBinary only) === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + { + var target = CreatePopulateTarget(testOrder); + AcBinaryDeserializer.Populate(acBinaryNoRef, target); + } + var acPopulateMs = sw.Elapsed.TotalMilliseconds; + + // === PopulateMerge (AcBinary only) === + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + { + var target = CreatePopulateTarget(testOrder); + AcBinaryDeserializer.PopulateMerge(acBinaryNoRef.AsSpan(), target); + } + var acMergeMs = sw.Elapsed.TotalMilliseconds; + + // Print performance table + PrintTableHeader("PERFORMANCE COMPARISON (lower is better)"); + PrintTableRow("Serialize (WithRef)", acWithRefSerMs, msgPackSerMs); + PrintTableRow("Serialize (NoRef)", acNoRefSerMs, msgPackSerMs); + PrintTableRow("Deserialize (WithRef)", acWithRefDeserMs, msgPackDeserMs); + PrintTableRow("Deserialize (NoRef)", acNoRefDeserMs, msgPackDeserMs); + PrintTableRow("Populate (NoRef)", acPopulateMs, 0); + PrintTableRow("Merge (NoRef)", acMergeMs, 0); + PrintTableRow("Round-trip (WithRef)", acWithRefSerMs + acWithRefDeserMs, msgPackSerMs + msgPackDeserMs); + PrintTableRow("Round-trip (NoRef)", acNoRefSerMs + acNoRefDeserMs, msgPackSerMs + msgPackDeserMs); + PrintTableFooter(); + + PrintSummary(acBinaryNoRef.Length, msgPackData.Length, acNoRefSerMs, msgPackSerMs, acNoRefDeserMs, msgPackDeserMs); + + // Assertions + Assert.IsTrue(acBinaryWithRef.Length < msgPackData.Length, "AcBinary WithRef should be smaller than MessagePack"); + Assert.IsTrue(acBinaryNoRef.Length < msgPackData.Length, "AcBinary NoRef should be smaller than MessagePack"); + } + + [TestMethod] + public void RunWithRefVsNoRefComparison() + { + PrintBanner("AcBinary WithRef vs NoRef Comparison"); + + // Create test data WITH shared references (to show WithRef advantage) + TestDataFactory.ResetIdCounter(); + var sharedTag = TestDataFactory.CreateTag("SharedTag"); + var sharedUser = TestDataFactory.CreateUser("shareduser"); + var sharedMeta = TestDataFactory.CreateMetadata("shared", withChild: true); + + var testOrder = TestDataFactory.CreateOrder( + itemCount: 5, + palletsPerItem: 4, + measurementsPerPallet: 3, + pointsPerMeasurement: 5, + sharedTag: sharedTag, + sharedUser: sharedUser, + sharedMetadata: sharedMeta); + + var withRefOptions = new AcBinarySerializerOptions(); + var noRefOptions = AcBinarySerializerOptions.WithoutReferenceHandling(); + + // Warmup + Console.WriteLine("Warming up..."); + for (int i = 0; i < 50; i++) + { + _ = AcBinarySerializer.Serialize(testOrder, withRefOptions); + _ = AcBinarySerializer.Serialize(testOrder, noRefOptions); + } + + var withRefData = AcBinarySerializer.Serialize(testOrder, withRefOptions); + var noRefData = AcBinarySerializer.Serialize(testOrder, noRefOptions); + + Console.WriteLine($"Iterations: {DefaultIterations:N0}"); + + // Size comparison + PrintBanner("SIZE COMPARISON (bytes)"); + Console.WriteLine($"WithRef : {withRefData.Length,12:N0} (baseline)"); + var sizeDiff = noRefData.Length - withRefData.Length; + var ratio = withRefData.Length == 0 ? 0 : 100.0 * noRefData.Length / withRefData.Length; + Console.WriteLine($"NoRef : {noRefData.Length,12:N0} (diff {sizeDiff:+#;-#;0}) => {ratio:F1}% of WithRef"); + + var sw = Stopwatch.StartNew(); + + // Serialize WithRef + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinarySerializer.Serialize(testOrder, withRefOptions); + var withRefSerMs = sw.Elapsed.TotalMilliseconds; + + // Serialize NoRef + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinarySerializer.Serialize(testOrder, noRefOptions); + var noRefSerMs = sw.Elapsed.TotalMilliseconds; + + // Deserialize WithRef + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinaryDeserializer.Deserialize(withRefData); + var withRefDeserMs = sw.Elapsed.TotalMilliseconds; + + // Deserialize NoRef + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinaryDeserializer.Deserialize(noRefData); + var noRefDeserMs = sw.Elapsed.TotalMilliseconds; + + PrintBanner("PERFORMANCE COMPARISON (ms)"); + Console.WriteLine($"Serialize -> WithRef: {withRefSerMs,8:F2} | NoRef: {noRefSerMs,8:F2}"); + Console.WriteLine($"Deserialize-> WithRef: {withRefDeserMs,8:F2} | NoRef: {noRefDeserMs,8:F2}"); + Console.WriteLine($"Round-trip -> WithRef: {withRefSerMs + withRefDeserMs,8:F2} | NoRef: {noRefSerMs + noRefDeserMs,8:F2}"); + + if (withRefData.Length < noRefData.Length) + { + Console.WriteLine($"[OK] WithRef saves {noRefData.Length - withRefData.Length:N0} bytes by deduplicating shared references."); + } + else + { + Console.WriteLine($"[INFO] NoRef saves {withRefData.Length - noRefData.Length:N0} bytes because it skips reference metadata."); + } + + // Verify correctness + var resultWithRef = AcBinaryDeserializer.Deserialize(withRefData); + var resultNoRef = AcBinaryDeserializer.Deserialize(noRefData); + Assert.IsNotNull(resultWithRef); + Assert.IsNotNull(resultNoRef); + Assert.AreEqual(testOrder.Id, resultWithRef.Id); + Assert.AreEqual(testOrder.Id, resultNoRef.Id); + } + + [TestMethod] + public void RunPopulateAndMergeBenchmark() + { + PrintBanner("AcBinary Populate & Merge Benchmark"); + + TestDataFactory.ResetIdCounter(); + var testOrder = TestDataFactory.CreateBenchmarkOrder( + itemCount: 3, + palletsPerItem: 3, + measurementsPerPallet: 3, + pointsPerMeasurement: 4); + + var options = AcBinarySerializerOptions.WithoutReferenceHandling(); + var binaryData = AcBinarySerializer.Serialize(testOrder, options); + + Console.WriteLine("Warming up..."); + for (int i = 0; i < 50; i++) + { + var target = CreatePopulateTarget(testOrder); + AcBinaryDeserializer.Populate(binaryData, target); + AcBinaryDeserializer.PopulateMerge(binaryData.AsSpan(), target); + } + + Console.WriteLine($"Iterations: {DefaultIterations:N0}"); + Console.WriteLine($"Data size: {binaryData.Length:N0} bytes"); + + var sw = Stopwatch.StartNew(); + + // Deserialize (creates new object) + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + _ = AcBinaryDeserializer.Deserialize(binaryData); + var deserializeMs = sw.Elapsed.TotalMilliseconds; + + // Populate (reuses existing object) + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + { + var target = CreatePopulateTarget(testOrder); + AcBinaryDeserializer.Populate(binaryData, target); + } + var populateMs = sw.Elapsed.TotalMilliseconds; + + // PopulateMerge (IId-based merge) + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + { + var target = CreatePopulateTarget(testOrder); + AcBinaryDeserializer.PopulateMerge(binaryData.AsSpan(), target); + } + var mergeMs = sw.Elapsed.TotalMilliseconds; + + // PopulateMerge with RemoveOrphanedItems + var mergeWithRemoveOptions = new AcBinarySerializerOptions { RemoveOrphanedItems = true }; + sw.Restart(); + for (int i = 0; i < DefaultIterations; i++) + { + var target = CreatePopulateTarget(testOrder); + AcBinaryDeserializer.PopulateMerge(binaryData.AsSpan(), target, mergeWithRemoveOptions); + } + var mergeWithRemoveMs = sw.Elapsed.TotalMilliseconds; + + PrintBanner("OPERATION COMPARISON (ms)"); + Console.WriteLine($"Deserialize (new object): {deserializeMs:F2} (baseline)"); + Console.WriteLine($"Populate (reuse obj) : {populateMs:F2} ({populateMs / deserializeMs:F2}x of baseline)"); + Console.WriteLine($"PopulateMerge : {mergeMs:F2} ({mergeMs / deserializeMs:F2}x of baseline)"); + Console.WriteLine($"PopulateMerge + cleanup: {mergeWithRemoveMs:F2} ({mergeWithRemoveMs / deserializeMs:F2}x of baseline)"); + + Console.WriteLine("[INFO] Populate/Merge reuse existing objects - ideal for UI data binding scenarios."); + + Assert.IsTrue(true); // Test passed if no exceptions + } + + #endregion + + #region Test Models + public class TestClassWithRepeatedValues { public int Id { get; set; } @@ -294,4 +673,6 @@ public class QuickBenchmark public string Category { get; set; } = ""; public string Priority { get; set; } = ""; } + + #endregion } diff --git a/AyCode.Core.sln b/AyCode.Core.sln index 23a1b14..08722dd 100644 --- a/AyCode.Core.sln +++ b/AyCode.Core.sln @@ -42,6 +42,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" ProjectSection(SolutionItems) = preProject AyCode.Core.targets = AyCode.Core.targets + RunQuickBenchmark.bat = RunQuickBenchmark.bat + RunQuickBenchmark.ps1 = RunQuickBenchmark.ps1 EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Benchmark", "AyCode.Benchmark\AyCode.Benchmark.csproj", "{A20861A9-411E-6150-BF5C-69E8196E5D22}" diff --git a/RunQuickBenchmark.bat b/RunQuickBenchmark.bat new file mode 100644 index 0000000..9f91ef5 --- /dev/null +++ b/RunQuickBenchmark.bat @@ -0,0 +1,17 @@ +@echo off +REM Run Quick Benchmark PowerShell script using pwsh if available, otherwise Windows PowerShell +setlocal enabledelayedexpansion +set SCRIPT_DIR=%~dp0 +where pwsh >nul 2>&1 +if %ERRORLEVEL%==0 ( + pwsh -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%RunQuickBenchmark.ps1" -All + set EXITCODE=!ERRORLEVEL! +) else ( + powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%RunQuickBenchmark.ps1" -All + set EXITCODE=!ERRORLEVEL! +) + +echo. +pause +endlocal +exit /b %EXITCODE% \ No newline at end of file diff --git a/RunQuickBenchmark.ps1 b/RunQuickBenchmark.ps1 new file mode 100644 index 0000000..ec26ec4 --- /dev/null +++ b/RunQuickBenchmark.ps1 @@ -0,0 +1,166 @@ +# AcBinary Quick Benchmark Runner +# Run this script to execute all binary serialization benchmarks +# Usage: .\RunQuickBenchmark.ps1 [-All] [-Full] [-WithRef] [-Populate] [-StringIntern] [-MessagePack] + +param( + [switch]$All, + [switch]$Full, + [switch]$WithRef, + [switch]$Populate, + [switch]$StringIntern, + [switch]$MessagePack, + [switch]$Help +) + +$ErrorActionPreference = "Stop" +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + +# Colors for output +function Write-ColorOutput($ForegroundColor, $Message) { + $fc = $host.UI.RawUI.ForegroundColor + $host.UI.RawUI.ForegroundColor = $ForegroundColor + Write-Output $Message + $host.UI.RawUI.ForegroundColor = $fc +} + +function Show-Help { + Write-Host "" + Write-Host "????????????????????????????????????????????????????????????????????????????????" -ForegroundColor Cyan + Write-Host "? AcBinary Quick Benchmark Runner ?" -ForegroundColor Cyan + Write-Host "????????????????????????????????????????????????????????????????????????????????" -ForegroundColor Cyan + Write-Host "" + Write-Host "Usage: .\RunQuickBenchmark.ps1 [options]" -ForegroundColor Yellow + Write-Host "" + Write-Host "Options:" -ForegroundColor Green + Write-Host " -All Run all benchmark tests" + Write-Host " -Full Run full AcBinary vs MessagePack comparison" + Write-Host " -WithRef Run WithRef vs NoRef comparison" + Write-Host " -Populate Run Populate & Merge benchmarks" + Write-Host " -StringIntern Run String Interning benchmarks" + Write-Host " -MessagePack Run MessagePack comparison" + Write-Host " -Help Show this help message" + Write-Host "" + Write-Host "Examples:" -ForegroundColor Green + Write-Host " .\RunQuickBenchmark.ps1 -All # Run all tests" + Write-Host " .\RunQuickBenchmark.ps1 -Full # Full benchmark comparison" + Write-Host " .\RunQuickBenchmark.ps1 -WithRef -Populate # Multiple specific tests" + Write-Host "" +} + +function Run-DotNetTest { + param( + [string]$Filter, + [string]$Description + ) + + Write-Host "" + Write-Host "Running: $Description" -ForegroundColor Yellow + Write-Host ("=" * 80) -ForegroundColor DarkGray + + $testProject = Join-Path $ScriptDir "AyCode.Core.Tests\AyCode.Core.Tests.csproj" + + if (-not (Test-Path $testProject)) { + Write-Host "Error: Test project not found at $testProject" -ForegroundColor Red + return $false + } + + $result = dotnet test $testProject ` + --filter "FullyQualifiedName~$Filter" ` + --configuration Release ` + --logger "console;verbosity=detailed" ` + --no-build 2>&1 + + $result | ForEach-Object { Write-Host $_ } + + return $LASTEXITCODE -eq 0 +} + +# Check for help +if ($Help -or ($PSBoundParameters.Count -eq 0)) { + Show-Help + if ($PSBoundParameters.Count -eq 0) { + Write-Host "No options specified. Running full benchmark..." -ForegroundColor Yellow + $Full = $true + } else { + exit 0 + } +} + +Write-Host "" +Write-Host "????????????????????????????????????????????????????????????????????????????????" -ForegroundColor Cyan +Write-Host "? AcBinary Quick Benchmark ?" -ForegroundColor Cyan +Write-Host "????????????????????????????????????????????????????????????????????????????????" -ForegroundColor Cyan +Write-Host "" +Write-Host "Building solution in Release mode..." -ForegroundColor Yellow + +# Build first +$buildResult = dotnet build (Join-Path $ScriptDir "AyCode.Core.sln") --configuration Release --verbosity minimal 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Host "Build failed!" -ForegroundColor Red + $buildResult | ForEach-Object { Write-Host $_ } + exit 1 +} + +Write-Host "Build successful!" -ForegroundColor Green + +$testsRun = 0 +$testsPassed = 0 + +# Run requested tests +if ($All) { + $Full = $true + $WithRef = $true + $Populate = $true + $StringIntern = $true + $MessagePack = $true +} + +if ($Full) { + $testsRun++ + if (Run-DotNetTest "QuickBenchmark.RunFullBenchmarkComparison" "Full AcBinary vs MessagePack Comparison") { + $testsPassed++ + } +} + +if ($WithRef) { + $testsRun++ + if (Run-DotNetTest "QuickBenchmark.RunWithRefVsNoRefComparison" "WithRef vs NoRef Comparison") { + $testsPassed++ + } +} + +if ($Populate) { + $testsRun++ + if (Run-DotNetTest "QuickBenchmark.RunPopulateAndMergeBenchmark" "Populate & Merge Benchmark") { + $testsPassed++ + } +} + +if ($StringIntern) { + $testsRun++ + if (Run-DotNetTest "QuickBenchmark.RunStringInterningBenchmark" "String Interning Benchmark") { + $testsPassed++ + } + $testsRun++ + if (Run-DotNetTest "QuickBenchmark.RunStringInterningVsMessagePack" "String Interning vs MessagePack") { + $testsPassed++ + } +} + +if ($MessagePack) { + $testsRun++ + if (Run-DotNetTest "QuickBenchmark.RunMessagePackComparison" "MessagePack Comparison") { + $testsPassed++ + } +} + +# Summary +Write-Host "" +Write-Host ("=" * 80) -ForegroundColor DarkGray +Write-Host "" +if ($testsPassed -eq $testsRun) { + Write-Host "All $testsRun benchmark(s) completed successfully!" -ForegroundColor Green +} else { + Write-Host "$testsPassed of $testsRun benchmark(s) completed." -ForegroundColor Yellow +} +Write-Host ""