using AyCode.Core.Extensions; using AyCode.Core.Interfaces; using BenchmarkDotNet.Attributes; using Newtonsoft.Json; using System.Text; namespace AyCode.Core.Benchmarks; [MemoryDiagnoser] public class SerializationBenchmarks { // Complex graph with 7 levels, ~1500 objects, cross-references private Level1_Company _complexGraph = null!; // Pre-serialized JSON for deserialization benchmarks private string _newtonsoftJson = null!; private string _ayCodeJson = null!; // Settings private JsonSerializerSettings _newtonsoftNoRefSettings = null!; private JsonSerializerSettings _ayCodeSettings = null!; [GlobalSetup] public void Setup() { // Newtonsoft WITHOUT reference handling (baseline) _newtonsoftNoRefSettings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, // Fair comparison - also skip defaults Formatting = Formatting.None }; // AyCode WITH reference handling (our optimized solution) _ayCodeSettings = SerializeObjectExtensions.Options; // Create complex 7-level graph with ~1500 objects and cross-references _complexGraph = CreateComplexGraph(); // Pre-serialize for deserialization benchmarks _newtonsoftJson = JsonConvert.SerializeObject(_complexGraph, _newtonsoftNoRefSettings); _ayCodeJson = _complexGraph.ToJson(_ayCodeSettings); // Output sizes for comparison var newtonsoftBytes = Encoding.UTF8.GetByteCount(_newtonsoftJson); var ayCodeBytes = Encoding.UTF8.GetByteCount(_ayCodeJson); Console.WriteLine("=== JSON Size Comparison ==="); Console.WriteLine($"Newtonsoft (skip defaults): {_newtonsoftJson.Length:N0} chars ({newtonsoftBytes:N0} bytes, {newtonsoftBytes / 1024.0:F1} KB)"); Console.WriteLine($"AcJsonSerializer (refs+skip): {_ayCodeJson.Length:N0} chars ({ayCodeBytes:N0} bytes, {ayCodeBytes / 1024.0:F1} KB)"); Console.WriteLine($"Size reduction: {(1.0 - (double)ayCodeBytes / newtonsoftBytes) * 100:F1}%"); Console.WriteLine($"Bytes saved: {newtonsoftBytes - ayCodeBytes:N0}"); } #region Serialization Benchmarks [Benchmark(Description = "Newtonsoft (no refs)")] [BenchmarkCategory("Serialize")] public string Serialize_Newtonsoft_NoRefs() => JsonConvert.SerializeObject(_complexGraph, _newtonsoftNoRefSettings); [Benchmark(Description = "AyCode (with refs)")] [BenchmarkCategory("Serialize")] public string Serialize_AyCode_WithRefs() => _complexGraph.ToJson(_ayCodeSettings); #endregion #region Deserialization Benchmarks [Benchmark(Description = "Newtonsoft (no refs)")] [BenchmarkCategory("Deserialize")] public Level1_Company? Deserialize_Newtonsoft_NoRefs() => JsonConvert.DeserializeObject(_newtonsoftJson, _newtonsoftNoRefSettings); [Benchmark(Description = "AyCode (with refs)")] [BenchmarkCategory("Deserialize")] public Level1_Company? Deserialize_AyCode_WithRefs() => _ayCodeJson.JsonTo(_ayCodeSettings); [Benchmark(Description = "AcJsonDeserializer (custom)")] [BenchmarkCategory("Deserialize")] public Level1_Company? Deserialize_AcJsonDeserializer() => AcJsonDeserializer.Deserialize(_ayCodeJson); #endregion #region JSON Size Comparison (not timed, just for reporting) [Benchmark(Description = "JSON Size - Newtonsoft")] [BenchmarkCategory("Size")] public int JsonSize_Newtonsoft() => _newtonsoftJson.Length; [Benchmark(Description = "JSON Size - AyCode")] [BenchmarkCategory("Size")] public int JsonSize_AyCode() => _ayCodeJson.Length; #endregion #region Complex Graph Factory - 7 Levels, ~1500 objects, Cross-references private static int _idCounter = 1; /// /// Creates a 7-level deep graph with approximately 1500 objects and cross-references. /// Structure: Company -> Departments -> Teams -> Projects -> Tasks -> SubTasks -> Comments /// Each object has 8-15 properties of various types. /// private static Level1_Company CreateComplexGraph() { _idCounter = 1; // Shared references (cross-references across the graph) var sharedTags = Enumerable.Range(1, 10) .Select(i => new SharedTag { Id = _idCounter++, Name = $"Tag-{i}", Color = $"#{i:X2}{i * 10:X2}{i * 20:X2}", Priority = i % 5, IsActive = i % 2 == 0, CreatedAt = DateTime.UtcNow.AddDays(-i * 10), Metadata = $"Metadata for tag {i}" }) .ToList(); var sharedCategories = Enumerable.Range(1, 5) .Select(i => new SharedCategory { Id = _idCounter++, Name = $"Category-{i}", Description = $"Description for category {i} with some extra text to make it longer", SortOrder = i * 100, IconUrl = $"https://icons.example.com/cat-{i}.png", IsDefault = i == 1, ParentCategoryId = i > 1 ? i - 1 : null, CreatedAt = DateTime.UtcNow.AddMonths(-i), UpdatedAt = DateTime.UtcNow.AddDays(-i) }) .ToList(); var sharedUser = new SharedUser { Id = _idCounter++, Username = "admin", Email = "admin@company.com", FirstName = "System", LastName = "Administrator", PhoneNumber = "+1-555-0100", IsActive = true, Role = UserRole.Admin, LastLoginAt = DateTime.UtcNow.AddHours(-1), CreatedAt = DateTime.UtcNow.AddYears(-2), Preferences = new UserPreferences { Theme = "dark", Language = "en-US", NotificationsEnabled = true, EmailDigestFrequency = "daily" } }; // Level 1: Company (1 object) var company = new Level1_Company { Id = _idCounter++, Name = "TechCorp International", LegalName = "TechCorp International Holdings Ltd.", TaxId = "TC-123456789", FoundedDate = new DateTime(2010, 3, 15), EmployeeCount = 1500, AnnualRevenue = 125_000_000.50m, IsPubliclyTraded = true, StockSymbol = "TECH", HeadquartersAddress = "123 Innovation Drive, Tech City, TC 12345", Website = "https://www.techcorp.example.com", PrimaryContact = sharedUser, MainCategory = sharedCategories[0], Tags = [sharedTags[0], sharedTags[1], sharedTags[2]], CreatedAt = DateTime.UtcNow.AddYears(-5), UpdatedAt = DateTime.UtcNow }; // Level 2: Departments (5 objects) company.Departments = Enumerable.Range(1, 5).Select(deptIdx => new Level2_Department { Id = _idCounter++, Name = $"Department-{deptIdx}", Code = $"DEPT-{deptIdx:D3}", Description = $"This is department {deptIdx} responsible for various operations and strategic initiatives", Budget = 1_000_000m + (deptIdx * 250_000m), HeadCount = 50 + (deptIdx * 20), Location = $"Building {(char)('A' + deptIdx - 1)}, Floor {deptIdx}", CostCenter = $"CC-{1000 + deptIdx}", IsActive = true, Manager = sharedUser, // Cross-reference Category = sharedCategories[deptIdx % sharedCategories.Count], // Cross-reference Tags = [sharedTags[deptIdx % sharedTags.Count], sharedTags[(deptIdx + 1) % sharedTags.Count]], // Cross-reference EstablishedDate = DateTime.UtcNow.AddYears(-4).AddMonths(deptIdx), CreatedAt = DateTime.UtcNow.AddYears(-4), UpdatedAt = DateTime.UtcNow.AddMonths(-deptIdx), // Level 3: Teams (6 per department = 30 total) Teams = Enumerable.Range(1, 6).Select(teamIdx => new Level3_Team { Id = _idCounter++, Name = $"Team-{deptIdx}-{teamIdx}", Acronym = $"T{deptIdx}{teamIdx}", Description = $"Team {teamIdx} in department {deptIdx}, focused on delivering excellence", MemberCount = 5 + (teamIdx * 2), Capacity = 10 + (teamIdx * 2), Utilization = 0.65 + (teamIdx * 0.05), SprintLength = 14, VelocityAverage = 42.5 + teamIdx, IsRemote = teamIdx % 3 == 0, Timezone = teamIdx % 2 == 0 ? "UTC" : "America/New_York", SlackChannel = $"#team-{deptIdx}-{teamIdx}", TeamLead = sharedUser, // Cross-reference PrimaryTag = sharedTags[(deptIdx + teamIdx) % sharedTags.Count], // Cross-reference CreatedAt = DateTime.UtcNow.AddYears(-3).AddMonths(teamIdx), UpdatedAt = DateTime.UtcNow.AddDays(-teamIdx * 7), // Level 4: Projects (4 per team = 120 total) Projects = Enumerable.Range(1, 4).Select(projIdx => new Level4_Project { Id = _idCounter++, Name = $"Project-{deptIdx}-{teamIdx}-{projIdx}", Code = $"PRJ-{deptIdx}{teamIdx}{projIdx:D2}", Description = $"Project {projIdx} for team {teamIdx}, delivering key business value and innovation", Status = (ProjectStatus)(projIdx % 4), Priority = (Priority)(projIdx % 3), Budget = 50_000m + (projIdx * 15_000m), SpentAmount = 25_000m + (projIdx * 5_000m), ProgressPercent = 0.1 + (projIdx * 0.2), StartDate = DateTime.UtcNow.AddMonths(-projIdx * 2), DueDate = DateTime.UtcNow.AddMonths(projIdx), CompletedDate = projIdx == 4 ? DateTime.UtcNow.AddDays(-10) : null, EstimatedHours = 200 + (projIdx * 50), ActualHours = 150 + (projIdx * 40), RiskLevel = projIdx % 3, Owner = sharedUser, // Cross-reference Category = sharedCategories[projIdx % sharedCategories.Count], // Cross-reference Tags = [sharedTags[projIdx % sharedTags.Count]], // Cross-reference CreatedAt = DateTime.UtcNow.AddMonths(-projIdx * 3), UpdatedAt = DateTime.UtcNow.AddDays(-projIdx), // Level 5: Tasks (5 per project = 600 total) Tasks = Enumerable.Range(1, 5).Select(taskIdx => new Level5_Task { Id = _idCounter++, Title = $"Task-{deptIdx}-{teamIdx}-{projIdx}-{taskIdx}", Description = $"Detailed task description for task {taskIdx} in project {projIdx}. This includes requirements and acceptance criteria.", Status = (TaskStatus)(taskIdx % 5), Priority = (Priority)(taskIdx % 3), Type = (TaskType)(taskIdx % 4), StoryPoints = taskIdx * 2, EstimatedHours = 4 + taskIdx * 2, ActualHours = 3 + taskIdx * 1.5, DueDate = DateTime.UtcNow.AddDays(taskIdx * 3), CompletedDate = taskIdx <= 2 ? DateTime.UtcNow.AddDays(-taskIdx) : null, IsBlocked = taskIdx == 3, BlockedReason = taskIdx == 3 ? "Waiting for external dependency" : null, Assignee = sharedUser, // Cross-reference Reporter = sharedUser, // Cross-reference Labels = [sharedTags[taskIdx % sharedTags.Count]], // Cross-reference CreatedAt = DateTime.UtcNow.AddDays(-taskIdx * 5), UpdatedAt = DateTime.UtcNow.AddHours(-taskIdx), // Level 6: SubTasks (3 per task = 1800 total -> we'll limit to keep ~1500) SubTasks = Enumerable.Range(1, 2).Select(subIdx => new Level6_SubTask { Id = _idCounter++, Title = $"SubTask-{taskIdx}-{subIdx}", Description = $"Sub-task {subIdx} details for completing parent task {taskIdx}", Status = (TaskStatus)(subIdx % 5), EstimatedMinutes = 30 + subIdx * 15, ActualMinutes = 25 + subIdx * 12, IsCompleted = subIdx == 1, CompletedAt = subIdx == 1 ? DateTime.UtcNow.AddHours(-subIdx * 2) : null, Assignee = sharedUser, // Cross-reference CreatedAt = DateTime.UtcNow.AddDays(-subIdx), UpdatedAt = DateTime.UtcNow.AddMinutes(-subIdx * 30), // Level 7: Comments (2 per subtask = 2400 total -> limiting) Comments = Enumerable.Range(1, 1).Select(comIdx => new Level7_Comment { Id = _idCounter++, Text = $"Comment {comIdx} on subtask {subIdx}: This is a detailed comment with feedback and suggestions for improvement.", Author = sharedUser, // Cross-reference IsEdited = comIdx % 2 == 0, EditedAt = comIdx % 2 == 0 ? DateTime.UtcNow.AddHours(-1) : null, LikeCount = comIdx * 3, ReplyCount = comIdx, CreatedAt = DateTime.UtcNow.AddHours(-comIdx * 4), MentionedTags = [sharedTags[comIdx % sharedTags.Count]] // Cross-reference }).ToList() }).ToList() }).ToList() }).ToList() }).ToList() }).ToList(); return company; } #endregion #region 7-Level Deep DTOs with 8-15 Properties Each // Shared cross-reference types public class SharedTag : IId { public int Id { get; set; } public string Name { get; set; } = ""; public string Color { get; set; } = ""; public int Priority { get; set; } public bool IsActive { get; set; } public DateTime CreatedAt { get; set; } public string Metadata { get; set; } = ""; } public class SharedCategory : IId { public int Id { get; set; } public string Name { get; set; } = ""; public string Description { get; set; } = ""; public int SortOrder { get; set; } public string IconUrl { get; set; } = ""; public bool IsDefault { get; set; } public int? ParentCategoryId { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } } public class SharedUser : IId { public int Id { get; set; } public string Username { get; set; } = ""; public string Email { get; set; } = ""; public string FirstName { get; set; } = ""; public string LastName { get; set; } = ""; public string PhoneNumber { get; set; } = ""; public bool IsActive { get; set; } public UserRole Role { get; set; } public DateTime? LastLoginAt { get; set; } public DateTime CreatedAt { get; set; } public UserPreferences? Preferences { get; set; } } public class UserPreferences { public string Theme { get; set; } = ""; public string Language { get; set; } = ""; public bool NotificationsEnabled { get; set; } public string EmailDigestFrequency { get; set; } = ""; } public enum UserRole { User, Manager, Admin } public enum ProjectStatus { Planning, Active, OnHold, Completed } public enum TaskStatus { Backlog, Todo, InProgress, Review, Done } public enum TaskType { Feature, Bug, Improvement, Task } public enum Priority { Low, Medium, High } // Level 1: Company (15 properties) public class Level1_Company : IId { public int Id { get; set; } public string Name { get; set; } = ""; public string LegalName { get; set; } = ""; public string TaxId { get; set; } = ""; public DateTime FoundedDate { get; set; } public int EmployeeCount { get; set; } public decimal AnnualRevenue { get; set; } public bool IsPubliclyTraded { get; set; } public string? StockSymbol { get; set; } public string HeadquartersAddress { get; set; } = ""; public string Website { get; set; } = ""; public SharedUser? PrimaryContact { get; set; } // Cross-ref public SharedCategory? MainCategory { get; set; } // Cross-ref public List Tags { get; set; } = []; // Cross-ref public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public List Departments { get; set; } = []; } // Level 2: Department (15 properties) public class Level2_Department : IId { public int Id { get; set; } public string Name { get; set; } = ""; public string Code { get; set; } = ""; public string Description { get; set; } = ""; public decimal Budget { get; set; } public int HeadCount { get; set; } public string Location { get; set; } = ""; public string CostCenter { get; set; } = ""; public bool IsActive { get; set; } public SharedUser? Manager { get; set; } // Cross-ref public SharedCategory? Category { get; set; } // Cross-ref public List Tags { get; set; } = []; // Cross-ref public DateTime EstablishedDate { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public List Teams { get; set; } = []; } // Level 3: Team (15 properties) public class Level3_Team : IId { public int Id { get; set; } public string Name { get; set; } = ""; public string Acronym { get; set; } = ""; public string Description { get; set; } = ""; public int MemberCount { get; set; } public int Capacity { get; set; } public double Utilization { get; set; } public int SprintLength { get; set; } public double VelocityAverage { get; set; } public bool IsRemote { get; set; } public string Timezone { get; set; } = ""; public string SlackChannel { get; set; } = ""; public SharedUser? TeamLead { get; set; } // Cross-ref public SharedTag? PrimaryTag { get; set; } // Cross-ref public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public List Projects { get; set; } = []; } // Level 4: Project (18 properties) public class Level4_Project : IId { public int Id { get; set; } public string Name { get; set; } = ""; public string Code { get; set; } = ""; public string Description { get; set; } = ""; public ProjectStatus Status { get; set; } public Priority Priority { get; set; } public decimal Budget { get; set; } public decimal SpentAmount { get; set; } public double ProgressPercent { get; set; } public DateTime StartDate { get; set; } public DateTime DueDate { get; set; } public DateTime? CompletedDate { get; set; } public int EstimatedHours { get; set; } public int ActualHours { get; set; } public int RiskLevel { get; set; } public SharedUser? Owner { get; set; } // Cross-ref public SharedCategory? Category { get; set; } // Cross-ref public List Tags { get; set; } = []; // Cross-ref public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public List Tasks { get; set; } = []; } // Level 5: Task (18 properties) public class Level5_Task : IId { public int Id { get; set; } public string Title { get; set; } = ""; public string Description { get; set; } = ""; public TaskStatus Status { get; set; } public Priority Priority { get; set; } public TaskType Type { get; set; } public int StoryPoints { get; set; } public double EstimatedHours { get; set; } public double ActualHours { get; set; } public DateTime DueDate { get; set; } public DateTime? CompletedDate { get; set; } public bool IsBlocked { get; set; } public string? BlockedReason { get; set; } public SharedUser? Assignee { get; set; } // Cross-ref public SharedUser? Reporter { get; set; } // Cross-ref public List Labels { get; set; } = []; // Cross-ref public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public List SubTasks { get; set; } = []; } // Level 6: SubTask (11 properties) public class Level6_SubTask : IId { public int Id { get; set; } public string Title { get; set; } = ""; public string Description { get; set; } = ""; public TaskStatus Status { get; set; } public int EstimatedMinutes { get; set; } public int ActualMinutes { get; set; } public bool IsCompleted { get; set; } public DateTime? CompletedAt { get; set; } public SharedUser? Assignee { get; set; } // Cross-ref public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public List Comments { get; set; } = []; } // Level 7: Comment (10 properties) public class Level7_Comment : IId { public int Id { get; set; } public string Text { get; set; } = ""; public SharedUser? Author { get; set; } // Cross-ref public bool IsEdited { get; set; } public DateTime? EditedAt { get; set; } public int LikeCount { get; set; } public int ReplyCount { get; set; } public DateTime CreatedAt { get; set; } public List MentionedTags { get; set; } = []; // Cross-ref } #endregion #region AcJsonSerializer Benchmarks [Benchmark(Description = "AcJsonSerializer (custom)")] [BenchmarkCategory("Serialize")] public string Serialize_AcJsonSerializer() => AcJsonSerializer.Serialize(_complexGraph); #endregion }