Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ bin
artifacts/
results/
logs/
BenchmarkDotNet.Artifacts/
lmdb/lmdb/
testrun/
buildlog
Expand Down
82 changes: 82 additions & 0 deletions src/LightningDB.Benchmarks/ComparerBenchmarkBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.IO;
using BenchmarkDotNet.Attributes;

namespace LightningDB.Benchmarks;

/// <summary>
/// Base class for comparer benchmarks with configurable database setup.
/// Derived classes must add their own [ParamsSource] attribute for the Comparer property.
/// </summary>
public abstract class ComparerBenchmarkBase
{
private string _path;

protected LightningEnvironment Env { get; private set; }
protected LightningDatabase DB { get; private set; }

// Note: Derived classes must add [ParamsSource] attribute and override this property
public virtual ComparerDescriptor Comparer { get; set; }

[Params(100, 1000)]
public int OpsPerTransaction { get; set; }

[Params(64, 256)]
public int ValueSize { get; set; }

protected byte[] ValueBuffer { get; private set; }
protected KeyBatch KeyBuffers { get; private set; }

[GlobalSetup]
public void GlobalSetup()
{
Console.WriteLine($"Global Setup Begin - Comparer: {Comparer.Name}");

_path = $"BenchmarkDir_{Guid.NewGuid():N}";
if (Directory.Exists(_path))
Directory.Delete(_path, true);

Env = new LightningEnvironment(_path) { MaxDatabases = 1 };
Env.Open();

var config = new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create };
if (Comparer.Comparer != null)
config.CompareWith(Comparer.Comparer);

using (var tx = Env.BeginTransaction()) {
DB = tx.OpenDatabase(configuration: config);
tx.Commit();
}

ValueBuffer = new byte[ValueSize];
KeyBuffers = GenerateKeys();

RunSetup();

Console.WriteLine("Global Setup End");
}

protected virtual KeyBatch GenerateKeys()
=> KeyBatch.Generate(OpsPerTransaction, KeyOrdering.Sequential);

protected virtual void RunSetup() { }

[GlobalCleanup]
public void GlobalCleanup()
{
Console.WriteLine("Global Cleanup Begin");

try {
DB?.Dispose();
Env?.Dispose();

if (Directory.Exists(_path))
Directory.Delete(_path, true);
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}

Console.WriteLine("Global Cleanup End");
}
}
66 changes: 66 additions & 0 deletions src/LightningDB.Benchmarks/ComparerDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Collections.Generic;
using LightningDB.Comparers;

namespace LightningDB.Benchmarks;

/// <summary>
/// Wraps a comparer for BenchmarkDotNet parameterization with friendly display names.
/// </summary>
public readonly struct ComparerDescriptor
{
public string Name { get; }
public IComparer<MDBValue> Comparer { get; }

private ComparerDescriptor(string name, IComparer<MDBValue> comparer)
{
Name = name;
Comparer = comparer;
}

public override string ToString() => Name;

/// <summary>
/// All available comparers including Default (null = LMDB native).
/// </summary>
public static IEnumerable<ComparerDescriptor> All => new[]
{
new ComparerDescriptor("Default", null),
new ComparerDescriptor("Bitwise", BitwiseComparer.Instance),
new ComparerDescriptor("ReverseBitwise", ReverseBitwiseComparer.Instance),
new ComparerDescriptor("SignedInt", SignedIntegerComparer.Instance),
new ComparerDescriptor("ReverseSignedInt", ReverseSignedIntegerComparer.Instance),
new ComparerDescriptor("UnsignedInt", UnsignedIntegerComparer.Instance),
new ComparerDescriptor("ReverseUnsignedInt", ReverseUnsignedIntegerComparer.Instance),
new ComparerDescriptor("Utf8String", Utf8StringComparer.Instance),
new ComparerDescriptor("ReverseUtf8String", ReverseUtf8StringComparer.Instance),
new ComparerDescriptor("Length", LengthComparer.Instance),
new ComparerDescriptor("ReverseLength", ReverseLengthComparer.Instance),
new ComparerDescriptor("LengthOnly", LengthOnlyComparer.Instance),
new ComparerDescriptor("HashCode", HashCodeComparer.Instance),
new ComparerDescriptor("Guid", GuidComparer.Instance),
new ComparerDescriptor("ReverseGuid", ReverseGuidComparer.Instance),
};

/// <summary>
/// Integer comparers only (for focused integer key benchmarks).
/// </summary>
public static IEnumerable<ComparerDescriptor> IntegerComparers => new[]
{
new ComparerDescriptor("Default", null),
new ComparerDescriptor("SignedInt", SignedIntegerComparer.Instance),
new ComparerDescriptor("ReverseSignedInt", ReverseSignedIntegerComparer.Instance),
new ComparerDescriptor("UnsignedInt", UnsignedIntegerComparer.Instance),
new ComparerDescriptor("ReverseUnsignedInt", ReverseUnsignedIntegerComparer.Instance),
};

/// <summary>
/// GUID comparers only (for focused 16-byte key benchmarks).
/// </summary>
public static IEnumerable<ComparerDescriptor> GuidComparers => new[]
{
new ComparerDescriptor("Default", null),
new ComparerDescriptor("Bitwise", BitwiseComparer.Instance),
new ComparerDescriptor("Guid", GuidComparer.Instance),
new ComparerDescriptor("ReverseGuid", ReverseGuidComparer.Instance),
};
}
36 changes: 36 additions & 0 deletions src/LightningDB.Benchmarks/ComparerReadBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;

