Files
CBDD/tests/CBDD.Tests.Benchmark/CompressionBenchmarks.cs
Joseph Doherty 3ffd468c79
All checks were successful
NuGet Publish / build-and-pack (push) Successful in 45s
NuGet Publish / publish-to-gitea (push) Successful in 52s
Fix audit findings for coverage, architecture checks, and XML docs
2026-02-20 15:43:25 -05:00

199 lines
5.8 KiB
C#

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using System.IO.Compression;
using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Compression;
using ZB.MOM.WW.CBDD.Core.Storage;
namespace ZB.MOM.WW.CBDD.Tests.Benchmark;
[SimpleJob]
[InProcess]
[MemoryDiagnoser]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[HtmlExporter]
[JsonExporterAttribute.Full]
public class CompressionBenchmarks
{
private const int SeedCount = 300;
private const int WorkloadCount = 100;
/// <summary>
/// Gets or sets whether compression is enabled for the benchmark run.
/// </summary>
[Params(false, true)]
public bool EnableCompression { get; set; }
/// <summary>
/// Gets or sets the compression codec for the benchmark run.
/// </summary>
[Params(CompressionCodec.Brotli, CompressionCodec.Deflate)]
public CompressionCodec Codec { get; set; }
/// <summary>
/// Gets or sets the compression level for the benchmark run.
/// </summary>
[Params(CompressionLevel.Fastest, CompressionLevel.Optimal)]
public CompressionLevel Level { get; set; }
private string _dbPath = string.Empty;
private string _walPath = string.Empty;
private StorageEngine _storage = null!;
private BenchmarkTransactionHolder _transactionHolder = null!;
private DocumentCollection<Person> _collection = null!;
private Person[] _insertBatch = Array.Empty<Person>();
private ObjectId[] _seedIds = Array.Empty<ObjectId>();
/// <summary>
/// Prepares benchmark storage and seed data for each iteration.
/// </summary>
[IterationSetup]
public void Setup()
{
var id = Guid.NewGuid().ToString("N");
_dbPath = Path.Combine(AppContext.BaseDirectory, $"bench_compression_{id}.db");
_walPath = Path.ChangeExtension(_dbPath, ".wal");
var compressionOptions = new CompressionOptions
{
EnableCompression = EnableCompression,
MinSizeBytes = 256,
MinSavingsPercent = 0,
Codec = Codec,
Level = Level
};
_storage = new StorageEngine(_dbPath, PageFileConfig.Default, compressionOptions);
_transactionHolder = new BenchmarkTransactionHolder(_storage);
_collection = new DocumentCollection<Person>(_storage, _transactionHolder, new PersonMapper());
_seedIds = new ObjectId[SeedCount];
for (var i = 0; i < SeedCount; i++)
{
var doc = CreatePerson(i, includeLargeBio: true);
_seedIds[i] = _collection.Insert(doc);
}
_transactionHolder.CommitAndReset();
_insertBatch = Enumerable.Range(SeedCount, WorkloadCount)
.Select(i => CreatePerson(i, includeLargeBio: true))
.ToArray();
}
/// <summary>
/// Cleans up benchmark resources for each iteration.
/// </summary>
[IterationCleanup]
public void Cleanup()
{
_transactionHolder?.Dispose();
_storage?.Dispose();
if (File.Exists(_dbPath)) File.Delete(_dbPath);
if (File.Exists(_walPath)) File.Delete(_walPath);
}
/// <summary>
/// Benchmarks insert workload performance.
/// </summary>
[Benchmark(Baseline = true)]
[BenchmarkCategory("Compression_InsertUpdateRead")]
public void Insert_Workload()
{
_collection.InsertBulk(_insertBatch);
_transactionHolder.CommitAndReset();
}
/// <summary>
/// Benchmarks update workload performance.
/// </summary>
[Benchmark]
[BenchmarkCategory("Compression_InsertUpdateRead")]
public void Update_Workload()
{
for (var i = 0; i < WorkloadCount; i++)
{
var id = _seedIds[i];
var current = _collection.FindById(id);
if (current == null)
continue;
current.Bio = BuildBio(i + 10_000);
current.Age += 1;
_collection.Update(current);
}
_transactionHolder.CommitAndReset();
}
/// <summary>
/// Benchmarks read workload performance.
/// </summary>
[Benchmark]
[BenchmarkCategory("Compression_InsertUpdateRead")]
public int Read_Workload()
{
var checksum = 0;
for (var i = 0; i < WorkloadCount; i++)
{
var person = _collection.FindById(_seedIds[i]);
if (person != null)
{
checksum += person.Age;
}
}
_transactionHolder.CommitAndReset();
return checksum;
}
private static Person CreatePerson(int i, bool includeLargeBio)
{
return new Person
{
Id = ObjectId.NewObjectId(),
FirstName = $"First_{i}",
LastName = $"Last_{i}",
Age = 20 + (i % 50),
Bio = includeLargeBio ? BuildBio(i) : $"bio-{i}",
CreatedAt = DateTime.UnixEpoch.AddMinutes(i),
Balance = 100 + i,
HomeAddress = new Address
{
Street = $"{i} Main St",
City = "Bench City",
ZipCode = "12345"
},
EmploymentHistory =
[
new WorkHistory
{
CompanyName = $"Company_{i}",
Title = "Engineer",
DurationYears = i % 10,
Tags = ["csharp", "db", "compression"]
}
]
};
}
private static string BuildBio(int seed)
{
var builder = new System.Text.StringBuilder(4500);
for (var i = 0; i < 150; i++)
{
builder.Append("bio-");
builder.Append(seed.ToString("D6"));
builder.Append('-');
builder.Append(i.ToString("D3"));
builder.Append('|');
}
return builder.ToString();
}
}