@@ -10,14 +10,10 @@ namespace SharpCoreDB.Benchmarks;
1010
1111/// <summary>
1212/// Phase 2A Optimization Benchmarks
13- /// Measures actual performance improvements from:
14- /// - Monday-Tuesday: WHERE Clause Caching (50-100x for repeated)
15- /// - Wednesday: SELECT* StructRow Path (2-3x + 25x memory)
16- /// - Thursday: Type Conversion Caching (5-10x)
17- /// - Friday: Batch PK Validation (1.1-1.3x)
1813///
19- /// CRITICAL: Data is populated ONCE in GlobalSetup, not per-iteration!
20- /// This measures QUERY performance, not insert performance.
14+ /// IMPORTANT: These benchmarks measure specific optimizations:
15+ /// - WHERE caching: Compilation is cached, but results still materialized
16+ /// - SELECT* path: Zero-copy StructRow vs Dictionary materialization
2117/// </summary>
2218[ MemoryDiagnoser ]
2319[ SimpleJob ( warmupCount : 3 , iterationCount : 5 ) ]
@@ -38,8 +34,7 @@ public void Setup()
3834 // Create test table
3935 db . CreateUsersTable ( ) ;
4036
41- // CRITICAL: Populate data ONCE in GlobalSetup, not per-iteration
42- // This ensures we measure QUERY performance, not insert performance
37+ // Populate data ONCE in GlobalSetup
4338 PopulateTestDataOnce ( ) ;
4439 }
4540
@@ -52,25 +47,52 @@ public void Cleanup()
5247 #region Monday-Tuesday: WHERE Clause Caching Benchmarks
5348
5449 /// <summary>
55- /// Repeated WHERE clause query - demonstrates cache benefits.
56- /// First execution compiles and caches the predicate.
57- /// Subsequent executions reuse cached predicate (99%+ hit rate).
58- ///
59- /// Expected improvement: 50-100x for repeated queries
50+ /// Baseline: Single WHERE query (no cache benefit yet).
51+ /// This is the first execution where compilation happens.
6052 /// </summary>
61- [ Benchmark ( Description = "WHERE caching: Execute same WHERE 100x (cache benefits)" ) ]
62- public int WhereClauseCaching_RepeatedQuery ( )
53+ [ Benchmark ( Description = "WHERE single query (baseline, no cache)" ) ]
54+ public int WhereCaching_SingleQuery ( )
55+ {
56+ // Execute WHERE once - this compiles and caches the predicate
57+ var result = db . Database . ExecuteQuery ( "SELECT * FROM users WHERE age > 25" ) ;
58+ return result . Count ;
59+ }
60+
61+ /// <summary>
62+ /// WHERE repeated 10x - tests cache effectiveness.
63+ /// After first execution, predicate should be cached.
64+ /// Expected: Similar performance to first query (filtering still needed).
65+ /// Cache benefit: Reduced compilation overhead.
66+ /// </summary>
67+ [ Benchmark ( Description = "WHERE repeated 10x (cache benefits)" ) ]
68+ public int WhereCaching_Repeated10 ( )
6369 {
64- // Execute same WHERE query 100 times
65- // First run: compiles and caches predicate
66- // Runs 2-100: reuse cached predicate (99%+ cache hit rate)
6770 int totalCount = 0 ;
68-
69- for ( int i = 0 ; i < 100 ; i ++ )
71+ for ( int i = 0 ; i < 10 ; i ++ )
7072 {
7173 var result = db . Database . ExecuteQuery ( "SELECT * FROM users WHERE age > 25" ) ;
7274 totalCount += result . Count ;
7375 }
76+ return totalCount ;
77+ }
78+
79+ /// <summary>
80+ /// Different WHERE clause - tests cache separation.
81+ /// Different predicate = new cache entry.
82+ /// Verifies cache works correctly for different queries.
83+ /// </summary>
84+ [ Benchmark ( Description = "WHERE different clause (tests cache isolation)" ) ]
85+ public int WhereCaching_DifferentClause ( )
86+ {
87+ int totalCount = 0 ;
88+
89+ // Query 1: age > 25 (may use cached predicate)
90+ var result1 = db . Database . ExecuteQuery ( "SELECT * FROM users WHERE age > 25" ) ;
91+ totalCount += result1 . Count ;
92+
93+ // Query 2: age < 40 (different predicate = new cache entry)
94+ var result2 = db . Database . ExecuteQuery ( "SELECT * FROM users WHERE age < 40" ) ;
95+ totalCount += result2 . Count ;
7496
7597 return totalCount ;
7698 }
@@ -82,8 +104,7 @@ public int WhereClauseCaching_RepeatedQuery()
82104 /// <summary>
83105 /// SELECT * using traditional Dictionary path (baseline).
84106 /// Each row materializes to Dictionary<string, object>.
85- ///
86- /// Expected memory: ~200 bytes per row = 2MB for 10k rows
107+ /// Expected: ~200 bytes per row overhead
87108 /// </summary>
88109 [ Benchmark ( Description = "SELECT * Dictionary path (baseline)" ) ]
89110 public int SelectDictionary_Path ( )
@@ -94,10 +115,8 @@ public int SelectDictionary_Path()
94115
95116 /// <summary>
96117 /// SELECT * using fast path with StructRow (optimized).
97- /// Uses zero-copy StructRow instead of Dictionary.
98- ///
99- /// Expected improvement: 2-3x faster, 25x less memory
100- /// Expected memory: ~20 bytes per row = 200KB for 10k rows
118+ /// Uses zero-copy StructRow instead of Dictionary materialization.
119+ /// Expected: 2-3x faster, 25x less memory
101120 /// </summary>
102121 [ Benchmark ( Description = "SELECT * StructRow fast path (optimized)" ) ]
103122 public int SelectStructRow_FastPath ( )
@@ -110,15 +129,10 @@ public int SelectStructRow_FastPath()
110129
111130 #region Helper Methods
112131
113- /// <summary>
114- /// Populate test data ONCE in GlobalSetup.
115- /// This ensures benchmarks measure QUERY performance, not insert performance.
116- /// </summary>
117132 private void PopulateTestDataOnce ( )
118133 {
119134 var random = new Random ( 42 ) ;
120135
121- // Bulk insert to minimize insertion overhead
122136 for ( int i = 0 ; i < DATASET_SIZE ; i ++ )
123137 {
124138 var id = i ;
@@ -137,7 +151,6 @@ INSERT INTO users (id, name, email, age, created_at, is_active)
137151 }
138152 catch ( InvalidOperationException ex ) when ( ex . Message . Contains ( "Primary key" ) )
139153 {
140- // Skip if row already exists
141154 continue ;
142155 }
143156 }
0 commit comments