using AyCode.Core.Compression; using AyCode.Core.Serializers.Attributes; using AyCode.Core.Serializers.Binaries; using AyCode.Core.Tests.Serialization; // DrainFromAsync extension (test-only, used by benchmark) using AyCode.Core.Tests.TestModels; using MemoryPack; #if !AYCODE_NATIVEAOT using MessagePack; using MessagePack.Resolvers; #endif using Microsoft.Extensions.Options; using System.Buffers; using System.Diagnostics; using System.IO.Pipelines; using System.IO.Pipes; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; using AyCode.Core.Serializers.Console.Benchmarks; namespace AyCode.Core.Serializers.Console; /// /// Comprehensive benchmark application for all serializers. /// Compares: AcBinary (all options), MemoryPack, MessagePack, Newtonsoft.Json, System.Text.Json /// /// Usage: /// dotnet run # Run all benchmarks /// dotnet run -- quick # Quick mode (fewer iterations) /// dotnet run -- serialize # Serialize only /// dotnet run -- deserialize # Deserialize only /// public static class Program { // Configuration (constants, mutable state, attribute-flag aggregation) → Configuration.cs // BuildAcBinary + GetMemPack helpers → Benchmarks/BenchmarkOptions.cs public static void Main(string[] args) { // Set console encoding to UTF-8 for proper Unicode character display System.Console.OutputEncoding = Encoding.UTF8; // Setup validation — abort BEFORE any benchmark logic if MemoryPack baseline is invalid. // Done early so user is told immediately, not after warmup. BenchmarkLoop.ValidateMemoryPackSetup(); // CLI mode (args provided): run once, parse args, exit. Backward-compatible behaviour. if (args.Length > 0) { if (!TryParseCliArgs(args, out var layer, out var opMode, out var serializerMode)) return; // invalid args BenchmarkLoop.RunBenchmark(layer, opMode, serializerMode); return; } // Interactive mode (no args): loop the menu so the user doesn't have to restart between runs. // Q exits the menu (and the application). while (true) { var selection = Menu.ShowInteractiveMenu(); if (selection == null) return; // user pressed Q BenchmarkLoop.RunBenchmark(selection.Value.layer, "all", selection.Value.serializerMode); System.Console.WriteLine(); System.Console.WriteLine("─────────────────────────────────────────────────────────────────────"); System.Console.WriteLine("Returning to menu — press any key to continue, or Q to quit..."); var key = System.Console.ReadKey(intercept: true); if (key.Key == ConsoleKey.Q) return; System.Console.WriteLine(); } } /// /// Parses CLI arguments into (layer, opMode, serializerMode). Returns false if the args /// are invalid; the caller should then exit without running the standard benchmark. /// private static bool TryParseCliArgs(string[] args, out string layer, out string opMode, out string serializerMode) { layer = "all"; opMode = "all"; serializerMode = "standard"; var arg = args[0].ToLower(); // Quick mode: short warmup, few iterations, small sample count if (arg == "quick") { Configuration.WarmupIterations = 5; Configuration.TestIterations = 100; Configuration.BenchmarkSamples = 3; layer = "all"; } else if (arg is "core" or "comprehensive" or "edge" or "all" or "small" or "medium" or "large" or "repeated" or "deep") { layer = arg; } else if (arg is "asyncpipe" or "pipe") { // AsyncPipe-only mode: streaming I/O isolation across all test data. layer = "all"; serializerMode = "asyncpipe"; } else if (arg is "ser" or "serialize") { opMode = "serialize"; layer = "all"; } else if (arg is "des" or "deserialize") { opMode = "deserialize"; layer = "all"; } else { // Backwards compat: unknown arg → treat as layer keyword layer = arg; } return true; } // RunBenchmark + RunBenchmarksForTestData + CreateSerializers → BenchmarkLoop.cs // Serializer implementations (ISerializerBenchmark + 12 concrete benchmark classes) → Benchmarks/ // Results / output formatters → Output.cs // BenchmarkResult DTO → BenchmarkResult.cs }