Files
ParadoxSaveParser/ParadoxSaveParser.Benchmarks/JqBenchmarks.cs
T
2026-09-14 23:09:25 +02:00

80 lines
2.6 KiB
C#

using System;
using System.Diagnostics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
namespace ParadoxSaveParser.Benchmarks;
/// <summary>
/// jq runs as an external process on the JSON twin of the save.
/// Numbers therefore include process start and reading the file from the OS page cache,
/// which is exactly what you pay when you shell out to jq.
/// <see cref="JqNoop" /> measures that fixed overhead so it can be subtracted.
/// </summary>
[SimpleJob(RunStrategy.Monitoring, launchCount: 1, warmupCount: 1, iterationCount: 5)]
public class JqBenchmarks
{
public static string JqPath { get; set; } = "jq";
[GlobalSetup]
public void Setup() => Run("--version", null);
[Benchmark(Description = "jq .countries | map_values(.technology)")]
public int Jq() => Run("-c", BenchData.JqQuery);
[Benchmark(Description = "jq empty (parse only)")]
public int JqParseOnly() => Run("-c", "empty");
[Benchmark(Description = "jq --stream (no DOM)")]
public int JqStream() => Run("-c --stream", "empty");
[Benchmark(Description = "jq process startup only (tiny input)")]
public int JqNoop()
{
var psi = new ProcessStartInfo(JqPath)
{
RedirectStandardOutput = true,
RedirectStandardInput = true,
UseShellExecute = false,
};
psi.ArgumentList.Add("-c");
psi.ArgumentList.Add("empty");
using var p = Process.Start(psi)!;
p.StandardInput.Write("{}");
p.StandardInput.Close();
p.StandardOutput.ReadToEnd();
p.WaitForExit();
return p.ExitCode;
}
private static int Run(string flags, string? program)
{
var psi = new ProcessStartInfo(JqPath)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
};
foreach (var f in flags.Split(' ', StringSplitOptions.RemoveEmptyEntries))
psi.ArgumentList.Add(f);
if (program != null)
{
psi.ArgumentList.Add(program);
psi.ArgumentList.Add(BenchData.JsonPath);
}
using var p = Process.Start(psi)!;
// output must be drained, otherwise jq blocks on a full pipe
long bytes = 0;
var buf = new char[1 << 16];
int read;
while ((read = p.StandardOutput.Read(buf, 0, buf.Length)) > 0)
bytes += read;
p.StandardError.ReadToEnd();
p.WaitForExit();
if (p.ExitCode != 0)
throw new Exception($"jq exited with {p.ExitCode}");
return (int)(bytes & int.MaxValue);
}
}