Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit e4be048

Browse files
committed
feat: optimize cycle detection with fastutil and refine application settings
- Replaced standard collections with `fastutil` for improved performance in `JohnsonSimpleCycles`. - Updated application properties: enabled ANSI output and turned off JPA caching-related settings. - Changed Spring application's banner mode to `OFF` for cleaner startup logs.
1 parent 7415ea0 commit e4be048

File tree

3 files changed

+52
-56
lines changed

3 files changed

+52
-56
lines changed

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/CloudCoreInstance.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ class CloudCoreInstance : CloudInstance {
193193

194194
val builder = SpringApplicationBuilder(applicationClass)
195195
.resourceLoader(resourceLoader)
196-
.bannerMode(Banner.Mode.CONSOLE)
196+
.bannerMode(Banner.Mode.OFF)
197197
.banner(SurfSpringBanner())
198198
.properties(properties)
199199
.profiles(springProfile)
@@ -236,7 +236,8 @@ class CloudCoreInstance : CloudInstance {
236236

237237
customizer(builder)
238238

239-
log.atInfo().log("Starting Spring application...")
239+
log.atInfo()
240+
.log("Starting Spring application for ${applicationClass.name} with profile(s): $springProfile")
240241
builder.run()
241242
}
242243
}

surf-cloud-core/surf-cloud-core-common/src/main/resources/application.properties

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,5 @@ spring.sql.init.mode=never
44
spring.main.web-application-type=none
55
logging.include-application-name=false
66
logging.level.root=info
7-
8-
spring.jpa.show-sql=true
9-
spring.jpa.properties.jakarta.persistence.sharedCache.mode=ENABLE_SELECTIVE
10-
spring.jpa.properties.hibernate.generate_statistics=true
11-
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
12-
spring.jpa.properties.hibernate.cache.use_query_cache=true
13-
spring.jpa.properties.hibernate.cache.region.factory_class=jcache
14-
spring.jpa.properties.hibernate.javax.cache.provider=org.ehcache.jsr107.EhcacheCachingProvider
15-
spring.jpa.properties.hibernate.javax.cache.missing_cache_strategy=create
16-
7+
spring.output.ansi.enabled=always
178
spring.aop.proxy-target-class=false

