Skip to content

Commit 45e6746

Browse files
committed
perf: more efficient LoopedTracker
1 parent a49421b commit 45e6746

1 file changed

Lines changed: 11 additions & 19 deletions

File tree

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

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

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,33 @@
11
package ai.timefold.solver.core.impl.domain.variable.declarative;
22

3-
import java.util.BitSet;
3+
import ai.timefold.solver.core.impl.util.DynamicIntArray;
44

55
import org.jspecify.annotations.NullMarked;
66

77
@NullMarked
88
public final class LoopedTracker {
99

10-
// Simple LoopedStatus[] array would have occupied too much memory with large node counts.
11-
// Furthermore, allocating and/or clearing these large arrays is expensive as well.
12-
private final BitSet present;
13-
private final BitSet looped;
10+
// For some reason, the array was getting re-created on every values() call.
11+
// So, we cache a single instance.
12+
private static final LoopedStatus[] VALUES = LoopedStatus.values();
13+
14+
private final DynamicIntArray looped;
1415

1516
public LoopedTracker(int nodeCount) {
16-
this.present = new BitSet(nodeCount);
17-
this.looped = new BitSet(nodeCount);
17+
this.looped = new DynamicIntArray(nodeCount);
1818
}
1919

2020
public void mark(int node, LoopedStatus status) {
21-
if (status == LoopedStatus.UNKNOWN) {
22-
present.clear(node);
23-
looped.clear(node);
24-
} else {
25-
present.set(node);
26-
looped.set(node, status == LoopedStatus.LOOPED);
27-
}
21+
looped.set(node, status.ordinal());
2822
}
2923

3024
public LoopedStatus status(int node) {
31-
if (present.isEmpty() || !present.get(node)) {
32-
return LoopedStatus.UNKNOWN;
33-
}
34-
return looped.get(node) ? LoopedStatus.LOOPED : LoopedStatus.NOT_LOOPED;
25+
// When in the unallocated part of the dynamic array, the value returned is zero.
26+
// Therefore it is imperative that LoopedStatus.UNKNOWN be the first element in the enum.
27+
return VALUES[looped.get(node)];
3528
}
3629

3730
public void clear() {
38-
present.clear();
3931
looped.clear();
4032
}
4133

0 commit comments

Comments
 (0)