From 29b22ff1c1c1cdbed33ff6d3007cfb330bf5820a Mon Sep 17 00:00:00 2001 From: WeijunDeng Date: Wed, 23 Apr 2025 11:28:17 +0800 Subject: [PATCH] [Incremental Builds]optimize performance for ModuleDependencyGraph by reducing the number of external dependencies from N*M to N+M --- .../ModuleDependencyGraph.swift | 24 +++++++++++ .../NodeFinder.swift | 41 ++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift b/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift index 621d9778a..710ec6e90 100644 --- a/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift @@ -1350,6 +1350,30 @@ extension ModuleDependencyGraph { } } } + if graph.nodeFinder.enableExperimatalExternalDependenciesHashOptimization { + var hasWrittenAllExternalDependentNodes = false + for key in graph.nodeFinder.externalDependencies { + serializer.stream.writeRecord(serializer.abbreviations[.dependsOnNode]!) { + $0.append(RecordID.dependsOnNode) + write(key: key, to: &$0) + } + + for use in graph.nodeFinder.externalDependentNodes { + guard let useID = serializer.nodeIDs[use] else { + fatalError("Node ID was not registered! \(use)") + } + + serializer.stream.writeRecord(serializer.abbreviations[.useIDNode]!) { + $0.append(RecordID.useIDNode) + $0.append(UInt32(useID)) + } + if hasWrittenAllExternalDependentNodes { + break + } + } + hasWrittenAllExternalDependentNodes = true + } + } for fingerprintedExternalDependency in graph.fingerprintedExternalDependencies { serializer.stream.writeRecord(serializer.abbreviations[.externalDepNode]!) { $0.append(RecordID.externalDepNode) diff --git a/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/NodeFinder.swift b/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/NodeFinder.swift index 629618372..be351c466 100644 --- a/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/NodeFinder.swift +++ b/Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraphParts/NodeFinder.swift @@ -33,8 +33,16 @@ extension ModuleDependencyGraph { /// that would need to change if/when we can recompile a smaller unit than a /// source file.) - /// Tracks def-use relationships by DependencyKey. + /// Tracks def-use relationships by DependencyKey except for external expendencies (Reduce the number of external dependencies from N*M to N+M). @_spi(Testing) public private(set) var usesByDef = Multidictionary() + + /// Tracks external DependencyKey. + @_spi(Testing) public private(set) var externalDependencies = Set() + + /// Tracks nodes which dependent external DependencyKey. + @_spi(Testing) public private(set) var externalDependentNodes = Set() + + public let enableExperimatalExternalDependenciesHashOptimization = true } } // MARK: - finding @@ -78,6 +86,11 @@ extension ModuleDependencyGraph.NodeFinder { /// definition node. func uses(of def: Graph.Node) -> Set { var uses = usesByDef[def.key, default: Set()] + if enableExperimatalExternalDependenciesHashOptimization { + if externalDependencies.contains(def.key) { + uses = externalDependentNodes + } + } if let impl = findCorrespondingImplementation(of: def) { uses.insert(impl) } @@ -99,7 +112,12 @@ extension ModuleDependencyGraph.NodeFinder { } func defsUsing(_ n: Graph.Node) -> Set { - usesByDef.keysContainingValue(n) + if enableExperimatalExternalDependenciesHashOptimization { + if externalDependentNodes.contains(n) { + return usesByDef.keysContainingValue(n).union(externalDependencies) + } + } + return usesByDef.keysContainingValue(n) } } @@ -122,6 +140,13 @@ extension ModuleDependencyGraph.NodeFinder { /// record def-use, return if is new use mutating func record(def: DependencyKey, use: Graph.Node) -> Bool { assert(verifyOKTODependUponSomeKey(use)) + if enableExperimatalExternalDependenciesHashOptimization { + if let _ = def.designator.externalDependency { + let inserted1 = externalDependencies.insert(def).inserted + let inserted2 = externalDependentNodes.insert(use).inserted + return inserted1 || inserted2 + } + } return usesByDef.insertValue(use, forKey: def) } } @@ -135,6 +160,12 @@ extension ModuleDependencyGraph.NodeFinder { } private mutating func removeUsings(of nodeToNotUse: Graph.Node) { + if enableExperimatalExternalDependenciesHashOptimization { + if externalDependentNodes.contains(nodeToNotUse) { + externalDependentNodes.remove(nodeToNotUse) + return + } + } usesByDef.removeOccurrences(of: nodeToNotUse) assert(defsUsing(nodeToNotUse).isEmpty) } @@ -164,6 +195,12 @@ extension ModuleDependencyGraph.NodeFinder { definitionLocation: .known(newDependencySource)) assert(original.definitionLocation == .unknown, "Would have to search every use in usesByDef if original could be a use.") + + if enableExperimatalExternalDependenciesHashOptimization { + if externalDependentNodes.remove(original) != nil { + externalDependentNodes.insert(replacement) + } + } if usesByDef.removeValue(original, forKey: original.key) != nil { usesByDef.insertValue(replacement, forKey: original.key) }