-
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathSyncBenchmark.cs
More file actions
93 lines (81 loc) · 3.52 KB
/
SyncBenchmark.cs
File metadata and controls
93 lines (81 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using BenchmarkDotNet.Attributes;
#if !DEBUG
using BenchmarkDotNet.Running;
using FluentAssertions.Execution;
#endif
using FwLiteProjectSync.Tests.Fixtures;
using LexCore.Sync;
using Microsoft.Extensions.DependencyInjection;
using MiniLcm;
using Xunit.Abstractions;
namespace FwLiteProjectSync.Tests;
/// <summary>
/// Times a first sync of Sena-3 from an empty CRDT (sync, not import — we save an empty
/// snapshot first so the path runs as a real sync).
/// </summary>
[Trait("Category", "Integration")]
[Trait("Category", "Benchmark")]
[Collection(Sena3Collection.Name)]
public class SyncBenchmark(Sena3Fixture fixture, ITestOutputHelper output)
{
[Fact]
public async Task First_Sync_Sena3()
{
FirstSyncBench.Fixture = fixture;
#if DEBUG
// Debug timings are unreliable (no JIT optimizations + BDN harness flags Debug builds).
// Run once for code-path coverage; thresholds enforced in Release only (CI benchmark job).
output.WriteLine("Debug build: running once for coverage; threshold enforced in Release only.");
var bench = new FirstSyncBench();
bench.IterationSetup();
try { _ = await bench.SyncFromEmpty(); }
finally { bench.IterationCleanup(); }
#else
using var scope = new AssertionScope();
var summary = BenchmarkRunner.Run<FirstSyncBench>(BenchmarkSupport.ConfigFor(output));
BenchmarkSupport.AssertRunWasSuccessful(summary);
var report = summary.Reports.Single();
var meanSeconds = report.ResultStatistics!.Mean / 1_000_000_000.0;
output.WriteLine($"first-sync mean = {meanSeconds:F2}s (bound={FirstSyncBench.ThresholdSeconds:F2}s)");
meanSeconds.Should().BeLessThan(FirstSyncBench.ThresholdSeconds,
$"first-sync should not regress past its threshold — see {nameof(FirstSyncBench)}.{nameof(FirstSyncBench.ThresholdSeconds)}");
#endif
}
}
// BenchmarkDotNet doesn't support async [IterationSetup]/[IterationCleanup] signatures,
// so GetAwaiter().GetResult() below is intentional.
#pragma warning disable VSTHRD002
public class FirstSyncBench
{
// CI baseline (no index): mean 52.34s, StdDev 0.27s (low variance) => 57s (~10%)
// CI with commits order index: mean 44.35s, StdDev 1.10s (med variance) => 51s (~15%)
public const double ThresholdSeconds = 51.0;
internal static Sena3Fixture Fixture = null!;
private TestProject _project = null!;
private ProjectSnapshot _projectSnapshot = null!;
private CrdtFwdataProjectSyncService _syncService = null!;
[IterationSetup]
public void IterationSetup()
{
_project = Fixture.SetupProjects().GetAwaiter().GetResult();
_syncService = _project.Services.GetRequiredService<CrdtFwdataProjectSyncService>();
// Save an empty snapshot so the first sync runs as Sync, not Import.
ProjectSnapshotService.SaveProjectSnapshot(_project.FwDataProject, ProjectSnapshot.Empty)
.GetAwaiter().GetResult();
_projectSnapshot = _project.Services.GetRequiredService<ProjectSnapshotService>()
.GetProjectSnapshot(_project.FwDataProject).GetAwaiter().GetResult()
?? throw new InvalidOperationException("Expected snapshot to exist after saving");
}
[Benchmark]
public async Task<SyncResult> SyncFromEmpty()
{
return await _syncService.Sync(_project.CrdtApi, _project.FwDataApi, _projectSnapshot);
}
[IterationCleanup]
public void IterationCleanup()
{
_project?.Dispose();
_project = null!;
}
}
#pragma warning restore VSTHRD002