33using BenchmarkDotNet . Attributes ;
44using BenchmarkDotNet . Configs ;
55using BenchmarkDotNet . Order ;
6- using Foundatio . Utility ;
76using DeepClonerExt = Foundatio . Force . DeepCloner . DeepClonerExtensions ;
87using FastClonerExt = Foundatio . Utility . ObjectExtensions ;
98
@@ -24,54 +23,35 @@ namespace Foundatio.Benchmarks;
2423[ CategoriesColumn ]
2524public class DeepCloneBenchmarks
2625{
27- // Small objects - typical cache entries
2826 private SmallObject _smallObject ;
2927 private SmallObjectWithCollections _smallObjectWithCollections ;
30-
31- // Medium objects - typical queue messages
3228 private MediumNestedObject _mediumNestedObject ;
3329 private FileSpec _fileSpec ;
34-
35- // Large objects - ~10MB realistic data
3630 private LargeEventDocument _largeEventDocument ;
3731 private LargeLogBatch _largeLogBatch ;
38-
39- // Dynamic type objects - object properties with various runtime types
4032 private ObjectWithDynamicProperties _dynamicWithDictionary ;
4133 private ObjectWithDynamicProperties _dynamicWithNestedObject ;
4234 private ObjectWithDynamicProperties _dynamicWithArray ;
43-
44- // Arrays and collections
4535 private string [ ] _stringArray ;
4636 private List < MediumNestedObject > _objectList ;
4737 private Dictionary < string , LargeEventDocument > _objectDictionary ;
4838
49- // Seed for deterministic data generation
5039 private const int Seed = 42 ;
5140
5241 [ GlobalSetup ]
5342 public void Setup ( )
5443 {
5544 var random = new Random ( Seed ) ;
5645
57- // Small objects
5846 _smallObject = CreateSmallObject ( random ) ;
5947 _smallObjectWithCollections = CreateSmallObjectWithCollections ( random ) ;
60-
61- // Medium objects
6248 _mediumNestedObject = CreateMediumNestedObject ( random ) ;
6349 _fileSpec = CreateFileSpec ( random ) ;
64-
65- // Large objects (~10MB each)
6650 _largeEventDocument = CreateLargeEventDocument ( random , targetSizeBytes : 10 * 1024 * 1024 ) ;
6751 _largeLogBatch = CreateLargeLogBatch ( random , targetSizeBytes : 10 * 1024 * 1024 ) ;
68-
69- // Dynamic type objects
7052 _dynamicWithDictionary = CreateDynamicWithDictionary ( random ) ;
7153 _dynamicWithNestedObject = CreateDynamicWithNestedObject ( random ) ;
7254 _dynamicWithArray = CreateDynamicWithArray ( random ) ;
73-
74- // Arrays and collections
7555 _stringArray = CreateStringArray ( random , 1000 ) ;
7656 _objectList = CreateObjectList ( random , 100 ) ;
7757 _objectDictionary = CreateObjectDictionary ( random , 50 ) ;
@@ -86,10 +66,6 @@ public void Setup()
8666 [ BenchmarkCategory ( "SmallObject" ) ]
8767 public SmallObject FastCloner_SmallObject ( ) => FastClonerExt . DeepClone ( _smallObject ) ;
8868
89- [ Benchmark ]
90- [ BenchmarkCategory ( "SmallObject" ) ]
91- public SmallObject FastClonerGenerated_SmallObject ( ) => FastClonerExt . DeepCloneGenerated ( _smallObject ) ;
92-
9369 // ========== SmallObjectWithCollections ==========
9470 [ Benchmark ( Baseline = true ) ]
9571 [ BenchmarkCategory ( "SmallObjectWithCollections" ) ]
@@ -99,10 +75,6 @@ public void Setup()
9975 [ BenchmarkCategory ( "SmallObjectWithCollections" ) ]
10076 public SmallObjectWithCollections FastCloner_SmallObjectWithCollections ( ) => FastClonerExt . DeepClone ( _smallObjectWithCollections ) ;
10177
102- [ Benchmark ]
103- [ BenchmarkCategory ( "SmallObjectWithCollections" ) ]
104- public SmallObjectWithCollections FastClonerGenerated_SmallObjectWithCollections ( ) => FastClonerExt . DeepCloneGenerated ( _smallObjectWithCollections ) ;
105-
10678 // ========== MediumNestedObject ==========
10779 [ Benchmark ( Baseline = true ) ]
10880 [ BenchmarkCategory ( "MediumNestedObject" ) ]
@@ -112,10 +84,6 @@ public void Setup()
11284 [ BenchmarkCategory ( "MediumNestedObject" ) ]
11385 public MediumNestedObject FastCloner_MediumNestedObject ( ) => FastClonerExt . DeepClone ( _mediumNestedObject ) ;
11486
115- [ Benchmark ]
116- [ BenchmarkCategory ( "MediumNestedObject" ) ]
117- public MediumNestedObject FastClonerGenerated_MediumNestedObject ( ) => FastClonerExt . DeepCloneGenerated ( _mediumNestedObject ) ;
118-
11987 // ========== FileSpec ==========
12088 [ Benchmark ( Baseline = true ) ]
12189 [ BenchmarkCategory ( "FileSpec" ) ]
@@ -125,10 +93,6 @@ public void Setup()
12593 [ BenchmarkCategory ( "FileSpec" ) ]
12694 public FileSpec FastCloner_FileSpec ( ) => FastClonerExt . DeepClone ( _fileSpec ) ;
12795
128- [ Benchmark ]
129- [ BenchmarkCategory ( "FileSpec" ) ]
130- public FileSpec FastClonerGenerated_FileSpec ( ) => FastClonerExt . DeepCloneGenerated ( _fileSpec ) ;
131-
13296 // ========== LargeEventDocument_10MB ==========
13397 [ Benchmark ( Baseline = true ) ]
13498 [ BenchmarkCategory ( "LargeEventDocument" ) ]
@@ -138,10 +102,6 @@ public void Setup()
138102 [ BenchmarkCategory ( "LargeEventDocument" ) ]
139103 public LargeEventDocument FastCloner_LargeEventDocument_10MB ( ) => FastClonerExt . DeepClone ( _largeEventDocument ) ;
140104
141- [ Benchmark ]
142- [ BenchmarkCategory ( "LargeEventDocument" ) ]
143- public LargeEventDocument FastClonerGenerated_LargeEventDocument_10MB ( ) => FastClonerExt . DeepCloneGenerated ( _largeEventDocument ) ;
144-
145105 // ========== LargeLogBatch_10MB ==========
146106 [ Benchmark ( Baseline = true ) ]
147107 [ BenchmarkCategory ( "LargeLogBatch" ) ]
@@ -151,10 +111,6 @@ public void Setup()
151111 [ BenchmarkCategory ( "LargeLogBatch" ) ]
152112 public LargeLogBatch FastCloner_LargeLogBatch_10MB ( ) => FastClonerExt . DeepClone ( _largeLogBatch ) ;
153113
154- [ Benchmark ]
155- [ BenchmarkCategory ( "LargeLogBatch" ) ]
156- public LargeLogBatch FastClonerGenerated_LargeLogBatch_10MB ( ) => FastClonerExt . DeepCloneGenerated ( _largeLogBatch ) ;
157-
158114 // ========== DynamicWithDictionary ==========
159115 [ Benchmark ( Baseline = true ) ]
160116 [ BenchmarkCategory ( "DynamicWithDictionary" ) ]
@@ -164,10 +120,6 @@ public void Setup()
164120 [ BenchmarkCategory ( "DynamicWithDictionary" ) ]
165121 public ObjectWithDynamicProperties FastCloner_DynamicWithDictionary ( ) => FastClonerExt . DeepClone ( _dynamicWithDictionary ) ;
166122
167- [ Benchmark ]
168- [ BenchmarkCategory ( "DynamicWithDictionary" ) ]
169- public ObjectWithDynamicProperties FastClonerGenerated_DynamicWithDictionary ( ) => FastClonerExt . DeepCloneGenerated ( _dynamicWithDictionary ) ;
170-
171123 // ========== DynamicWithNestedObject ==========
172124 [ Benchmark ( Baseline = true ) ]
173125 [ BenchmarkCategory ( "DynamicWithNestedObject" ) ]
@@ -177,10 +129,6 @@ public void Setup()
177129 [ BenchmarkCategory ( "DynamicWithNestedObject" ) ]
178130 public ObjectWithDynamicProperties FastCloner_DynamicWithNestedObject ( ) => FastClonerExt . DeepClone ( _dynamicWithNestedObject ) ;
179131
180- [ Benchmark ]
181- [ BenchmarkCategory ( "DynamicWithNestedObject" ) ]
182- public ObjectWithDynamicProperties FastClonerGenerated_DynamicWithNestedObject ( ) => FastClonerExt . DeepCloneGenerated ( _dynamicWithNestedObject ) ;
183-
184132 // ========== DynamicWithArray ==========
185133 [ Benchmark ( Baseline = true ) ]
186134 [ BenchmarkCategory ( "DynamicWithArray" ) ]
@@ -190,10 +138,6 @@ public void Setup()
190138 [ BenchmarkCategory ( "DynamicWithArray" ) ]
191139 public ObjectWithDynamicProperties FastCloner_DynamicWithArray ( ) => FastClonerExt . DeepClone ( _dynamicWithArray ) ;
192140
193- [ Benchmark ]
194- [ BenchmarkCategory ( "DynamicWithArray" ) ]
195- public ObjectWithDynamicProperties FastClonerGenerated_DynamicWithArray ( ) => FastClonerExt . DeepCloneGenerated ( _dynamicWithArray ) ;
196-
197141 // ========== StringArray_1000 ==========
198142 [ Benchmark ( Baseline = true ) ]
199143 [ BenchmarkCategory ( "StringArray" ) ]
@@ -203,10 +147,6 @@ public void Setup()
203147 [ BenchmarkCategory ( "StringArray" ) ]
204148 public string [ ] FastCloner_StringArray_1000 ( ) => FastClonerExt . DeepClone ( _stringArray ) ;
205149
206- [ Benchmark ]
207- [ BenchmarkCategory ( "StringArray" ) ]
208- public string [ ] FastClonerGenerated_StringArray_1000 ( ) => FastClonerExt . DeepCloneGenerated ( _stringArray ) ;
209-
210150 // ========== ObjectList_100 ==========
211151 [ Benchmark ( Baseline = true ) ]
212152 [ BenchmarkCategory ( "ObjectList" ) ]
@@ -216,10 +156,6 @@ public void Setup()
216156 [ BenchmarkCategory ( "ObjectList" ) ]
217157 public List < MediumNestedObject > FastCloner_ObjectList_100 ( ) => FastClonerExt . DeepClone ( _objectList ) ;
218158
219- [ Benchmark ]
220- [ BenchmarkCategory ( "ObjectList" ) ]
221- public List < MediumNestedObject > FastClonerGenerated_ObjectList_100 ( ) => FastClonerExt . DeepCloneGenerated ( _objectList ) ;
222-
223159 // ========== ObjectDictionary_50 ==========
224160 [ Benchmark ( Baseline = true ) ]
225161 [ BenchmarkCategory ( "ObjectDictionary" ) ]
@@ -229,10 +165,6 @@ public void Setup()
229165 [ BenchmarkCategory ( "ObjectDictionary" ) ]
230166 public Dictionary < string , LargeEventDocument > FastCloner_ObjectDictionary_50 ( ) => FastClonerExt . DeepClone ( _objectDictionary ) ;
231167
232- [ Benchmark ]
233- [ BenchmarkCategory ( "ObjectDictionary" ) ]
234- public Dictionary < string , LargeEventDocument > FastClonerGenerated_ObjectDictionary_50 ( ) => FastClonerExt . DeepCloneGenerated ( _objectDictionary ) ;
235-
236168 private static SmallObject CreateSmallObject ( Random random )
237169 {
238170 return new SmallObject
@@ -301,14 +233,8 @@ private static FileSpec CreateFileSpec(Random random)
301233
302234 private static LargeEventDocument CreateLargeEventDocument ( Random random , int targetSizeBytes )
303235 {
304- // For 10MB target: Create extended data entries with large strings
305- // Each char in .NET is 2 bytes, plus string object overhead (~26 bytes)
306- // We want the cloned object to be ~10MB
307-
308236 var stackFrameCount = 100 ;
309237 var extendedDataCount = 200 ;
310- // Calculate string length to achieve target size
311- // Each string entry: ~50KB of chars = 25K chars
312238 var extendedDataStringLength = Math . Max ( 100 , targetSizeBytes / extendedDataCount / 2 ) ;
313239
314240 var stackFrames = new List < StackFrameInfo > ( stackFrameCount ) ;
@@ -389,8 +315,6 @@ private static LargeEventDocument CreateLargeEventDocument(Random random, int ta
389315
390316 private static LargeLogBatch CreateLargeLogBatch ( Random random , int targetSizeBytes )
391317 {
392- // Each log entry is roughly 2000-5000 bytes
393- // Target ~10MB total
394318 var entryCount = targetSizeBytes / 3000 ;
395319
396320 var entries = new List < LogEntry > ( entryCount ) ;
@@ -497,15 +421,13 @@ private static Dictionary<string, LargeEventDocument> CreateObjectDictionary(Ran
497421 var dict = new Dictionary < string , LargeEventDocument > ( count ) ;
498422 for ( int i = 0 ; i < count ; i ++ )
499423 {
500- // Create medium-sized event documents for the dictionary (~10KB each)
501424 dict [ $ "event_{ i } "] = CreateMediumEventDocument ( random ) ;
502425 }
503426 return dict ;
504427 }
505428
506429 private static LargeEventDocument CreateMediumEventDocument ( Random random )
507430 {
508- // Create a medium-sized event document (~10KB) for collection benchmarks
509431 var stackFrames = new List < StackFrameInfo > ( 5 ) ;
510432 for ( int i = 0 ; i < 5 ; i ++ )
511433 {
@@ -614,9 +536,6 @@ private static Dictionary<string, string> GenerateStringDictionary(Random random
614536 }
615537}
616538
617- /// <summary>
618- /// Small object representing a typical cache entry.
619- /// </summary>
620539public class SmallObject
621540{
622541 public int Id { get ; set ; }
@@ -626,9 +545,6 @@ public class SmallObject
626545 public double Score { get ; set ; }
627546}
628547
629- /// <summary>
630- /// Small object with collections - typical for configuration or metadata caching.
631- /// </summary>
632548public class SmallObjectWithCollections
633549{
634550 public int Id { get ; set ; }
@@ -637,9 +553,6 @@ public class SmallObjectWithCollections
637553 public Dictionary < string , string > Metadata { get ; set ; }
638554}
639555
640- /// <summary>
641- /// Medium-sized nested object representing a typical queue message or event.
642- /// </summary>
643556public class MediumNestedObject
644557{
645558 public Guid Id { get ; set ; }
@@ -654,9 +567,6 @@ public class MediumNestedObject
654567 public RequestInfo Request { get ; set ; }
655568}
656569
657- /// <summary>
658- /// File specification - used in InMemoryFileStorage.
659- /// </summary>
660570public class FileSpec
661571{
662572 public string Path { get ; set ; }
@@ -666,9 +576,6 @@ public class FileSpec
666576 public Dictionary < string , string > Data { get ; set ; }
667577}
668578
669- /// <summary>
670- /// User information - common nested object in events.
671- /// </summary>
672579public class UserInfo
673580{
674581 public string Id { get ; set ; }
@@ -677,9 +584,6 @@ public class UserInfo
677584 public List < string > Roles { get ; set ; }
678585}
679586
680- /// <summary>
681- /// HTTP request information - common in error tracking systems.
682- /// </summary>
683587public class RequestInfo
684588{
685589 public string Method { get ; set ; }
@@ -690,9 +594,6 @@ public class RequestInfo
690594 public string UserAgent { get ; set ; }
691595}
692596
693- /// <summary>
694- /// Stack frame information for error tracking.
695- /// </summary>
696597public class StackFrameInfo
697598{
698599 public string FileName { get ; set ; }
@@ -705,9 +606,6 @@ public class StackFrameInfo
705606 public Dictionary < string , string > LocalVariables { get ; set ; }
706607}
707608
708- /// <summary>
709- /// Environment information for error tracking.
710- /// </summary>
711609public class EnvironmentInfo
712610{
713611 public string MachineName { get ; set ; }
@@ -724,10 +622,6 @@ public class EnvironmentInfo
724622 public Dictionary < string , string > EnvironmentVariables { get ; set ; }
725623}
726624
727- /// <summary>
728- /// Large event document - represents error/exception events in systems like Exceptionless.
729- /// Designed to be ~10MB when fully populated.
730- /// </summary>
731625public class LargeEventDocument
732626{
733627 public string Id { get ; set ; }
@@ -753,9 +647,6 @@ public class LargeEventDocument
753647 public EnvironmentInfo Environment { get ; set ; }
754648}
755649
756- /// <summary>
757- /// Log entry for batch logging scenarios.
758- /// </summary>
759650public class LogEntry
760651{
761652 public Guid Id { get ; set ; }
@@ -771,10 +662,6 @@ public class LogEntry
771662 public string ParentSpanId { get ; set ; }
772663}
773664
774- /// <summary>
775- /// Large log batch - represents a batch of log entries for bulk processing.
776- /// Designed to be ~10MB when fully populated.
777- /// </summary>
778665public class LargeLogBatch
779666{
780667 public Guid BatchId { get ; set ; }
@@ -784,10 +671,6 @@ public class LargeLogBatch
784671 public Dictionary < string , string > Metadata { get ; set ; }
785672}
786673
787- /// <summary>
788- /// Object with dynamic (object) properties to test cloning of unknown types at compile time.
789- /// This simulates scenarios where JSON deserialization produces Dictionary<string, object> or JToken.
790- /// </summary>
791674public class ObjectWithDynamicProperties
792675{
793676 public int Id { get ; set ; }
0 commit comments