namespace LightningDB.Benchmarks;

/// <summary>
/// Benchmarks read operations across all comparers.
/// Reads use comparers for B-tree traversal during key lookups.
/// </summary>
[MemoryDiagnoser]
public class ComparerReadBenchmarks : ComparerBenchmarkBase
{
[ParamsSource(nameof(AllComparers))]
public override ComparerDescriptor Comparer { get; set; }

public static IEnumerable<ComparerDescriptor> AllComparers => ComparerDescriptor.All;

protected override void RunSetup()
{
// Pre-populate database for reads
using var tx = Env.BeginTransaction();
for (var i = 0; i < KeyBuffers.Count; i++)
tx.Put(DB, KeyBuffers[i], ValueBuffer);
tx.Commit();
}

[Benchmark]
public void Read()
{
using var tx = Env.BeginTransaction(TransactionBeginFlags.ReadOnly);

for (var i = 0; i < OpsPerTransaction; i++) {
_ = tx.Get(DB, KeyBuffers[i]);
}
}
}
29 changes: 29 additions & 0 deletions src/LightningDB.Benchmarks/ComparerWriteBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;

namespace LightningDB.Benchmarks;

/// <summary>
/// Benchmarks write operations across all comparers.
/// Write operations trigger B-tree insertions which invoke comparers frequently.
/// </summary>
[MemoryDiagnoser]
public class ComparerWriteBenchmarks : ComparerBenchmarkBase
{
[ParamsSource(nameof(AllComparers))]
public override ComparerDescriptor Comparer { get; set; }

public static IEnumerable<ComparerDescriptor> AllComparers => ComparerDescriptor.All;

[Benchmark]
public void Write()
{
using var tx = Env.BeginTransaction();

for (var i = 0; i < OpsPerTransaction; i++) {
tx.Put(DB, KeyBuffers[i], ValueBuffer);
}

tx.Commit();
}
}
98 changes: 98 additions & 0 deletions src/LightningDB.Benchmarks/GuidComparerBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using BenchmarkDotNet.Attributes;

namespace LightningDB.Benchmarks;

/// <summary>
/// Focused benchmarks for GUID comparers testing their optimized 16-byte path.
/// GuidComparer uses two ulong comparisons with big-endian reads for byte ordering.
/// </summary>
[MemoryDiagnoser]
public class GuidComparerBenchmarks
{
private string _path;
private LightningEnvironment _env;
private LightningDatabase _db;
private byte[][] _keys;
private byte[] _valueBuffer;

[ParamsSource(nameof(GuidComparers))]
public ComparerDescriptor Comparer { get; set; }

public static IEnumerable<ComparerDescriptor> GuidComparers
=> ComparerDescriptor.GuidComparers;

[Params(1000, 10000)]
public int OpsPerTransaction { get; set; }

[GlobalSetup]
public void GlobalSetup()
{
Console.WriteLine($"Global Setup Begin - Comparer: {Comparer.Name}");

_path = $"GuidBenchDir_{Guid.NewGuid():N}";
if (Directory.Exists(_path))
Directory.Delete(_path, true);

_env = new LightningEnvironment(_path) { MaxDatabases = 1 };
_env.Open();

var config = new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create };
if (Comparer.Comparer != null)
config.CompareWith(Comparer.Comparer);

using (var tx = _env.BeginTransaction()) {
_db = tx.OpenDatabase(configuration: config);
tx.Commit();
}

_valueBuffer = new byte[64];
_keys = GenerateGuidKeys(OpsPerTransaction);

Console.WriteLine("Global Setup End");
}

private static byte[][] GenerateGuidKeys(int count)
{
var keys = new byte[count][];

for (var i = 0; i < count; i++) {
keys[i] = Guid.NewGuid().ToByteArray();
}

return keys;
}

[Benchmark]
public void WriteGuids()
{
using var tx = _env.BeginTransaction();

for (var i = 0; i < OpsPerTransaction; i++) {
tx.Put(_db, _keys[i], _valueBuffer);
}

tx.Commit();
}

[GlobalCleanup]
public void GlobalCleanup()
{
Console.WriteLine("Global Cleanup Begin");

try {
_db?.Dispose();
_env?.Dispose();

if (Directory.Exists(_path))
Directory.Delete(_path, true);
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}

Console.WriteLine("Global Cleanup End");
}
}
Loading