save progress
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
// <copyright file="ConcurrentHlcBenchmarks.cs" company="StellaOps">
|
||||
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Engines;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
|
||||
namespace StellaOps.HybridLogicalClock.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for concurrent HLC operations.
|
||||
/// Measures thread contention and scalability under parallel access.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[SimpleJob(RunStrategy.Monitoring, iterationCount: 5)]
|
||||
public class ConcurrentHlcBenchmarks
|
||||
{
|
||||
private HybridLogicalClock _clock = null!;
|
||||
private InMemoryHlcStateStore _stateStore = null!;
|
||||
private FakeTimeProvider _timeProvider = null!;
|
||||
|
||||
[Params(1, 2, 4, 8)]
|
||||
public int ThreadCount { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_timeProvider = new FakeTimeProvider(DateTimeOffset.UtcNow);
|
||||
_stateStore = new InMemoryHlcStateStore();
|
||||
_clock = new HybridLogicalClock(
|
||||
_timeProvider,
|
||||
"concurrent-benchmark-node",
|
||||
_stateStore);
|
||||
|
||||
// Initialize the clock
|
||||
_ = _clock.Tick();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark concurrent tick operations.
|
||||
/// Each thread generates 1000 ticks; measures total throughput and contention.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void ConcurrentTicks_1000PerThread()
|
||||
{
|
||||
const int ticksPerThread = 1000;
|
||||
|
||||
Parallel.For(0, ThreadCount, threadIndex =>
|
||||
{
|
||||
for (int i = 0; i < ticksPerThread; i++)
|
||||
{
|
||||
_clock.Tick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark mixed concurrent operations (ticks and receives).
|
||||
/// Simulates real-world distributed scenario.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void ConcurrentMixed_TicksAndReceives()
|
||||
{
|
||||
const int operationsPerThread = 500;
|
||||
|
||||
Parallel.For(0, ThreadCount, threadId =>
|
||||
{
|
||||
for (int i = 0; i < operationsPerThread; i++)
|
||||
{
|
||||
if (i % 3 == 0)
|
||||
{
|
||||
// Every third operation is a receive
|
||||
var remote = new HlcTimestamp
|
||||
{
|
||||
PhysicalTime = _timeProvider.GetUtcNow().ToUnixTimeMilliseconds(),
|
||||
NodeId = $"remote-node-{threadId}",
|
||||
LogicalCounter = i
|
||||
};
|
||||
_clock.Receive(remote);
|
||||
}
|
||||
else
|
||||
{
|
||||
_clock.Tick();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// <copyright file="HlcBenchmarks.cs" company="StellaOps">
|
||||
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Engines;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
|
||||
namespace StellaOps.HybridLogicalClock.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for Hybrid Logical Clock operations.
|
||||
/// HLC-010: Measures tick throughput and memory allocation.
|
||||
///
|
||||
/// To run: dotnet run -c Release
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[SimpleJob(RunStrategy.Throughput, iterationCount: 10)]
|
||||
public class HlcBenchmarks
|
||||
{
|
||||
private HybridLogicalClock _clock = null!;
|
||||
private InMemoryHlcStateStore _stateStore = null!;
|
||||
private FakeTimeProvider _timeProvider = null!;
|
||||
private HlcTimestamp _remoteTimestamp;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_timeProvider = new FakeTimeProvider(DateTimeOffset.UtcNow);
|
||||
_stateStore = new InMemoryHlcStateStore();
|
||||
_clock = new HybridLogicalClock(
|
||||
_timeProvider,
|
||||
"benchmark-node-1",
|
||||
_stateStore);
|
||||
|
||||
// Pre-initialize the clock
|
||||
_ = _clock.Tick();
|
||||
|
||||
// Create a remote timestamp for Receive benchmarks
|
||||
_remoteTimestamp = new HlcTimestamp
|
||||
{
|
||||
PhysicalTime = _timeProvider.GetUtcNow().ToUnixTimeMilliseconds(),
|
||||
NodeId = "remote-node-1",
|
||||
LogicalCounter = 5
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark single Tick operation throughput.
|
||||
/// Measures the raw performance of generating a new HLC timestamp.
|
||||
/// </summary>
|
||||
[Benchmark(Baseline = true)]
|
||||
public HlcTimestamp Tick()
|
||||
{
|
||||
return _clock.Tick();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark Tick with time advancement.
|
||||
/// Simulates real-world usage where physical time advances between ticks.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public HlcTimestamp Tick_WithTimeAdvance()
|
||||
{
|
||||
_timeProvider.Advance(TimeSpan.FromMilliseconds(1));
|
||||
return _clock.Tick();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark Receive operation.
|
||||
/// Measures performance of merging a remote timestamp.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public HlcTimestamp Receive()
|
||||
{
|
||||
return _clock.Receive(_remoteTimestamp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark batch of 100 ticks.
|
||||
/// Simulates high-throughput job scheduling scenarios.
|
||||
/// </summary>
|
||||
[Benchmark(OperationsPerInvoke = 100)]
|
||||
public void Tick_Batch100()
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
_ = _clock.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark batch of 1000 ticks.
|
||||
/// Stress test for very high throughput scenarios.
|
||||
/// </summary>
|
||||
[Benchmark(OperationsPerInvoke = 1000)]
|
||||
public void Tick_Batch1000()
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
_ = _clock.Tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// <copyright file="HlcTimestampBenchmarks.cs" company="StellaOps">
|
||||
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
using System.Text.Json;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Engines;
|
||||
|
||||
namespace StellaOps.HybridLogicalClock.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for HlcTimestamp operations.
|
||||
/// Measures parsing, serialization, and comparison performance.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[SimpleJob(RunStrategy.Throughput, iterationCount: 10)]
|
||||
public class HlcTimestampBenchmarks
|
||||
{
|
||||
private HlcTimestamp _timestamp;
|
||||
private string _sortableString = null!;
|
||||
private string _jsonString = null!;
|
||||
private HlcTimestamp[] _timestamps = null!;
|
||||
private static readonly JsonSerializerOptions JsonOptions = new();
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_timestamp = new HlcTimestamp
|
||||
{
|
||||
PhysicalTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
NodeId = "scheduler-east-1",
|
||||
LogicalCounter = 42
|
||||
};
|
||||
|
||||
_sortableString = _timestamp.ToSortableString();
|
||||
_jsonString = JsonSerializer.Serialize(_timestamp, JsonOptions);
|
||||
|
||||
// Generate array of timestamps for sorting benchmark
|
||||
_timestamps = new HlcTimestamp[1000];
|
||||
var random = new Random(42);
|
||||
for (int i = 0; i < _timestamps.Length; i++)
|
||||
{
|
||||
_timestamps[i] = new HlcTimestamp
|
||||
{
|
||||
PhysicalTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + random.Next(-1000, 1000),
|
||||
NodeId = $"node-{random.Next(1, 10)}",
|
||||
LogicalCounter = random.Next(0, 1000)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark ToSortableString serialization.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public string ToSortableString()
|
||||
{
|
||||
return _timestamp.ToSortableString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark Parse from sortable string.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public HlcTimestamp Parse()
|
||||
{
|
||||
return HlcTimestamp.Parse(_sortableString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark TryParse from sortable string.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public bool TryParse()
|
||||
{
|
||||
return HlcTimestamp.TryParse(_sortableString, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark full round-trip: serialize then parse.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public HlcTimestamp RoundTrip()
|
||||
{
|
||||
var str = _timestamp.ToSortableString();
|
||||
return HlcTimestamp.Parse(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark JSON serialization.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public string JsonSerialize()
|
||||
{
|
||||
return JsonSerializer.Serialize(_timestamp, JsonOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark JSON deserialization.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public HlcTimestamp JsonDeserialize()
|
||||
{
|
||||
return JsonSerializer.Deserialize<HlcTimestamp>(_jsonString, JsonOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark CompareTo operation.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public int CompareTo()
|
||||
{
|
||||
var other = new HlcTimestamp
|
||||
{
|
||||
PhysicalTime = _timestamp.PhysicalTime + 1,
|
||||
NodeId = _timestamp.NodeId,
|
||||
LogicalCounter = 0
|
||||
};
|
||||
return _timestamp.CompareTo(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark sorting 1000 timestamps.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void Sort1000Timestamps()
|
||||
{
|
||||
var copy = (HlcTimestamp[])_timestamps.Clone();
|
||||
Array.Sort(copy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// <copyright file="Program.cs" company="StellaOps">
|
||||
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace StellaOps.HybridLogicalClock.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for HLC benchmarks.
|
||||
/// </summary>
|
||||
public static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Run benchmarks.
|
||||
/// Usage:
|
||||
/// dotnet run -c Release # Run all benchmarks
|
||||
/// dotnet run -c Release --filter "Tick" # Run only Tick benchmarks
|
||||
/// dotnet run -c Release --list flat # List available benchmarks
|
||||
/// </summary>
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var config = DefaultConfig.Instance
|
||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator);
|
||||
|
||||
BenchmarkSwitcher
|
||||
.FromAssembly(typeof(Program).Assembly)
|
||||
.Run(args, config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.HybridLogicalClock\StellaOps.HybridLogicalClock.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user