Skip to content

Commit 58b61ad

Browse files
committed
Experiment: bulk clear for ifExists
1 parent 058233b commit 58b61ad

File tree

5 files changed

+44
-9
lines changed

5 files changed

+44
-9
lines changed

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIfExistsNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ protected void decrementCounterRight(ExistsCounter<LeftTuple_> counter) {
113113

114114
IndexedSet<ExistsCounterHandle<LeftTuple_>> updateRightHandleSet(UniTuple<Right_> rightTuple) {
115115
IndexedSet<ExistsCounterHandle<LeftTuple_>> rightHandleSet = rightTuple.getStore(inputStoreIndexRightHandleSet);
116-
rightHandleSet.forEach(handle -> {
117-
handle.remove();
116+
rightHandleSet.clear(handle -> {
117+
handle.removeByRight();
118118
decrementCounterRight(handle.counter);
119119
});
120120
return rightHandleSet;

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIndexedJoinNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ public final void updateLeft(LeftTuple_ leftTuple) {
8282
// Prefer an update over retract-insert if possible
8383
innerUpdateLeft(leftTuple, consumer -> indexerRight.forEach(oldIndexKeys, consumer));
8484
} else {
85-
IndexedSet<OutTuple_> outTupleSetLeft = leftTuple.getStore(inputStoreIndexLeftOutTupleSet);
8685
indexerLeft.remove(oldIndexKeys, leftTuple);
86+
IndexedSet<OutTuple_> outTupleSetLeft = leftTuple.getStore(inputStoreIndexLeftOutTupleSet);
8787
outTupleSetLeft.forEach(this::retractOutTuple);
8888
// outTupleSetLeft is now empty, no need for leftTuple.setStore(...);
8989
indexAndPropagateLeft(leftTuple, newIndexKeys);

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/ExistsCounter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public final class ExistsCounter<Tuple_ extends AbstractTuple>
2121
}
2222

2323
public void clearWithoutCount() {
24-
leftHandleSet.forEach(ExistsCounterHandle::remove);
24+
leftHandleSet.clear(ExistsCounterHandle::removeByLeft);
2525
}
2626

2727
public void clearIncludingCount() {

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/ExistsCounterHandle.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ final class ExistsCounterHandle<LeftTuple_ extends AbstractTuple> {
2828
rightHandleSet.add(this);
2929
}
3030

31-
public void remove() {
32-
counter.leftHandleSet.remove(this);
31+
public void removeByLeft() {
3332
rightHandleSet.remove(this);
3433
}
3534

35+
public void removeByRight() {
36+
counter.leftHandleSet.remove(this);
37+
}
38+
3639
}

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IndexedSet.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,20 @@ public void remove(T element) {
104104

105105
private boolean clearIfPossible() {
106106
if (gapCount > 0 && lastElementPosition + 1 == gapCount) { // All positions are gaps. Clear the list entirely.
107-
elementList.clear();
108-
gapCount = 0;
109-
lastElementPosition = -1;
107+
forceClear();
110108
return true;
111109
}
112110
return false;
113111
}
114112

113+
private void forceClear() {
114+
if (elementList != null) {
115+
elementList.clear();
116+
}
117+
gapCount = 0;
118+
lastElementPosition = -1;
119+
}
120+
115121
public boolean isEmpty() {
116122
return size() == 0;
117123
}
@@ -300,4 +306,30 @@ private void forceCompaction() {
300306
}
301307
}
302308

309+
public void clear(Consumer<T> elementConsumer) {
310+
var nonGapCount = size();
311+
if (nonGapCount == 0) {
312+
forceClear();
313+
return;
314+
}
315+
var oldLastElementPosition = lastElementPosition;
316+
for (var i = 0; i <= oldLastElementPosition; i++) {
317+
var element = elementList.get(i);
318+
if (element == null) {
319+
continue;
320+
}
321+
elementConsumer.accept(element);
322+
if (lastElementPosition != oldLastElementPosition) {
323+
throw new IllegalStateException("Impossible state: the IndexedSet was modified while being cleared.");
324+
}
325+
elementPositionTracker.clearPosition(element);
326+
// We can stop early once all non-gap elements have been processed.
327+
nonGapCount--;
328+
if (nonGapCount == 0) {
329+
break;
330+
}
331+
}
332+
forceClear();
333+
}
334+
303335
}

0 commit comments

Comments
 (0)