From 9d92cf4dda0f7023c4500b643700fc1636fa76dd Mon Sep 17 00:00:00 2001 From: MPCoreDeveloper Date: Wed, 2 Sep 2026 21:11:42 +0200 Subject: [PATCH 1/2] bench: add fixed-width variant to the fair PK comparison --pk now runs SharpCoreDB legacy variable-length AND fixed-width (FixedWidthRecordLayout) on the id-PK schema vs SQLite. Measured (AppendOnly, 1 run): fixed-width INSERT +46%, UPDATE +23%, DELETE +16% vs legacy; UPDATE gap vs SQLite narrows 6.3x -> 5.1x. Evidence that fixed-width-by-default alone does not close the residual per-row gap. --- .../Program.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs b/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs index 1a6ac5f4..d5fff3b6 100644 --- a/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs +++ b/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs @@ -347,12 +347,13 @@ static void RunInsertMicroBenchmark() Console.WriteLine($" SQL/Direct overhead: {(sqlMedian / directMedian):F2}x"); } - static DatabaseConfig BuildConfig(SharpCoreDB.Interfaces.StorageEngineType engineType) + static DatabaseConfig BuildConfig(SharpCoreDB.Interfaces.StorageEngineType engineType, bool fixedWidth = false) { return new DatabaseConfig { NoEncryptMode = true, StorageEngineType = engineType, + FixedWidthRecordLayout = fixedWidth, UseGroupCommitWal = false, EnableAdaptiveWalBatching = false, HighSpeedInsertMode = true, @@ -840,7 +841,7 @@ data TEXT /// ExecuteBatchSQL (single transaction). This exercises the PK B-tree fast paths and the /// recommended usage; the no-PK harness scenario above under-measures the engine on DML. /// - static BenchmarkResult RunSharpCoreDBPk(SharpCoreDB.Interfaces.StorageEngineType engineType) + static BenchmarkResult RunSharpCoreDBPk(SharpCoreDB.Interfaces.StorageEngineType engineType, bool fixedWidth = false) { var dbPath = Path.Combine(Path.GetTempPath(), $"bench-sharpcoredb-pk-{Guid.NewGuid()}"); var result = new BenchmarkResult(); @@ -852,7 +853,7 @@ static BenchmarkResult RunSharpCoreDBPk(SharpCoreDB.Interfaces.StorageEngineType var sp = services.BuildServiceProvider(); var factory = sp.GetRequiredService(); - var config = BuildConfig(engineType); + var config = BuildConfig(engineType, fixedWidth); using var db = (SharpCoreDB.Database)factory.Create( dbPath: dbPath, @@ -962,22 +963,29 @@ static void RunPkComparison(SharpCoreDB.Interfaces.StorageEngineType engineType) Console.WriteLine(); Console.WriteLine($"Engine: {engineLabel}"); - Console.WriteLine("━━━ SharpCoreDB (SQL, PK) ━━━"); + Console.WriteLine("━━━ SharpCoreDB (SQL, PK, legacy variable-length) ━━━"); var scdb = RunSharpCoreDBPk(engineType); Console.WriteLine(); + Console.WriteLine("━━━ SharpCoreDB (SQL, PK, fixed-width) ━━━"); + var scdbFw = RunSharpCoreDBPk(engineType, fixedWidth: true); + Console.WriteLine(); + Console.WriteLine("━━━ SQLite (reference) ━━━"); var sqlite = RunSQLite(); Console.WriteLine(); Console.WriteLine("║ Database │ INSERT │ READ │ UPDATE │ DELETE ║"); Console.WriteLine($"║ SharpCoreDB │ {scdb.InsertOpsPerSec,10:N0} │ {scdb.ReadOpsPerSec,8:N0} │ {scdb.UpdateOpsPerSec,8:N0} │ {scdb.DeleteOpsPerSec,8:N0} ║"); + Console.WriteLine($"║ SharpCoreDB FW│ {scdbFw.InsertOpsPerSec,10:N0} │ {scdbFw.ReadOpsPerSec,8:N0} │ {scdbFw.UpdateOpsPerSec,8:N0} │ {scdbFw.DeleteOpsPerSec,8:N0} ║"); Console.WriteLine($"║ SQLite │ {sqlite.InsertOpsPerSec,10:N0} │ {sqlite.ReadOpsPerSec,8:N0} │ {sqlite.UpdateOpsPerSec,8:N0} │ {sqlite.DeleteOpsPerSec,8:N0} ║"); - Console.WriteLine($"\n UPDATE gap: {sqlite.UpdateOpsPerSec / (double)scdb.UpdateOpsPerSec:F1}x DELETE gap: {sqlite.DeleteOpsPerSec / (double)scdb.DeleteOpsPerSec:F1}x"); + Console.WriteLine($"\n UPDATE gap: SQLite vs legacy {sqlite.UpdateOpsPerSec / (double)scdb.UpdateOpsPerSec:F1}x vs fixed-width {sqlite.UpdateOpsPerSec / (double)scdbFw.UpdateOpsPerSec:F1}x"); + Console.WriteLine($" DELETE gap: SQLite vs legacy {sqlite.DeleteOpsPerSec / (double)scdb.DeleteOpsPerSec:F1}x vs fixed-width {sqlite.DeleteOpsPerSec / (double)scdbFw.DeleteOpsPerSec:F1}x"); var results = new Dictionary { - ["SharpCoreDB (SQL, PK)"] = scdb, + ["SharpCoreDB (SQL, PK, legacy)"] = scdb, + ["SharpCoreDB (SQL, PK, fixed-width)"] = scdbFw, ["SQLite"] = sqlite, }; From 16e04ddc3cc33b916aac4e1783fdb7fbd2b34044 Mon Sep 17 00:00:00 2001 From: MPCoreDeveloper Date: Wed, 2 Sep 2026 21:51:56 +0200 Subject: [PATCH 2/2] bench: keep legacy PK arm truly variable-length (AutoFixedWidthRecords opt-out) DatabaseConfig.AutoFixedWidthRecords (default true, landed in perf/fixedwidth-default) makes new PK tables fixed-width; the --pk legacy arm now opts out so the comparison keeps measuring the legacy layout against the fixed-width arm. --- .../benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs b/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs index d5fff3b6..c3614872 100644 --- a/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs +++ b/tests/benchmarks/SharpCoreDB.Benchmarks.Comparative/Program.cs @@ -353,6 +353,10 @@ static DatabaseConfig BuildConfig(SharpCoreDB.Interfaces.StorageEngineType engin { NoEncryptMode = true, StorageEngineType = engineType, + // The fair PK comparison intentionally isolates the record-layout variable: the legacy + // arm opts out of the AutoFixedWidthRecords default so it measures true variable-length + // records; the fixed-width arm forces FixedWidthRecordLayout. + AutoFixedWidthRecords = !fixedWidth, FixedWidthRecordLayout = fixedWidth, UseGroupCommitWal = false, EnableAdaptiveWalBatching = false,