diff --git a/src/Utilities/TrackedDependencies/CanonicalTrackedInputFiles.cs b/src/Utilities/TrackedDependencies/CanonicalTrackedInputFiles.cs index a6281533708..be7d6f571f5 100644 --- a/src/Utilities/TrackedDependencies/CanonicalTrackedInputFiles.cs +++ b/src/Utilities/TrackedDependencies/CanonicalTrackedInputFiles.cs @@ -1033,6 +1033,9 @@ private void RemoveDependencyFromEntry(string rootingMarker, ITaskItem dependenc /// Outputs that correspond ot the sources (used for same file processing) public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] correspondingOutputs) { + // Cache of files that have been checked and exist. + Dictionary fileCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (correspondingOutputs != null) { ErrorUtilities.VerifyThrowArgument(source.Length == correspondingOutputs.Length, "Tracking_SourcesAndCorrespondingOutputMismatch"); @@ -1041,7 +1044,7 @@ public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] // construct a combined root marker for the sources and outputs to remove from the graph string rootingMarker = FileTracker.FormatRootingMarker(source, correspondingOutputs); - RemoveDependenciesFromEntryIfMissing(rootingMarker); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); // Remove entries for each individual source for (int sourceIndex = 0; sourceIndex < source.Length; sourceIndex++) @@ -1049,7 +1052,7 @@ public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] rootingMarker = correspondingOutputs != null ? FileTracker.FormatRootingMarker(source[sourceIndex], correspondingOutputs[sourceIndex]) : FileTracker.FormatRootingMarker(source[sourceIndex]); - RemoveDependenciesFromEntryIfMissing(rootingMarker); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); } } @@ -1057,7 +1060,8 @@ public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] /// Remove the output graph entries for the given rooting marker /// /// - private void RemoveDependenciesFromEntryIfMissing(string rootingMarker) + /// The cache used to store whether each file exists or not. + private void RemoveDependenciesFromEntryIfMissing(string rootingMarker, Dictionary fileCache) { // In the event of incomplete tracking information (i.e. this root was not present), just continue quietly // as the user could have killed the tool being tracked, or another error occurred during its execution. @@ -1070,8 +1074,19 @@ private void RemoveDependenciesFromEntryIfMissing(string rootingMarker) { if (keyIndex++ > 0) { - // If we are ignoring missing files, then only record those that exist - if (FileUtilities.FileExistsNoThrow(file)) + // Record whether or not each file exists and cache it. + // We do this to save time (On^2), at the expense of data O(n). + bool inFileCache = fileCache.TryGetValue(file, out bool fileExists); + + // Have we cached the file yet? If not, cache whether or not it exists. + if (!inFileCache) + { + fileExists = FileUtilities.FileExistsNoThrow(file); + fileCache.Add(file, fileExists); + } + + // Does the cached file exist? + if (fileExists) { dependenciesWithoutMissingFiles.Add(file, dependencies[file]); } diff --git a/src/Utilities/TrackedDependencies/CanonicalTrackedOutputFiles.cs b/src/Utilities/TrackedDependencies/CanonicalTrackedOutputFiles.cs index 5c906de1683..7ca96f897c0 100644 --- a/src/Utilities/TrackedDependencies/CanonicalTrackedOutputFiles.cs +++ b/src/Utilities/TrackedDependencies/CanonicalTrackedOutputFiles.cs @@ -724,6 +724,9 @@ private void RemoveDependencyFromEntry(string rootingMarker, ITaskItem dependenc /// Outputs that correspond ot the sources (used for same file processing) public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] correspondingOutputs) { + // Cache of files and whether or not they exist. + Dictionary fileCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (correspondingOutputs != null) { ErrorUtilities.VerifyThrowArgument(source.Length == correspondingOutputs.Length, "Tracking_SourcesAndCorrespondingOutputMismatch"); @@ -732,21 +735,15 @@ public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] // construct a combined root marker for the sources and outputs to remove from the graph string rootingMarker = FileTracker.FormatRootingMarker(source, correspondingOutputs); - RemoveDependenciesFromEntryIfMissing(rootingMarker); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); // Remove entries for each individual source for (int sourceIndex = 0; sourceIndex < source.Length; sourceIndex++) { - if (correspondingOutputs != null) - { - rootingMarker = FileTracker.FormatRootingMarker(source[sourceIndex], correspondingOutputs[sourceIndex]); - } - else - { - rootingMarker = FileTracker.FormatRootingMarker(source[sourceIndex]); - } - - RemoveDependenciesFromEntryIfMissing(rootingMarker); + rootingMarker = correspondingOutputs != null + ? FileTracker.FormatRootingMarker(source[sourceIndex], correspondingOutputs[sourceIndex]) + : FileTracker.FormatRootingMarker(source[sourceIndex]); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); } } @@ -754,7 +751,8 @@ public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] /// Remove the output graph entries for the given rooting marker /// /// - private void RemoveDependenciesFromEntryIfMissing(string rootingMarker) + /// The cache used to store whether each file exists or not. + private void RemoveDependenciesFromEntryIfMissing(string rootingMarker, Dictionary fileCache) { // In the event of incomplete tracking information (i.e. this root was not present), just continue quietly // as the user could have killed the tool being tracked, or another error occurred during its execution. @@ -767,8 +765,19 @@ private void RemoveDependenciesFromEntryIfMissing(string rootingMarker) { if (keyIndex++ > 0) { - // If we are ignoring missing files, then only record those that exist - if (FileUtilities.FileExistsNoThrow(file)) + // Record whether or not each file exists and cache it. + // We do this to save time (On^2), at the expense of data O(n). + bool inFileCache = fileCache.TryGetValue(file, out bool fileExists); + + // Have we cached the file yet? If not, cache its existence. + if (!inFileCache) + { + fileExists = FileUtilities.FileExistsNoThrow(file); + fileCache.Add(file, fileExists); + } + + // Does the cached file exist? + if (fileExists) { dependenciesWithoutMissingFiles.Add(file, dependencies[file]); }