surf-cloud-standalone/src/main/java/dev/slne/surf/cloud/standalone/plugin/entrypoint/strategy/JohnsonSimpleCycles.java

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
import com.google.common.graph.Graph;
55
import com.google.common.graph.GraphBuilder;
66
import com.google.common.graph.MutableGraph;
7-
import com.mojang.datafixers.util.Pair;
7+
import it.unimi.dsi.fastutil.objects.AbstractObject2IntMap;
8+
import it.unimi.dsi.fastutil.objects.Object2IntMap;
9+
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
10+
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
11+
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
12+
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
13+
import it.unimi.dsi.fastutil.objects.ObjectList;
14+
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
15+
import it.unimi.dsi.fastutil.objects.ObjectSet;
816
import java.util.ArrayDeque;
9-
import java.util.ArrayList;
10-
import java.util.HashMap;
11-
import java.util.HashSet;
12-
import java.util.List;
13-
import java.util.Map;
1417
import java.util.Set;
1518
import java.util.function.BiConsumer;
1619
import java.util.function.Consumer;
@@ -32,21 +35,21 @@ public class JohnsonSimpleCycles<V> {
3235
private final Graph<V> graph;
3336

3437
// The main state of the algorithm.
35-
private Consumer<List<V>> cycleConsumer = null;
38+
private Consumer<ObjectList<V>> cycleConsumer = null;
3639
private BiConsumer<V, V> cycleVertexSuccessorConsumer = null; // Paper
3740
private V[] iToV = null;
38-
private Map<V, Integer> vToI = null;
39-
private Set<V> blocked = null;
40-
private Map<V, Set<V>> bSets = null;
41+
private Object2IntMap<V> vToI = null;
42+
private ObjectSet<V> blocked = null;
43+
private Object2ObjectMap<V, ObjectSet<V>> bSets = null;
4144
private ArrayDeque<V> stack = null;
4245

4346
// The state of the embedded Tarjan SCC algorithm.
44-
private List<Set<V>> foundSCCs = null;
47+
private ObjectList<ObjectSet<V>> foundSCCs = null;
4548
private int index = 0;
46-
private Map<V, Integer> vIndex = null;
47-
private Map<V, Integer> vLowlink = null;
49+
private Object2IntMap<V> vIndex = null;
50+
private Object2IntMap<V> vLowlink = null;
4851
private ArrayDeque<V> path = null;
49-
private Set<V> pathSet = null;
52+
private ObjectSet<V> pathSet = null;
5053

5154
/**
5255
* Create a simple cycle finder for the specified graph.
@@ -64,8 +67,8 @@ public JohnsonSimpleCycles(Graph<V> graph) {
6467
*
6568
* @return The list of all simple cycles. Possibly empty but never <code>null</code>.
6669
*/
67-
public List<List<V>> findAndRemoveSimpleCycles() {
68-
List<List<V>> result = new ArrayList<>();
70+
public ObjectList<ObjectList<V>> findAndRemoveSimpleCycles() {
71+
ObjectList<ObjectList<V>> result = new ObjectArrayList<>();
6972
findSimpleCycles(result::add, (v, s) -> ((MutableGraph<V>) graph).removeEdge(v, s)); // Paper
7073
return result;
7174
}
@@ -75,7 +78,7 @@ public List<List<V>> findAndRemoveSimpleCycles() {
7578
*
7679
* @param consumer Consumer that will be called with each cycle found.
7780
*/
78-
public void findSimpleCycles(Consumer<List<V>> consumer,
81+
public void findSimpleCycles(Consumer<ObjectList<V>> consumer,
7982
BiConsumer<V, V> vertexSuccessorConsumer) // Paper
8083
{
8184
if (graph == null) {
@@ -87,10 +90,10 @@ public void findSimpleCycles(Consumer<List<V>> consumer,
8790
int startIndex = 0;
8891
int size = graph.nodes().size();
8992
while (startIndex < size) {
90-
Pair<Graph<V>, Integer> minSCCGResult = findMinSCSG(startIndex);
93+
Object2IntMap.Entry<Graph<V>> minSCCGResult = findMinSCSG(startIndex);
9194
if (minSCCGResult != null) {
92-
startIndex = minSCCGResult.getSecond();
93-
Graph<V> scg = minSCCGResult.getFirst();
95+
startIndex = minSCCGResult.getIntValue();
96+
Graph<V> scg = minSCCGResult.getKey();
9497
V startV = toV(startIndex);
9598
for (V v : scg.successors(startV)) {
9699
blocked.remove(v);
@@ -106,7 +109,7 @@ public void findSimpleCycles(Consumer<List<V>> consumer,
106109
clearState();
107110
}
108111

109-
private Pair<Graph<V>, Integer> findMinSCSG(int startIndex) {
112+
private Object2IntMap.Entry<Graph<V>> findMinSCSG(int startIndex) {
110113
/*
111114
* Per Johnson : "adjacency structure of strong component $K$ with least vertex in subgraph
112115
* of $G$ induced by $(s, s + 1, n)$". Or in contemporary terms: the strongly connected
@@ -115,12 +118,12 @@ private Pair<Graph<V>, Integer> findMinSCSG(int startIndex) {
115118
*/
116119
initMinSCGState();
117120

118-
List<Set<V>> foundSCCs = findSCCS(startIndex);
121+
ObjectList<ObjectSet<V>> foundSCCs = findSCCS(startIndex);
119122

120123
// find the SCC with the minimum index
121124
int minIndexFound = Integer.MAX_VALUE;
122-
Set<V> minSCC = null;
123-
for (Set<V> scc : foundSCCs) {
125+
ObjectSet<V> minSCC = null;
126+
for (ObjectSet<V> scc : foundSCCs) {
124127
for (V v : scc) {
125128
int t = toI(v);
126129
if (t < minIndexFound) {
@@ -144,12 +147,13 @@ private Pair<Graph<V>, Integer> findMinSCSG(int startIndex) {
144147
}
145148
}
146149

147-
Pair<Graph<V>, Integer> result = Pair.of(dependencyGraph, minIndexFound);
150+
Object2IntMap.Entry<Graph<V>> result = new AbstractObject2IntMap.BasicEntry(dependencyGraph,
151+
minIndexFound);
148152
clearMinSCCState();
149153
return result;
150154
}
151155

152-
private List<Set<V>> findSCCS(int startIndex) {
156+
private ObjectList<ObjectSet<V>> findSCCS(int startIndex) {
153157
// Find SCCs in the subgraph induced
154158
// by vertices startIndex and beyond.
155159
// A call to StrongConnectivityAlgorithm
@@ -168,7 +172,7 @@ private List<Set<V>> findSCCS(int startIndex) {
168172
getSCCs(startIndex, vI);
169173
}
170174
}
171-
List<Set<V>> result = foundSCCs;
175+
ObjectList<ObjectSet<V>> result = foundSCCs;
172176
foundSCCs = null;
173177
return result;
174178
}
@@ -195,7 +199,7 @@ private void getSCCs(int startIndex, int vertexIndex) {
195199
}
196200
}
197201
if (vLowlink.get(vertex).equals(vIndex.get(vertex))) {
198-
Set<V> result = new HashSet<>();
202+
ObjectSet<V> result = new ObjectOpenHashSet<>();
199203
V temp;
200204
do {
201205
temp = path.pop();
@@ -225,7 +229,7 @@ private boolean findCyclesInSCG(int startIndex, int vertexIndex, Graph<V> scg) {
225229
for (V successor : scg.successors(vertex)) {
226230
int successorIndex = toI(successor);
227231
if (successorIndex == startIndex) {
228-
List<V> cycle = new ArrayList<>(stack.size());
232+
ObjectList<V> cycle = new ObjectArrayList<>(stack.size());
229233
stack.descendingIterator().forEachRemaining(cycle::add);
230234
cycleConsumer.accept(cycle);
231235
cycleVertexSuccessorConsumer.accept(vertex, successor); // Paper
@@ -239,7 +243,7 @@ private boolean findCyclesInSCG(int startIndex, int vertexIndex, Graph<V> scg) {
239243
unblock(vertex);
240244
} else {
241245
for (V w : scg.successors(vertex)) {
242-
Set<V> bSet = getBSet(w);
246+
ObjectSet<V> bSet = getBSet(w);
243247
bSet.add(vertex);
244248
}
245249
}
@@ -249,7 +253,7 @@ private boolean findCyclesInSCG(int startIndex, int vertexIndex, Graph<V> scg) {
249253

250254
private void unblock(V vertex) {
251255
blocked.remove(vertex);
252-
Set<V> bSet = getBSet(vertex);
256+
ObjectSet<V> bSet = getBSet(vertex);
253257
while (bSet.size() > 0) {
254258
V w = bSet.iterator().next();
255259
bSet.remove(w);
@@ -260,12 +264,12 @@ private void unblock(V vertex) {
260264
}
261265

262266
@SuppressWarnings("unchecked")
263-
private void initState(Consumer<List<V>> consumer) {
267+
private void initState(Consumer<ObjectList<V>> consumer) {
264268
cycleConsumer = consumer;
265269
iToV = (V[]) graph.nodes().toArray();
266-
vToI = new HashMap<>();
267-
blocked = new HashSet<>();
268-
bSets = new HashMap<>();
270+
vToI = new Object2IntOpenHashMap<>();
271+
blocked = new ObjectOpenHashSet<>();
272+
bSets = new Object2ObjectOpenHashMap<>();
269273
stack = new ArrayDeque<>();
270274

271275
for (int i = 0; i < iToV.length; i++) {
@@ -284,11 +288,11 @@ private void clearState() {
284288

285289
private void initMinSCGState() {
286290
index = 0;
287-
foundSCCs = new ArrayList<>();
288-
vIndex = new HashMap<>();
289-
vLowlink = new HashMap<>();
291+
foundSCCs = new ObjectArrayList<>();
292+
vIndex = new Object2IntOpenHashMap<>();
293+
vLowlink = new Object2IntOpenHashMap<>();
290294
path = new ArrayDeque<>();
291-
pathSet = new HashSet<>();
295+
pathSet = new ObjectOpenHashSet<>();
292296
}
293297

294298
private void clearMinSCCState() {
@@ -300,17 +304,17 @@ private void clearMinSCCState() {
300304
pathSet = null;
301305
}
302306

303-
private Integer toI(V vertex) {
304-
return vToI.get(vertex);
307+
private int toI(V vertex) {
308+
return vToI.getInt(vertex);
305309
}
306310

307-
private V toV(Integer i) {
311+
private V toV(int i) {
308312
return iToV[i];
309313
}
310314

311-
private Set<V> getBSet(V v) {
315+
private ObjectSet<V> getBSet(V v) {
312316
// B sets typically not all needed,
313317
// so instantiate lazily.
314-
return bSets.computeIfAbsent(v, k -> new HashSet<>());
318+
return bSets.computeIfAbsent(v, k -> new ObjectOpenHashSet<>());
315319
}
316320
}

0 commit comments

Comments
 (0)