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) {