feat(benchmark): add --progress logging for engine solves
Long whole-job solves ran silently, so there was no way to tell a slow engine from a hung one until the timeout fired. --progress hands each solve a JobProgressLog that prints [job/engine] lines for start, finish (or failure/timeout), every plate commit, and candidate evaluations throttled to one line per 2 s so parallel runs stay readable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,8 @@ namespace OpenNest.Benchmark
|
||||
double? salvageRate = null,
|
||||
double? minimumSalvageDimension = null,
|
||||
string outputDirectory = null,
|
||||
int maxParallelism = 1
|
||||
int maxParallelism = 1,
|
||||
System.IO.TextWriter progressLog = null
|
||||
)
|
||||
{
|
||||
var pairs = jobs.SelectMany(job => engines.Select(engine => (Job: job, Engine: engine)))
|
||||
@@ -68,7 +69,8 @@ namespace OpenNest.Benchmark
|
||||
pairs[i].Engine,
|
||||
salvageRate,
|
||||
minimumSalvageDimension,
|
||||
outputDirectory
|
||||
outputDirectory,
|
||||
progressLog
|
||||
)
|
||||
);
|
||||
|
||||
@@ -223,10 +225,15 @@ namespace OpenNest.Benchmark
|
||||
NestingEngineInfo engineInfo,
|
||||
double? salvageRate,
|
||||
double? minimumSalvageDimension,
|
||||
string outputDirectory
|
||||
string outputDirectory,
|
||||
System.IO.TextWriter progressLog
|
||||
)
|
||||
{
|
||||
var requested = job.TotalRequestedQuantity;
|
||||
var log = progressLog == null
|
||||
? null
|
||||
: new JobProgressLog(progressLog, $"{job.Name}/{engineInfo.Name}");
|
||||
log?.Started();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
try
|
||||
@@ -234,7 +241,8 @@ namespace OpenNest.Benchmark
|
||||
var nestJob = job.BuildNestJob(MaxPlates, salvageRate, minimumSalvageDimension);
|
||||
var engine = engineInfo.Factory();
|
||||
using var cts = new CancellationTokenSource(SolveTimeout);
|
||||
var jobResult = engine.Solve(nestJob, null, cts.Token);
|
||||
var jobResult = engine.Solve(nestJob, log, cts.Token);
|
||||
log?.Finished(jobResult, sw.ElapsedMilliseconds);
|
||||
|
||||
var materialized = NestResultMaterializer.Materialize(nestJob, jobResult);
|
||||
var plateRuns = materialized
|
||||
@@ -357,6 +365,7 @@ namespace OpenNest.Benchmark
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
sw.Stop();
|
||||
log?.Failed("timed out", sw.ElapsedMilliseconds);
|
||||
return new JobResult
|
||||
{
|
||||
EngineName = engineInfo.Name,
|
||||
@@ -371,6 +380,7 @@ namespace OpenNest.Benchmark
|
||||
catch (Exception ex)
|
||||
{
|
||||
sw.Stop();
|
||||
log?.Failed($"{ex.GetType().Name}: {ex.Message}", sw.ElapsedMilliseconds);
|
||||
return new JobResult
|
||||
{
|
||||
EngineName = engineInfo.Name,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using OpenNest.Engine.Jobs;
|
||||
|
||||
namespace OpenNest.Benchmark
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes one solve's NestJobProgress as log lines prefixed with "[job/engine]". Every
|
||||
/// PlateCommitted is written; EvaluatingCandidate is throttled to one line per interval so a
|
||||
/// chatty engine cannot flood the console. One instance per solve; Report is thread-safe.
|
||||
/// </summary>
|
||||
public sealed class JobProgressLog : IProgress<NestJobProgress>
|
||||
{
|
||||
public static readonly TimeSpan DefaultInterval = TimeSpan.FromSeconds(2);
|
||||
|
||||
private readonly TextWriter writer;
|
||||
private readonly string label;
|
||||
private readonly TimeSpan interval;
|
||||
private readonly Func<TimeSpan> clock;
|
||||
private readonly object sync = new();
|
||||
private TimeSpan? lastEvaluating;
|
||||
|
||||
public JobProgressLog(
|
||||
TextWriter writer,
|
||||
string label,
|
||||
TimeSpan? interval = null,
|
||||
Func<TimeSpan> clock = null
|
||||
)
|
||||
{
|
||||
this.writer = writer ?? throw new ArgumentNullException(nameof(writer));
|
||||
this.label = label;
|
||||
this.interval = interval ?? DefaultInterval;
|
||||
if (clock == null)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
clock = () => stopwatch.Elapsed;
|
||||
}
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
public void Started() => Write("started");
|
||||
|
||||
public void Finished(NestJobResult result, long elapsedMs) =>
|
||||
Write(
|
||||
$"finished in {elapsedMs} ms: {result.Status} ({result.StopReason}), "
|
||||
+ $"{result.Plates.Count} plate(s)"
|
||||
);
|
||||
|
||||
public void Failed(string error, long elapsedMs) =>
|
||||
Write($"failed after {elapsedMs} ms: {error}");
|
||||
|
||||
public void Report(NestJobProgress value)
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
if (value.Stage == NestJobStage.PlateCommitted)
|
||||
{
|
||||
Write(
|
||||
$"committed plate {value.CommittedPlates} on stock {value.StockId} "
|
||||
+ $"({value.CommittedParts} parts placed)"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
lock (sync)
|
||||
{
|
||||
var now = clock();
|
||||
if (lastEvaluating.HasValue && now - lastEvaluating.Value < interval)
|
||||
return;
|
||||
lastEvaluating = now;
|
||||
}
|
||||
|
||||
var plate = value.PlateIndex >= 0 ? value.PlateIndex + 1 : value.CommittedPlates + 1;
|
||||
Write(
|
||||
$"evaluating plate {plate} on stock {value.StockId} "
|
||||
+ $"({value.CommittedPlates} plate(s), {value.CommittedParts} parts committed)"
|
||||
);
|
||||
}
|
||||
|
||||
private void Write(string message)
|
||||
{
|
||||
lock (sync)
|
||||
writer.WriteLine($"[{label}] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,8 @@ static class BenchmarkConsole
|
||||
options.SalvageRate,
|
||||
options.MinimumSalvageDimension,
|
||||
options.OutputDirectory,
|
||||
options.Parallel
|
||||
options.Parallel,
|
||||
options.Progress ? Console.Out : null
|
||||
);
|
||||
|
||||
Report.PrintDetailed(results);
|
||||
@@ -196,6 +197,10 @@ static class BenchmarkConsole
|
||||
);
|
||||
break;
|
||||
|
||||
case "--progress":
|
||||
o.Progress = true;
|
||||
break;
|
||||
|
||||
case "--help":
|
||||
PrintUsage();
|
||||
return null;
|
||||
@@ -322,6 +327,9 @@ static class BenchmarkConsole
|
||||
Console.Error.WriteLine(
|
||||
" which gives the cleanest per-engine timings)"
|
||||
);
|
||||
Console.Error.WriteLine(
|
||||
" --progress Log each solve's start, engine progress and finish"
|
||||
);
|
||||
Console.Error.WriteLine(" --help Show this message");
|
||||
}
|
||||
|
||||
@@ -336,5 +344,6 @@ static class BenchmarkConsole
|
||||
public double? SalvageRate;
|
||||
public double? MinimumSalvageDimension;
|
||||
public int Parallel = 3;
|
||||
public bool Progress;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user