Skip to content

Commit c2c9047

Browse files
authored
Performance improvements of graph calculations (#151)
* Move mutation methods out of the DependencyGraph to signal the graph is immutable after creation * Add test for a very large graph
1 parent a8b0b2b commit c2c9047

File tree

3 files changed

+15316
-19
lines changed

3 files changed

+15316
-19
lines changed

plugin/src/main/kotlin/com/jraska/module/graph/DependencyGraph.kt

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,14 @@ class DependencyGraph private constructor() {
6161
fun subTree(key: String): DependencyGraph {
6262
require(nodes.contains(key)) { "Dependency Tree doesn't contain module: $key" }
6363

64-
val dependencyTree = createSingular(key)
64+
val connections = mutableListOf<Pair<String, String>>()
65+
addConnections(nodes.getValue(key), connections)
6566

66-
addConnections(nodes.getValue(key), dependencyTree)
67-
68-
return dependencyTree
67+
return if (connections.isEmpty()) {
68+
createSingular(key)
69+
} else {
70+
create(connections)
71+
}
6972
}
7073

7174
fun serializableGraph(): SerializableGraph {
@@ -75,25 +78,17 @@ class DependencyGraph private constructor() {
7578
)
7679
}
7780

78-
private fun addConnections(node: Node, into: DependencyGraph) {
81+
private fun addConnections(node: Node, into: MutableList<Pair<String, String>>) {
7982
node.dependsOn.forEach {
80-
into.addEdge(node.key, it.key)
83+
into.add(node.key to it.key)
8184
addConnections(it, into)
8285
}
8386
}
8487

85-
private fun addEdge(from: String, to: String) {
86-
getOrCreate(from).dependsOn.add(getOrCreate(to))
87-
}
88-
8988
private fun countEdges(): Int {
9089
return nodes().flatMap { node -> node.dependsOn }.count()
9190
}
9291

93-
private fun getOrCreate(key: String): Node {
94-
return nodes[key] ?: Node(key).also { nodes[key] = it }
95-
}
96-
9792
class SerializableGraph(
9893
val dependencyPairs: ArrayList<Pair<String, String>>,
9994
val firstModule: String
@@ -102,16 +97,20 @@ class DependencyGraph private constructor() {
10297
class Node(val key: String) {
10398
val dependsOn = mutableSetOf<Node>()
10499

105-
private fun isLeaf() = dependsOn.isEmpty()
106-
107-
fun height(): Int {
100+
private val calculatedHeight by lazy {
108101
if (isLeaf()) {
109-
return 0
102+
0
110103
} else {
111-
return 1 + dependsOn.map { it.height() }.maxOrNull()!!
104+
1 + dependsOn.map { it.height() }.maxOrNull()!!
112105
}
113106
}
114107

108+
private fun isLeaf() = dependsOn.isEmpty()
109+
110+
fun height(): Int {
111+
return calculatedHeight
112+
}
113+
115114
internal fun longestPath(): List<Node> {
116115
if (isLeaf()) {
117116
return listOf(this)
@@ -156,5 +155,13 @@ class DependencyGraph private constructor() {
156155
return create(graph.dependencyPairs)
157156
}
158157
}
158+
159+
private fun DependencyGraph.addEdge(from: String, to: String) {
160+
getOrCreate(from).dependsOn.add(getOrCreate(to))
161+
}
162+
163+
private fun DependencyGraph.getOrCreate(key: String): Node {
164+
return nodes[key] ?: Node(key).also { nodes[key] = it }
165+
}
159166
}
160167
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.jraska.module.graph
2+
3+
import org.junit.Before
4+
import org.junit.Test
5+
import java.io.File
6+
7+
class DependencyGraphPerformanceTest {
8+
lateinit var dependencyGraph: DependencyGraph
9+
10+
@Before
11+
fun setUp() {
12+
val uri = javaClass.classLoader.getResource("graph/large-graph.txt")
13+
val file = File(uri?.path!!)
14+
val dependencyPairs = file.readLines().map {
15+
val parts = it.split(" -> ")
16+
parts[0] to parts[1]
17+
}
18+
19+
dependencyGraph = DependencyGraph.create(dependencyPairs)
20+
}
21+
22+
@Test(timeout = 1000) // 1000 ms is more than enough - it was taking hours before optimisations
23+
fun whenTheGraphIsLarge_statisticsCalculatedFast() {
24+
val statistics = dependencyGraph.statistics()
25+
26+
assert(statistics.height == 59)
27+
assert(statistics.modulesCount == 1000)
28+
assert(statistics.edgesCount == 15259)
29+
assert(statistics.longestPath.pathString().startsWith("23 -> 31 -> 36 -> 57 -> 61 -> 72 -> 74 -> 75"))
30+
}
31+
}

0 commit comments

Comments
 (0)