public void DuplicateInputsAndOutputsMergePredictedBys() { var predictors = new IProjectPredictor[] { new MockPredictor( new[] { @"common\inputFile" }, new[] { @"common\inputDirectory" }, new[] { @"common\outputFile" }, new[] { @"common\outputDirectory" }), new MockPredictor2( new[] { @"common\inputFile" }, new[] { @"common\inputDirectory" }, new[] { @"common\outputFile" }, new[] { @"common\outputDirectory" }), }; var executor = new ProjectPredictionExecutor(predictors); var project = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create()); ProjectPredictions predictions = executor.PredictInputsAndOutputs(project); predictions.AssertPredictions( project, new[] { new PredictedItem(@"common\inputFile", "MockPredictor", "MockPredictor2"), }, new[] { new PredictedItem(@"common\inputDirectory", "MockPredictor", "MockPredictor2"), }, new[] { new PredictedItem(@"common\outputFile", "MockPredictor", "MockPredictor2"), }, new[] { new PredictedItem(@"common\outputDirectory", "MockPredictor", "MockPredictor2"), }); }
public void DistinctInputsAndOutputsAreAggregated() { /* * Missed value in the input files: * PredictedItem: 1\inputFile; PredictedBy=MockPredictor * from among actual list * PredictedItem: E:\MSBuildPrediction\src\BuildPredictionTests\bin\Debug\net472\2\inputFile; PredictedBy=MockPredictor2:: PredictedItem: E:\MSBuildPrediction\src\BuildPredictionTests\bin\Debug\net472\1\inputFile; PredictedBy=MockPredictor */ var predictors = new IProjectPredictor[] { new MockPredictor( new[] { @"1\inputFile" }, new[] { @"1\inputDirectory" }, new[] { @"1\outputFile" }, new[] { @"1\outputDirectory" }), new MockPredictor2( new[] { @"2\inputFile" }, new[] { @"2\inputDirectory" }, new[] { @"2\outputFile" }, new[] { @"2\outputDirectory" }), }; var executor = new ProjectPredictionExecutor(predictors); var project = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create()); ProjectPredictions predictions = executor.PredictInputsAndOutputs(project); predictions.AssertPredictions( project, new[] { new PredictedItem(@"1\inputFile", "MockPredictor"), new PredictedItem(@"2\inputFile", "MockPredictor2"), }, new[] { new PredictedItem(@"1\inputDirectory", "MockPredictor"), new PredictedItem(@"2\inputDirectory", "MockPredictor2"), }, new[] { new PredictedItem(@"1\outputFile", "MockPredictor"), new PredictedItem(@"2\outputFile", "MockPredictor2"), }, new[] { new PredictedItem(@"1\outputDirectory", "MockPredictor"), new PredictedItem(@"2\outputDirectory", "MockPredictor2"), }); }
private static ProjectPredictions GetProjectPredictions(Project project) { // None and Content items are checked more carefully with other mechanisms. // Thus, we're excluding these items from the build predictor here. IEnumerable <IProjectPredictor> buildUpToDatePredictors = ProjectPredictors.AllPredictors.Where(p => !(p is NoneItemsPredictor) && !(p is ContentItemsPredictor)); var predictionExecutor = new ProjectPredictionExecutor(buildUpToDatePredictors); return(predictionExecutor.PredictInputsAndOutputs(project)); }
public void PredictorSparsenessStressTest() { int[] numPredictorCases = { 40 }; // Set to 1000 or more for reduced average noise when using tickResults for measurements. int[] sparsenessPercentages = { 0, 25, 50, 75, 100 }; var tickResults = new long[numPredictorCases.Length][]; var proj = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create()); // Run through twice and keep the second round only - first round affected by JIT overhead. for (int iter = 0; iter < 2; iter++) { for (int p = 0; p < numPredictorCases.Length; p++) { int numPredictors = numPredictorCases[p]; tickResults[p] = new long[sparsenessPercentages.Length]; var predictors = new IProjectPredictor[numPredictors]; int sparseIndex = 0; for (int s = 0; s < sparsenessPercentages.Length; s++) { int sparsenessPercentage = sparsenessPercentages[s]; for (int i = 0; i < numPredictors; i++) { if (sparseIndex < sparsenessPercentage) { predictors[i] = new MockPredictor(null, null, null, null); } else { predictors[i] = new MockPredictor( new[] { $@"{i}\inputFile" }, new[] { $@"{i}\inputDirectory" }, new[] { $@"{i}\outputFile" }, new[] { $@"{i}\outputDirectory" }); } sparseIndex++; if (sparseIndex > sparsenessPercentage) { sparseIndex = 0; } } var executor = new ProjectPredictionExecutor(predictors); Stopwatch sw = Stopwatch.StartNew(); executor.PredictInputsAndOutputs(proj); sw.Stop(); tickResults[p][s] = sw.ElapsedTicks; Console.WriteLine($"{numPredictors} @ {sparsenessPercentage}%: {sw.ElapsedTicks} ticks"); } } } }
public void EmptyPredictionsResultInEmptyAggregateResult() { var predictors = new IProjectPredictor[] { new MockPredictor(null, null, null, null), new MockPredictor(null, null, null, null), }; var executor = new ProjectPredictionExecutor(predictors); var project = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create()); ProjectPredictions predictions = executor.PredictInputsAndOutputs(project); Assert.NotNull(predictions); Assert.Equal(0, predictions.InputFiles.Count); Assert.Equal(0, predictions.InputDirectories.Count); Assert.Equal(0, predictions.OutputFiles.Count); Assert.Equal(0, predictions.OutputDirectories.Count); }
private static bool TryConstructGraph( ProjectGraph projectGraph, GraphBuilderReporter reporter, ConcurrentDictionary <ProjectInstance, Project> projectInstanceToProjectCache, IReadOnlyCollection <string> entryPointTargets, IReadOnlyCollection <IProjectPredictor> projectPredictorsForTesting, bool allowProjectsWithoutTargetProtocol, out ProjectGraphWithPredictions projectGraphWithPredictions, out string failure) { Contract.Assert(projectGraph != null); var projectNodes = new ProjectWithPredictions[projectGraph.ProjectNodes.Count]; var nodes = projectGraph.ProjectNodes.ToArray(); // Compute the list of targets to run per project reporter.ReportMessage("Computing targets to execute for each project..."); // This dictionary should be exclusively read only at this point, and therefore thread safe var targetsPerProject = projectGraph.GetTargetLists(entryPointTargets.ToArray()); // Bidirectional access from nodes with predictions to msbuild nodes in order to compute node references in the second pass // TODO: revisit the structures, since the projects are known upfront we might be able to use lock-free structures var nodeWithPredictionsToMsBuildNodes = new ConcurrentDictionary <ProjectWithPredictions, ProjectGraphNode>(Environment.ProcessorCount, projectNodes.Length); var msBuildNodesToNodeWithPredictionIndex = new ConcurrentDictionary <ProjectGraphNode, ProjectWithPredictions>(Environment.ProcessorCount, projectNodes.Length); reporter.ReportMessage("Statically predicting inputs and outputs..."); // Create the registered predictors and initialize the prediction executor // The prediction executor potentially initializes third-party predictors, which may contain bugs. So let's be very defensive here IReadOnlyCollection <IProjectPredictor> predictors; try { predictors = projectPredictorsForTesting ?? ProjectPredictors.AllPredictors; } catch (Exception ex) { failure = $"Cannot create standard predictors. An unexpected error occurred. Please contact BuildPrediction project owners with this stack trace: {ex.ToString()}"; projectGraphWithPredictions = new ProjectGraphWithPredictions(new ProjectWithPredictions <string>[] { }); return(false); } // Using single-threaded prediction since we're parallelizing on project nodes instead. var predictionExecutor = new ProjectPredictionExecutor(predictors, new ProjectPredictionOptions { MaxDegreeOfParallelism = 1 }); // Each predictor may return unexpected/incorrect results and targets may not be able to be predicted. We put those failures here for post-processing. ConcurrentQueue <(string predictorName, string failure)> predictionFailures = new ConcurrentQueue <(string, string)>(); var predictedTargetFailures = new ConcurrentQueue <string>(); // The predicted targets to execute (per project) go here var computedTargets = new ConcurrentBigMap <ProjectGraphNode, PredictedTargetsToExecute>(); // When projects are allowed to not implement the target protocol, its references need default targets as a post-processing step var pendingAddDefaultTargets = new ConcurrentBigSet <ProjectGraphNode>(); // First pass // Predict all projects in the graph in parallel and populate ProjectNodes Parallel.For(0, projectNodes.Length, (int i) => { ProjectGraphNode msBuildNode = nodes[i]; ProjectInstance projectInstance = msBuildNode.ProjectInstance; Project project = projectInstanceToProjectCache[projectInstance]; var outputFolderPredictions = new List <string>(); var predictionCollector = new MsBuildOutputPredictionCollector(outputFolderPredictions, predictionFailures); try { // Again, be defensive when using arbitrary predictors predictionExecutor.PredictInputsAndOutputs(project, predictionCollector); } catch (Exception ex) { predictionFailures.Enqueue(( "Unknown predictor", $"Cannot run static predictor on project '{project.FullPath ?? "Unknown project"}'. An unexpected error occurred. Please contact BuildPrediction project owners with this stack trace: {ex.ToString()}")); }