Skip to content

Commit c488f75

Browse files
author
Developer
committed
Added benchmarks for full CI test
1 parent 6b5a40f commit c488f75

File tree

4 files changed

+202
-0
lines changed

4 files changed

+202
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using BenchmarkDotNet.Attributes;
2+
using ByTech.EmbeddedCommitLog.Pipeline;
3+
using PeclPipeline = ByTech.EmbeddedCommitLog.Pipeline.Pipeline;
4+
5+
namespace ByTech.EmbeddedCommitLog.Benchmarks;
6+
7+
/// <summary>
8+
/// Measures sustained append throughput for a PECL pipeline using Batched durability:
9+
/// <see cref="RecordsPerBatch"/> records are written then flushed as a group.
10+
/// </summary>
11+
/// <remarks>
12+
/// Run in Release mode only:
13+
/// <code>dotnet run --project benchmarks/ByTech.EmbeddedCommitLog.Benchmarks -c Release</code>
14+
/// Record results in <c>.docs/PERFORMANCE-BASELINE.MD</c>.
15+
/// </remarks>
16+
[MemoryDiagnoser]
17+
public class AppendThroughputBenchmark
18+
{
19+
/// <summary>Number of records written per benchmark iteration (one batch).</summary>
20+
public const int RecordsPerBatch = 100_000;
21+
22+
/// <summary>64-byte payload — representative of a small-to-medium telemetry event.</summary>
23+
private static readonly byte[] _payload = new byte[64];
24+
25+
private string? _rootDirectory;
26+
private PeclPipeline? _pipeline;
27+
28+
/// <summary>Creates a fresh pipeline in a unique temp directory before each iteration.</summary>
29+
[IterationSetup]
30+
public void Setup()
31+
{
32+
_rootDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
33+
Directory.CreateDirectory(_rootDirectory);
34+
35+
_pipeline = new PeclPipeline(new PipelineConfiguration
36+
{
37+
RootDirectory = _rootDirectory,
38+
MaxSegmentSize = 512L * 1024 * 1024, // 512 MiB — avoid rollover during benchmark
39+
});
40+
_pipeline.Start();
41+
}
42+
43+
/// <summary>Disposes the pipeline and deletes the temp directory after each iteration.</summary>
44+
[IterationCleanup]
45+
public void Cleanup()
46+
{
47+
_pipeline?.Dispose();
48+
_pipeline = null;
49+
50+
if (_rootDirectory is not null && Directory.Exists(_rootDirectory))
51+
{
52+
Directory.Delete(_rootDirectory, recursive: true);
53+
}
54+
}
55+
56+
/// <summary>
57+
/// Appends <see cref="RecordsPerBatch"/> records then flushes once (Batched durability).
58+
/// This is the primary throughput scenario: high append rate with periodic group commits.
59+
/// </summary>
60+
[Benchmark]
61+
public void AppendBatch()
62+
{
63+
for (int i = 0; i < RecordsPerBatch; i++)
64+
{
65+
_pipeline!.Append(_payload);
66+
}
67+
_pipeline!.Flush();
68+
}
69+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<RootNamespace>ByTech.EmbeddedCommitLog.Benchmarks</RootNamespace>
4+
<OutputType>Exe</OutputType>
5+
<IsPackable>false</IsPackable>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="BenchmarkDotNet" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\..\src\ByTech.EmbeddedCommitLog\ByTech.EmbeddedCommitLog.csproj" />
14+
</ItemGroup>
15+
</Project>
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using BenchmarkDotNet.Attributes;
2+
using ByTech.EmbeddedCommitLog.Consumer;
3+
using ByTech.EmbeddedCommitLog.Pipeline;
4+
using ByTech.EmbeddedCommitLog.Sinks;
5+
using PeclPipeline = ByTech.EmbeddedCommitLog.Pipeline.Pipeline;
6+
7+
namespace ByTech.EmbeddedCommitLog.Benchmarks;
8+
9+
/// <summary>
10+
/// Measures fan-out throughput: records routed through a broadcast router to one or
11+
/// three <see cref="ISink"/> instances per consumer. Each iteration appends
12+
/// <see cref="RecordsPerBatch"/> records then calls <c>Stop()</c> to drain all lanes.
13+
/// </summary>
14+
/// <remarks>
15+
/// Run in Release mode only:
16+
/// <code>dotnet run --project benchmarks/ByTech.EmbeddedCommitLog.Benchmarks -c Release</code>
17+
/// Record results in <c>.docs/PERFORMANCE-BASELINE.MD</c>.
18+
/// </remarks>
19+
[MemoryDiagnoser]
20+
public class FanOutBenchmark
21+
{
22+
/// <summary>Number of records written per benchmark iteration.</summary>
23+
public const int RecordsPerBatch = 1_000;
24+
25+
/// <summary>64-byte payload — representative of a small-to-medium telemetry event.</summary>
26+
private static readonly byte[] _payload = new byte[64];
27+
28+
private string? _rootDirectory;
29+
private PeclPipeline? _pipeline;
30+
31+
/// <summary>Creates a fresh pipeline in a unique temp directory before each iteration.</summary>
32+
[IterationSetup(Targets = [nameof(FanOut_1Sink)])]
33+
public void Setup1Sink()
34+
{
35+
_rootDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
36+
Directory.CreateDirectory(_rootDirectory);
37+
38+
_pipeline = new PeclPipeline(new PipelineConfiguration
39+
{
40+
RootDirectory = _rootDirectory,
41+
MaxSegmentSize = 512L * 1024 * 1024, // 512 MiB — avoid rollover
42+
});
43+
_pipeline.RegisterConsumer("c");
44+
_pipeline.AddSink("c", "s1", new NoOpSink());
45+
_pipeline.Start();
46+
}
47+
48+
/// <summary>Creates a fresh pipeline in a unique temp directory before each iteration.</summary>
49+
[IterationSetup(Targets = [nameof(FanOut_3Sinks)])]
50+
public void Setup3Sinks()
51+
{
52+
_rootDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
53+
Directory.CreateDirectory(_rootDirectory);
54+
55+
_pipeline = new PeclPipeline(new PipelineConfiguration
56+
{
57+
RootDirectory = _rootDirectory,
58+
MaxSegmentSize = 512L * 1024 * 1024,
59+
});
60+
_pipeline.RegisterConsumer("c");
61+
_pipeline.AddSink("c", "s1", new NoOpSink());
62+
_pipeline.AddSink("c", "s2", new NoOpSink());
63+
_pipeline.AddSink("c", "s3", new NoOpSink());
64+
_pipeline.Start();
65+
}
66+
67+
/// <summary>Disposes the pipeline and deletes the temp directory after each iteration.</summary>
68+
[IterationCleanup]
69+
public void Cleanup()
70+
{
71+
_pipeline?.Dispose();
72+
_pipeline = null;
73+
74+
if (_rootDirectory is not null && Directory.Exists(_rootDirectory))
75+
{
76+
Directory.Delete(_rootDirectory, recursive: true);
77+
}
78+
}
79+
80+
/// <summary>
81+
/// Appends <see cref="RecordsPerBatch"/> records and calls <c>Stop()</c> to drain
82+
/// one sink lane. Measures end-to-end fan-out latency for a single-sink consumer.
83+
/// </summary>
84+
[Benchmark]
85+
public void FanOut_1Sink()
86+
{
87+
for (int i = 0; i < RecordsPerBatch; i++)
88+
{
89+
_pipeline!.Append(_payload);
90+
}
91+
_pipeline!.Stop();
92+
}
93+
94+
/// <summary>
95+
/// Appends <see cref="RecordsPerBatch"/> records and calls <c>Stop()</c> to drain
96+
/// three sink lanes. Measures broadcast fan-out overhead vs. single-sink baseline.
97+
/// </summary>
98+
[Benchmark]
99+
public void FanOut_3Sinks()
100+
{
101+
for (int i = 0; i < RecordsPerBatch; i++)
102+
{
103+
_pipeline!.Append(_payload);
104+
}
105+
_pipeline!.Stop();
106+
}
107+
108+
/// <summary>A no-op <see cref="ISink"/> that discards all records immediately.</summary>
109+
private sealed class NoOpSink : ISink
110+
{
111+
/// <inheritdoc/>
112+
public Task WriteAsync(IReadOnlyList<LogRecord> batch, CancellationToken ct) =>
113+
Task.CompletedTask;
114+
}
115+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using BenchmarkDotNet.Running;
2+
3+
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);

0 commit comments

Comments
 (0)