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..c12c664b88 100644
--- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
+++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
@@ -86,9 +86,32 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
var globalChronometer = Chronometer.Start();
- var buildPartitions = BenchmarkPartitioner.CreateForBuild(supportedBenchmarks, resolver);
- eventProcessor.OnStartBuildStage(buildPartitions);
- var buildResults = BuildInParallel(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer, eventProcessor);
+ var parallelBuildBenchmarks = supportedBenchmarks.Where(x => !x.Config.Options.IsSet(ConfigOptions.DisableParallelBuild)).ToArray();
+ var parallelBuildPartitions = BenchmarkPartitioner.CreateForBuild(parallelBuildBenchmarks, resolver);
+
+ 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);
@@ -408,10 +431,30 @@ private static Dictionary BuildInParallel(ILogger l
logger.WriteLineHeader($"// ***** Done, took {GetFormattedDifference(afterParallelBuild, afterSequentialBuild)} *****");
return buildResults;
+ }
+
+ private static Dictionary BuildSequential(ILogger logger, string rootArtifactsFolderPath, BuildPartition[] buildPartitions, in StartedClock globalChronometer, EventProcessor eventProcessor)
+ {
+ logger.WriteLineHeader($"// ***** Building {buildPartitions.Length} exe(s) in Sequential: Start *****");
+
+ var beforeBuild = globalChronometer.GetElapsed();
+
+ var buildResults = new Dictionary();
+ foreach (var buildPartition in buildPartitions)
+ {
+ buildResults[buildPartition] = Build(buildPartition, rootArtifactsFolderPath, logger);
+ 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)
+ 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)
{