From 7de44dc93cbfbebfb847ff284b678254a1647883 Mon Sep 17 00:00:00 2001
From: filzrev <103790468+filzrev@users.noreply.github.com>
Date: Sun, 27 Apr 2025 18:34:10 +0900
Subject: [PATCH 1/3] feat: add option to disable parallel build
---
src/BenchmarkDotNet/Configs/ConfigOptions.cs | 6 +++-
.../Running/BenchmarkRunnerClean.cs | 31 ++++++++++++++++++-
2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/src/BenchmarkDotNet/Configs/ConfigOptions.cs b/src/BenchmarkDotNet/Configs/ConfigOptions.cs
index c1ad75d642..06bc7768c7 100644
--- a/src/BenchmarkDotNet/Configs/ConfigOptions.cs
+++ b/src/BenchmarkDotNet/Configs/ConfigOptions.cs
@@ -48,7 +48,11 @@ public enum ConfigOptions
///
/// Continue the execution if the last run was stopped.
///
- Resume = 1 << 9
+ Resume = 1 << 9,
+ ///
+ /// Determines whether parallel build of benchmark projects should be disabled.
+ ///
+ DisableParallelBuild = 1 << 10,
}
internal static class ConfigOptionsExtensions
diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
index 4f520da38d..12773880fc 100644
--- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
+++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
@@ -88,7 +88,11 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
var buildPartitions = BenchmarkPartitioner.CreateForBuild(supportedBenchmarks, resolver);
eventProcessor.OnStartBuildStage(buildPartitions);
- var buildResults = BuildInParallel(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer, eventProcessor);
+
+ bool useParallelBuild = !supportedBenchmarks.Any(x => x.Config.Options.IsSet(ConfigOptions.DisableParallelBuild));
+ var buildResults = useParallelBuild
+ ? BuildInParallel(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer, eventProcessor)
+ : BuildSequential(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer, eventProcessor);
var allBuildsHaveFailed = buildResults.Values.All(buildResult => !buildResult.IsBuildSuccess);
@@ -413,6 +417,31 @@ static string GetFormattedDifference(ClockSpan before, ClockSpan after)
=> (after.GetTimeSpan() - before.GetTimeSpan()).ToFormattedTotalTime(DefaultCultureInfo.Instance);
}
+ private static Dictionary BuildSequential(ILogger logger, string rootArtifactsFolderPath, BuildPartition[] buildPartitions, in StartedClock globalChronometer, EventProcessor eventProcessor)
+ {
+ logger.WriteLineHeader($"// ***** Building {buildPartitions.Length} exe(s): Start *****");
+
+ var buildLogger = buildPartitions.Length == 1 ? logger : NullLogger.Instance; // when we have just one partition we can print to std out
+
+ var beforeBuild = globalChronometer.GetElapsed();
+
+ var buildResults = new Dictionary();
+ foreach (var buildPartition in buildPartitions)
+ {
+ buildResults[buildPartition] = Build(buildPartition, rootArtifactsFolderPath, buildLogger);
+ eventProcessor.OnBuildComplete(buildPartition, buildResults[buildPartition]);
+ }
+
+ var afterBuild = globalChronometer.GetElapsed();
+
+ logger.WriteLineHeader($"// ***** Done, took {GetFormattedDifference(beforeBuild, afterBuild)} *****");
+
+ return buildResults;
+
+ static string GetFormattedDifference(ClockSpan before, ClockSpan after)
+ => (after.GetTimeSpan() - before.GetTimeSpan()).ToFormattedTotalTime(DefaultCultureInfo.Instance);
+ }
+
private static BuildResult Build(BuildPartition buildPartition, string rootArtifactsFolderPath, ILogger buildLogger)
{
var toolchain = buildPartition.RepresentativeBenchmarkCase.GetToolchain(); // it's guaranteed that all the benchmarks in single partition have same toolchain
From 973ae6caf851943479a0915f3829c4e0a6c4ae27 Mon Sep 17 00:00:00 2001
From: filzrev <103790468+filzrev@users.noreply.github.com>
Date: Sat, 3 May 2025 09:31:21 +0900
Subject: [PATCH 2/3] chore: fix code pointed out by review
---
src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
index 12773880fc..f0a363e22d 100644
--- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
+++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
@@ -411,24 +411,19 @@ private static Dictionary BuildInParallel(ILogger l
logger.WriteLineHeader($"// ***** Done, took {GetFormattedDifference(afterParallelBuild, afterSequentialBuild)} *****");
- return buildResults;
-
- static string GetFormattedDifference(ClockSpan before, ClockSpan after)
- => (after.GetTimeSpan() - before.GetTimeSpan()).ToFormattedTotalTime(DefaultCultureInfo.Instance);
+ return buildResults;
}
private static Dictionary BuildSequential(ILogger logger, string rootArtifactsFolderPath, BuildPartition[] buildPartitions, in StartedClock globalChronometer, EventProcessor eventProcessor)
{
logger.WriteLineHeader($"// ***** Building {buildPartitions.Length} exe(s): Start *****");
- var buildLogger = buildPartitions.Length == 1 ? logger : NullLogger.Instance; // when we have just one partition we can print to std out
-
var beforeBuild = globalChronometer.GetElapsed();
var buildResults = new Dictionary();
foreach (var buildPartition in buildPartitions)
{
- buildResults[buildPartition] = Build(buildPartition, rootArtifactsFolderPath, buildLogger);
+ buildResults[buildPartition] = Build(buildPartition, rootArtifactsFolderPath, logger);
eventProcessor.OnBuildComplete(buildPartition, buildResults[buildPartition]);
}
@@ -437,10 +432,10 @@ private static Dictionary BuildSequential(ILogger l
logger.WriteLineHeader($"// ***** Done, took {GetFormattedDifference(beforeBuild, afterBuild)} *****");
return buildResults;
+ }
- static string GetFormattedDifference(ClockSpan before, ClockSpan after)
+ private static string GetFormattedDifference(ClockSpan before, ClockSpan after)
=> (after.GetTimeSpan() - before.GetTimeSpan()).ToFormattedTotalTime(DefaultCultureInfo.Instance);
- }
private static BuildResult Build(BuildPartition buildPartition, string rootArtifactsFolderPath, ILogger buildLogger)
{
From 409425e4ccd13bf16c3a0a446e866053736073c0 Mon Sep 17 00:00:00 2001
From: filzrev <103790468+filzrev@users.noreply.github.com>
Date: Sat, 3 May 2025 11:21:56 +0900
Subject: [PATCH 3/3] chore: add parallel/sequential build support based on
options
---
.../Running/BenchmarkRunnerClean.cs | 35 ++++++++++++++-----
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
index f0a363e22d..c12c664b88 100644
--- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
+++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
@@ -86,13 +86,32 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
var globalChronometer = Chronometer.Start();
- var buildPartitions = BenchmarkPartitioner.CreateForBuild(supportedBenchmarks, resolver);
- eventProcessor.OnStartBuildStage(buildPartitions);
+ var parallelBuildBenchmarks = supportedBenchmarks.Where(x => !x.Config.Options.IsSet(ConfigOptions.DisableParallelBuild)).ToArray();
+ var parallelBuildPartitions = BenchmarkPartitioner.CreateForBuild(parallelBuildBenchmarks, resolver);
- bool useParallelBuild = !supportedBenchmarks.Any(x => x.Config.Options.IsSet(ConfigOptions.DisableParallelBuild));
- var buildResults = useParallelBuild
- ? BuildInParallel(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer, eventProcessor)
- : BuildSequential(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer, eventProcessor);
+ var sequentialBuildBenchmarks = supportedBenchmarks.Where(x => x.Config.Options.IsSet(ConfigOptions.DisableParallelBuild)).ToArray();
+ var sequentialBuildPartitions = BenchmarkPartitioner.CreateForBuild(sequentialBuildBenchmarks, resolver);
+
+ eventProcessor.OnStartBuildStage([.. parallelBuildPartitions, .. sequentialBuildPartitions]);
+
+ var buildResults = new Dictionary();
+ if (parallelBuildBenchmarks.Length > 0)
+ {
+ var results = BuildInParallel(compositeLogger, rootArtifactsFolderPath, parallelBuildPartitions, in globalChronometer, eventProcessor);
+ foreach (var kvp in results)
+ {
+ buildResults.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ if (sequentialBuildBenchmarks.Length > 0)
+ {
+ var results = BuildSequential(compositeLogger, rootArtifactsFolderPath, sequentialBuildPartitions, in globalChronometer, eventProcessor);
+ foreach (var kvp in results)
+ {
+ buildResults.Add(kvp.Key, kvp.Value);
+ }
+ }
var allBuildsHaveFailed = buildResults.Values.All(buildResult => !buildResult.IsBuildSuccess);
@@ -411,12 +430,12 @@ private static Dictionary BuildInParallel(ILogger l
logger.WriteLineHeader($"// ***** Done, took {GetFormattedDifference(afterParallelBuild, afterSequentialBuild)} *****");
- return buildResults;
+ return buildResults;
}
private static Dictionary BuildSequential(ILogger logger, string rootArtifactsFolderPath, BuildPartition[] buildPartitions, in StartedClock globalChronometer, EventProcessor eventProcessor)
{
- logger.WriteLineHeader($"// ***** Building {buildPartitions.Length} exe(s): Start *****");
+ logger.WriteLineHeader($"// ***** Building {buildPartitions.Length} exe(s) in Sequential: Start *****");
var beforeBuild = globalChronometer.GetElapsed();