From bbc664ddccaf4f819891b6af53614b1179ce5fdd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:14:54 +0000 Subject: [PATCH] Add generate-baseline build target and JetBrains profiler support Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .gitignore | 2 + build/Program.cs | 57 ++++++++++++++ tests/SharpCompress.Performance/Program.cs | 90 ++++++++++++++++++++++ tests/SharpCompress.Performance/README.md | 42 +++++++++- 4 files changed, 190 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 85212284..2b0cb726 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ tools .idea/ artifacts/ BenchmarkDotNet.Artifacts/ +baseline-artifacts/ +profiler-snapshots/ .DS_Store *.snupkg diff --git a/build/Program.cs b/build/Program.cs index c6c70cec..152981d6 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -21,6 +21,7 @@ const string UpdateVersion = "update-version"; const string PushToNuGet = "push-to-nuget"; const string DisplayBenchmarkResults = "display-benchmark-results"; const string CompareBenchmarkResults = "compare-benchmark-results"; +const string GenerateBaseline = "generate-baseline"; Target( Clean, @@ -308,6 +309,62 @@ Target( } ); +Target( + GenerateBaseline, + () => + { + var perfProject = "tests/SharpCompress.Performance/SharpCompress.Performance.csproj"; + var baselinePath = "tests/SharpCompress.Performance/baseline-results.md"; + var artifactsDir = "baseline-artifacts"; + + Console.WriteLine("Building performance project..."); + Run("dotnet", $"build {perfProject} --configuration Release"); + + Console.WriteLine("Running benchmarks to generate baseline..."); + Run( + "dotnet", + $"run --project {perfProject} --configuration Release --no-build -- --filter \"*\" --exporters markdown --artifacts {artifactsDir}" + ); + + var resultsDir = Path.Combine(artifactsDir, "results"); + if (!Directory.Exists(resultsDir)) + { + Console.WriteLine("ERROR: No benchmark results generated."); + return; + } + + var markdownFiles = Directory + .GetFiles(resultsDir, "*-report-github.md") + .OrderBy(f => f) + .ToList(); + + if (markdownFiles.Count == 0) + { + Console.WriteLine("ERROR: No markdown reports found."); + return; + } + + Console.WriteLine($"Combining {markdownFiles.Count} benchmark reports..."); + var baselineContent = new List(); + + foreach (var file in markdownFiles) + { + var content = File.ReadAllText(file); + baselineContent.Add(content); + } + + File.WriteAllText(baselinePath, string.Join(Environment.NewLine, baselineContent)); + Console.WriteLine($"Baseline written to {baselinePath}"); + + // Clean up artifacts directory + if (Directory.Exists(artifactsDir)) + { + Directory.Delete(artifactsDir, true); + Console.WriteLine("Cleaned up artifacts directory."); + } + } +); + Target("default", [Publish], () => Console.WriteLine("Done!")); await RunTargetsAndExitAsync(args); diff --git a/tests/SharpCompress.Performance/Program.cs b/tests/SharpCompress.Performance/Program.cs index 8f0e9e79..75d4b16b 100644 --- a/tests/SharpCompress.Performance/Program.cs +++ b/tests/SharpCompress.Performance/Program.cs @@ -1,3 +1,4 @@ +using System; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; @@ -9,6 +10,14 @@ public class Program { public static void Main(string[] args) { + // Check if profiling mode is requested + if (args.Length > 0 && args[0].Equals("--profile", StringComparison.OrdinalIgnoreCase)) + { + RunWithProfiler(args); + return; + } + + // Default: Run BenchmarkDotNet var config = DefaultConfig.Instance.AddJob( Job.Default.WithToolchain(InProcessEmitToolchain.Instance) .WithWarmupCount(3) // Minimal warmup iterations for CI @@ -19,4 +28,85 @@ public class Program BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); } + + private static void RunWithProfiler(string[] args) + { + var profileType = "cpu"; // Default to CPU profiling + var outputPath = "./profiler-snapshots"; + + // Parse arguments + for (int i = 1; i < args.Length; i++) + { + if (args[i].Equals("--type", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length) + { + profileType = args[++i].ToLowerInvariant(); + } + else if ( + args[i].Equals("--output", StringComparison.OrdinalIgnoreCase) + && i + 1 < args.Length + ) + { + outputPath = args[++i]; + } + } + + Console.WriteLine($"Running with JetBrains Profiler ({profileType} mode)"); + Console.WriteLine($"Output path: {outputPath}"); + Console.WriteLine(); + Console.WriteLine( + "Usage: dotnet run --project SharpCompress.Performance.csproj -c Release -- --profile [--type cpu|memory] [--output ]" + ); + Console.WriteLine(); + + // Run a sample benchmark with profiling + RunSampleBenchmarkWithProfiler(profileType, outputPath); + } + + private static void RunSampleBenchmarkWithProfiler(string profileType, string outputPath) + { + Console.WriteLine("Running sample benchmark with profiler..."); + Console.WriteLine("Note: JetBrains profiler requires the profiler tools to be installed."); + Console.WriteLine("Install from: https://www.jetbrains.com/profiler/"); + Console.WriteLine(); + + try + { + IDisposable? profiler = null; + + if (profileType == "cpu") + { + profiler = Test.JetbrainsProfiler.Cpu(outputPath); + } + else if (profileType == "memory") + { + profiler = Test.JetbrainsProfiler.Memory(outputPath); + } + + using (profiler) + { + // Run a simple benchmark iteration + var zipBenchmark = new Benchmarks.ZipBenchmarks(); + zipBenchmark.Setup(); + + Console.WriteLine("Running benchmark iterations..."); + for (int i = 0; i < 10; i++) + { + zipBenchmark.ZipExtractArchiveApi(); + if (i % 3 == 0) + { + Console.Write("."); + } + } + Console.WriteLine(); + Console.WriteLine("Benchmark iterations completed."); + } + + Console.WriteLine($"Profiler snapshot saved to: {outputPath}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error running profiler: {ex.Message}"); + Console.WriteLine("Make sure JetBrains profiler tools are installed and accessible."); + } + } } diff --git a/tests/SharpCompress.Performance/README.md b/tests/SharpCompress.Performance/README.md index 8056fd7b..91ebfb33 100644 --- a/tests/SharpCompress.Performance/README.md +++ b/tests/SharpCompress.Performance/README.md @@ -53,11 +53,51 @@ dotnet run --project tests/SharpCompress.Performance/SharpCompress.Performance.c The baseline results are stored in `baseline-results.md` and represent the expected performance characteristics of the library. These results are used in CI to detect significant performance regressions. -To update the baseline: +### Generate Baseline (Automated) + +Use the build target to generate baseline results: +```bash +dotnet run --project build/build.csproj -- generate-baseline +``` + +This will: +1. Build the performance project +2. Run all benchmarks +3. Combine the markdown reports into `baseline-results.md` +4. Clean up temporary artifacts + +### Generate Baseline (Manual) + +To manually update the baseline: 1. Run the benchmarks: `dotnet run --project tests/SharpCompress.Performance/SharpCompress.Performance.csproj --configuration Release -- --exporters markdown --artifacts baseline-output` 2. Combine the results: `cat baseline-output/results/*-report-github.md > baseline-results.md` 3. Review the changes and commit if appropriate +## JetBrains Profiler Integration + +The performance project supports JetBrains profiler for detailed CPU and memory profiling during local development. + +### Prerequisites + +Install JetBrains profiler tools from: https://www.jetbrains.com/profiler/ + +### Run with CPU Profiling +```bash +dotnet run --project tests/SharpCompress.Performance/SharpCompress.Performance.csproj --configuration Release -- --profile --type cpu --output ./my-cpu-snapshots +``` + +### Run with Memory Profiling +```bash +dotnet run --project tests/SharpCompress.Performance/SharpCompress.Performance.csproj --configuration Release -- --profile --type memory --output ./my-memory-snapshots +``` + +### Profiler Options +- `--profile`: Enable profiler mode +- `--type cpu|memory`: Choose profiling type (default: cpu) +- `--output `: Specify snapshot output directory (default: ./profiler-snapshots) + +The profiler will run a sample benchmark and save snapshots that can be opened in JetBrains profiler tools for detailed analysis. + ## CI Integration The performance benchmarks run automatically in GitHub Actions on: