Skip to content

Commit c802c85

Browse files
perf: improve performance of isForMissingSuperclass (again).
1 parent 38723e8 commit c802c85

File tree

4 files changed

+30
-9
lines changed

4 files changed

+30
-9
lines changed

graph-support/src/main/kotlin/com/autonomousapps/graph/Graphs.kt

+9
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ public object Graphs {
4343
}
4444
}
4545

46+
// TODO(tsr): this function hasn't been published yet.
47+
/** Returns all nodes in this graph that are reachable from all nodes matching [predicate]. */
48+
public fun <N : Any> Graph<N>.reachableNodesMatching(predicate: (N) -> Boolean): Set<N> {
49+
return nodes().asSequence()
50+
.filter(predicate)
51+
.flatMap { node -> GuavaGraphs.reachableNodes(this, node) }
52+
.toSet()
53+
}
54+
4655
/**
4756
* Returns the first node it finds that has an in-degree of 0. This is the root node if this DAG contains only one
4857
* such node.

src/main/kotlin/com/autonomousapps/model/internal/ProjectVariant.kt

+6-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ internal data class ProjectVariant(
2929
sources.filterIsInstance<CodeSource>()
3030
}
3131

32+
val classNames: Set<String> by unsafeLazy {
33+
codeSource.mapToOrderedSet { src -> src.className }
34+
}
35+
3236
val usedNonAnnotationClassesBySrc: Set<String> by unsafeLazy {
3337
codeSource.flatMapToSet {
3438
it.usedNonAnnotationClasses
@@ -89,11 +93,9 @@ internal data class ProjectVariant(
8993
val externalSupers: Set<String> by unsafeLazy {
9094
val supers = codeSource.mapNotNullToOrderedSet { src -> src.superClass }
9195
val interfaces = codeSource.flatMapToOrderedSet { src -> src.interfaces }
92-
// These are all the super classes and interfaces in "this" module
93-
val localClasses = codeSource.mapToOrderedSet { src -> src.className }
9496
// These super classes and interfaces are not available from "this" module, so must come from dependencies.
95-
val externalSupers = supers - localClasses
96-
val externalInterfaces = interfaces - localClasses
97+
val externalSupers = supers - classNames
98+
val externalInterfaces = interfaces - classNames
9799
val externals = externalSupers + externalInterfaces
98100

99101
externals

src/main/kotlin/com/autonomousapps/tasks/ComputeUsagesTask.kt

+12-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import com.autonomousapps.model.internal.intermediates.DependencyTraceReport.Kin
1717
import com.autonomousapps.model.internal.intermediates.Reason
1818
import com.autonomousapps.visitor.GraphViewReader
1919
import com.autonomousapps.visitor.GraphViewVisitor
20+
import com.google.common.graph.Graphs
2021
import org.gradle.api.DefaultTask
2122
import org.gradle.api.file.DirectoryProperty
2223
import org.gradle.api.file.RegularFile
@@ -373,19 +374,25 @@ private class GraphVisitor(
373374
context: GraphViewVisitor.Context,
374375
): Boolean {
375376
val superGraph = context.superGraph
377+
val externalSupers = context.project.externalSupers
376378

377379
// collect all the dependencies associated with external supers
378-
val requiredExternalClasses = context.project.externalSupers.asSequence()
379-
.flatMap { external -> superGraph.reachableNodes(false) { it.className == external } }
380-
.mapNotNull { node ->
381-
val deps = node.deps.filterToOrderedSet { dep ->
380+
// nb: we start by iterating over `supergraph.nodes()`, and then filtering, as that is _far more efficient_
381+
// then iterating over `externalSupers` and then calling `supergraph.nodes()` repeatedly: I have observed graphs
382+
// with hundreds of thousands of nodes. This is why we use Guava directly here rather than going through our own
383+
// Graphs wrapper. There's a yet-to-be-published update to the wrapper that does this for us.
384+
val requiredExternalClasses = superGraph.nodes().asSequence()
385+
.filter { superNode -> superNode.className in externalSupers }
386+
.flatMap { superNode -> Graphs.reachableNodes(superGraph, superNode) }
387+
.mapNotNull { superNode ->
388+
val deps = superNode.deps.filterToOrderedSet { dep ->
382389
// If dep has just one parent and it's the root, then we must retain that edge
383390
val graph = context.graph.graph
384391
graph.parents(dep).singleOrNull { it == graph.root() } != null
385392
}
386393

387394
if (deps.isNotEmpty()) {
388-
SuperNode(node.className).apply { this.deps += deps }
395+
SuperNode(superNode.className).apply { this.deps += deps }
389396
} else {
390397
null
391398
}

src/main/kotlin/com/autonomousapps/visitor/GraphViewReader.kt

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ internal class DefaultContext(
3737
) : GraphViewVisitor.Context {
3838

3939
override val superGraph: Graph<SuperNode> by unsafeLazy {
40+
// TODO(tsr): use localClassNames to build smaller graphs? May be necessary to further improve performance of
41+
// ComputeUsagesAction::isForMissingSuperclass
42+
// SuperClassGraphBuilder.of(dependencies, project.classNames)
4043
SuperClassGraphBuilder.of(dependencies)
4144
}
4245
}

0 commit comments

Comments
 (0)