211 lines
6.8 KiB
C#
211 lines
6.8 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace StellaOps.Bench.LinkNotMerge;
|
|
|
|
internal sealed record BenchmarkConfig(
|
|
double? ThresholdMs,
|
|
double? MinThroughputPerSecond,
|
|
double? MinInsertThroughputPerSecond,
|
|
double? MaxAllocatedMb,
|
|
int? Iterations,
|
|
IReadOnlyList<LinkNotMergeScenarioConfig> Scenarios)
|
|
{
|
|
public static async Task<BenchmarkConfig> LoadAsync(string path)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(path);
|
|
|
|
var resolved = Path.GetFullPath(path);
|
|
if (!File.Exists(resolved))
|
|
{
|
|
throw new FileNotFoundException($"Benchmark configuration '{resolved}' was not found.", resolved);
|
|
}
|
|
|
|
await using var stream = File.OpenRead(resolved);
|
|
var model = await JsonSerializer.DeserializeAsync<BenchmarkConfigModel>(
|
|
stream,
|
|
new JsonSerializerOptions(JsonSerializerDefaults.Web)
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
AllowTrailingCommas = true,
|
|
}).ConfigureAwait(false);
|
|
|
|
if (model is null)
|
|
{
|
|
throw new InvalidOperationException($"Benchmark configuration '{resolved}' could not be parsed.");
|
|
}
|
|
|
|
if (model.Scenarios.Count == 0)
|
|
{
|
|
throw new InvalidOperationException($"Benchmark configuration '{resolved}' does not contain any scenarios.");
|
|
}
|
|
|
|
foreach (var scenario in model.Scenarios)
|
|
{
|
|
scenario.Validate();
|
|
}
|
|
|
|
return new BenchmarkConfig(
|
|
model.ThresholdMs,
|
|
model.MinThroughputPerSecond,
|
|
model.MinInsertThroughputPerSecond,
|
|
model.MaxAllocatedMb,
|
|
model.Iterations,
|
|
model.Scenarios);
|
|
}
|
|
|
|
private sealed class BenchmarkConfigModel
|
|
{
|
|
[JsonPropertyName("thresholdMs")]
|
|
public double? ThresholdMs { get; init; }
|
|
|
|
[JsonPropertyName("minThroughputPerSecond")]
|
|
public double? MinThroughputPerSecond { get; init; }
|
|
|
|
[JsonPropertyName("minInsertThroughputPerSecond")]
|
|
public double? MinInsertThroughputPerSecond { get; init; }
|
|
|
|
[JsonPropertyName("maxAllocatedMb")]
|
|
public double? MaxAllocatedMb { get; init; }
|
|
|
|
[JsonPropertyName("iterations")]
|
|
public int? Iterations { get; init; }
|
|
|
|
[JsonPropertyName("scenarios")]
|
|
public List<LinkNotMergeScenarioConfig> Scenarios { get; init; } = new();
|
|
}
|
|
}
|
|
|
|
internal sealed class LinkNotMergeScenarioConfig
|
|
{
|
|
private const int DefaultObservationCount = 5_000;
|
|
private const int DefaultAliasGroups = 500;
|
|
private const int DefaultPurlsPerObservation = 4;
|
|
private const int DefaultCpesPerObservation = 2;
|
|
private const int DefaultReferencesPerObservation = 3;
|
|
private const int DefaultTenants = 4;
|
|
private const int DefaultBatchSize = 500;
|
|
private const int DefaultSeed = 42_022;
|
|
|
|
[JsonPropertyName("id")]
|
|
public string? Id { get; init; }
|
|
|
|
[JsonPropertyName("label")]
|
|
public string? Label { get; init; }
|
|
|
|
[JsonPropertyName("observations")]
|
|
public int? Observations { get; init; }
|
|
|
|
[JsonPropertyName("aliasGroups")]
|
|
public int? AliasGroups { get; init; }
|
|
|
|
[JsonPropertyName("purlsPerObservation")]
|
|
public int? PurlsPerObservation { get; init; }
|
|
|
|
[JsonPropertyName("cpesPerObservation")]
|
|
public int? CpesPerObservation { get; init; }
|
|
|
|
[JsonPropertyName("referencesPerObservation")]
|
|
public int? ReferencesPerObservation { get; init; }
|
|
|
|
[JsonPropertyName("tenants")]
|
|
public int? Tenants { get; init; }
|
|
|
|
[JsonPropertyName("batchSize")]
|
|
public int? BatchSize { get; init; }
|
|
|
|
[JsonPropertyName("seed")]
|
|
public int? Seed { get; init; }
|
|
|
|
[JsonPropertyName("iterations")]
|
|
public int? Iterations { get; init; }
|
|
|
|
[JsonPropertyName("thresholdMs")]
|
|
public double? ThresholdMs { get; init; }
|
|
|
|
[JsonPropertyName("minThroughputPerSecond")]
|
|
public double? MinThroughputPerSecond { get; init; }
|
|
|
|
[JsonPropertyName("minInsertThroughputPerSecond")]
|
|
public double? MinInsertThroughputPerSecond { get; init; }
|
|
|
|
[JsonPropertyName("maxAllocatedMb")]
|
|
public double? MaxAllocatedMb { get; init; }
|
|
|
|
public string ScenarioId => string.IsNullOrWhiteSpace(Id) ? "linknotmerge" : Id!.Trim();
|
|
|
|
public string DisplayLabel => string.IsNullOrWhiteSpace(Label) ? ScenarioId : Label!.Trim();
|
|
|
|
public int ResolveObservationCount() => Observations.HasValue && Observations.Value > 0
|
|
? Observations.Value
|
|
: DefaultObservationCount;
|
|
|
|
public int ResolveAliasGroups() => AliasGroups.HasValue && AliasGroups.Value > 0
|
|
? AliasGroups.Value
|
|
: DefaultAliasGroups;
|
|
|
|
public int ResolvePurlsPerObservation() => PurlsPerObservation.HasValue && PurlsPerObservation.Value > 0
|
|
? PurlsPerObservation.Value
|
|
: DefaultPurlsPerObservation;
|
|
|
|
public int ResolveCpesPerObservation() => CpesPerObservation.HasValue && CpesPerObservation.Value >= 0
|
|
? CpesPerObservation.Value
|
|
: DefaultCpesPerObservation;
|
|
|
|
public int ResolveReferencesPerObservation() => ReferencesPerObservation.HasValue && ReferencesPerObservation.Value >= 0
|
|
? ReferencesPerObservation.Value
|
|
: DefaultReferencesPerObservation;
|
|
|
|
public int ResolveTenantCount() => Tenants.HasValue && Tenants.Value > 0
|
|
? Tenants.Value
|
|
: DefaultTenants;
|
|
|
|
public int ResolveBatchSize() => BatchSize.HasValue && BatchSize.Value > 0
|
|
? BatchSize.Value
|
|
: DefaultBatchSize;
|
|
|
|
public int ResolveSeed() => Seed.HasValue && Seed.Value > 0
|
|
? Seed.Value
|
|
: DefaultSeed;
|
|
|
|
public int ResolveIterations(int? defaultIterations)
|
|
{
|
|
var iterations = Iterations ?? defaultIterations ?? 3;
|
|
if (iterations <= 0)
|
|
{
|
|
throw new InvalidOperationException($"Scenario '{ScenarioId}' requires iterations > 0.");
|
|
}
|
|
|
|
return iterations;
|
|
}
|
|
|
|
public void Validate()
|
|
{
|
|
if (ResolveObservationCount() <= 0)
|
|
{
|
|
throw new InvalidOperationException($"Scenario '{ScenarioId}' requires observations > 0.");
|
|
}
|
|
|
|
if (ResolveAliasGroups() <= 0)
|
|
{
|
|
throw new InvalidOperationException($"Scenario '{ScenarioId}' requires aliasGroups > 0.");
|
|
}
|
|
|
|
if (ResolvePurlsPerObservation() <= 0)
|
|
{
|
|
throw new InvalidOperationException($"Scenario '{ScenarioId}' requires purlsPerObservation > 0.");
|
|
}
|
|
|
|
if (ResolveTenantCount() <= 0)
|
|
{
|
|
throw new InvalidOperationException($"Scenario '{ScenarioId}' requires tenants > 0.");
|
|
}
|
|
|
|
if (ResolveBatchSize() > ResolveObservationCount())
|
|
{
|
|
throw new InvalidOperationException($"Scenario '{ScenarioId}' batchSize cannot exceed observations.");
|
|
}
|
|
}
|
|
}
|