Skip to content

Commit a331447

Browse files
committed
perf: avoid calling Object.hashCode()
Replacing IdentityHashMap with a Map.of(...) when the keys in that map are of several different classes (such as multiple entity classes) will result in System.identityHashCode(...) being replaced by Object.hashCode() - a megamorphic call that the JVM can't optimize.
1 parent 055b214 commit a331447

2 files changed

Lines changed: 7 additions & 24 deletions

File tree

core/src/main/java/ai/timefold/solver/core/impl/domain/variable/declarative/AbstractVariableReferenceGraph.java

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ public abstract sealed class AbstractVariableReferenceGraph<Solution_, ChangeTra
4343
nodeList = List.copyOf(outerGraph.nodeList);
4444
var instanceCount = nodeList.size();
4545
// Often the maps are a singleton; we improve performance by actually making it so.
46-
variableReferenceToContainingNodeMap = mapOfMapsDeepCopyOf(outerGraph.variableReferenceToContainingNodeMap);
47-
variableReferenceToBeforeProcessor = mapOfListsDeepCopyOf(outerGraph.variableReferenceToBeforeProcessor);
48-
variableReferenceToAfterProcessor = mapOfListsDeepCopyOf(outerGraph.variableReferenceToAfterProcessor);
46+
variableReferenceToContainingNodeMap = Map.copyOf(outerGraph.variableReferenceToContainingNodeMap);
47+
variableReferenceToBeforeProcessor = Map.copyOf(outerGraph.variableReferenceToBeforeProcessor);
48+
variableReferenceToAfterProcessor = Map.copyOf(outerGraph.variableReferenceToAfterProcessor);
4949
edgeCount = new DynamicLinearProbeNonNegativeIntCounter[instanceCount];
50-
for (int i = 0; i < instanceCount; i++) {
50+
for (var i = 0; i < instanceCount; i++) {
5151
edgeCount[i] = new DynamicLinearProbeNonNegativeIntCounter();
5252
}
5353
graph = graphCreator.apply(instanceCount);
@@ -171,7 +171,7 @@ private void processEntity(List<BiConsumer<AbstractVariableReferenceGraph<Soluti
171171
var processorCount = processorList.size();
172172
// Avoid creation of iterators on the hot path.
173173
// The short-lived instances were observed to cause considerable GC pressure.
174-
for (int i = 0; i < processorCount; i++) {
174+
for (var i = 0; i < processorCount; i++) {
175175
processorList.get(i).accept(this, entity);
176176
}
177177
}
@@ -207,22 +207,4 @@ public String toString() {
207207

208208
}
209209

210-
@SuppressWarnings("unchecked")
211-
static <K1, K2, V> Map<K1, Map<K2, V>> mapOfMapsDeepCopyOf(Map<K1, Map<K2, V>> map) {
212-
var entryArray = map.entrySet()
213-
.stream()
214-
.map(e -> Map.entry(e.getKey(), Map.copyOf(e.getValue())))
215-
.toArray(Map.Entry[]::new);
216-
return Map.ofEntries(entryArray);
217-
}
218-
219-
@SuppressWarnings("unchecked")
220-
static <K1, V> Map<K1, List<V>> mapOfListsDeepCopyOf(Map<K1, List<V>> map) {
221-
var entryArray = map.entrySet()
222-
.stream()
223-
.map(e -> Map.entry(e.getKey(), List.copyOf(e.getValue())))
224-
.toArray(Map.Entry[]::new);
225-
return Map.ofEntries(entryArray);
226-
}
227-
228210
}

core/src/main/java/ai/timefold/solver/core/impl/domain/variable/declarative/DefaultVariableReferenceGraph.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.BitSet;
55
import java.util.IdentityHashMap;
66
import java.util.List;
7+
import java.util.Map;
78
import java.util.function.IntFunction;
89

910
import org.jspecify.annotations.NonNull;
@@ -30,7 +31,7 @@ public DefaultVariableReferenceGraph(VariableReferenceGraphBuilder<Solution_> ou
3031
}
3132
}
3233
// Immutable optimized version of the map, now that it won't be updated anymore.
33-
var immutableEntityToVariableReferenceMap = mapOfListsDeepCopyOf(entityToVariableReferenceMap);
34+
var immutableEntityToVariableReferenceMap = Map.copyOf(entityToVariableReferenceMap);
3435
// This mutable structure is created once, and reused from there on.
3536
// Otherwise its internal collections were observed being re-created so often
3637
// that the allocation of arrays would become a major bottleneck.

0 commit comments

Comments
 (0)