From 94af639f21e83976fe48fc9960b3dc6f6f39f7a2 Mon Sep 17 00:00:00 2001 From: larsbeck Date: Thu, 24 Sep 2026 15:12:40 +0200 Subject: [PATCH 1/2] perf: cache the location index when the distance matrix is filled first LongArrayDistanceMatrix#getShortId cached the index in the Location only when it created a new id. The map service converters fill the matrix with their own Location instances before the model's locations are attached, so for the model's locations the id already existed and was never cached. Every travel time and distance lookup then went through the HashMap, which was about 6% of the CPU time in the construction heuristic of the pickup-delivery-routing model. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01SNtk3rYnr8ivCJKYeevXdZ --- .../maps/impl/LongArrayDistanceMatrix.java | 4 +++- .../impl/LongArrayDistanceMatrixTest.java | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/service/maps/api/src/main/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrix.java b/service/maps/api/src/main/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrix.java index d45958958e1..6bf2f2ded55 100644 --- a/service/maps/api/src/main/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrix.java +++ b/service/maps/api/src/main/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrix.java @@ -161,9 +161,11 @@ private short getShortId(Location location) { if (shortId == null) { shortId = idGenerator; locationToId.put(location, shortId); - location.setIndex(this, shortId); idGenerator++; } + // The matrix is usually filled before the location is attached to it, + // so the id may already exist while the location has not cached it yet. + location.setIndex(this, shortId); return shortId; } diff --git a/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java b/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java index b843f993050..c5cb02d9149 100644 --- a/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java +++ b/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java @@ -64,6 +64,26 @@ void cacheShortIndexInLocation() { assertThat((int) b.getIndex(distanceMatrix)).isEqualTo(1); } + @Test + void cacheShortIndexInLocationAttachedAfterMatrixIsFilled() { + LongArrayDistanceMatrix distanceMatrix = new LongArrayDistanceMatrix(2); + // The converters fill the matrix with their own location instances before the model's locations are attached. + Location responseA = new Location(0.0, 1.0); + Location responseB = new Location(1.0, 0.0); + distanceMatrix.put(responseA, responseB, 7L); + distanceMatrix.put(responseB, responseA, 9L); + + Location a = new Location(0.0, 1.0); + Location b = new Location(1.0, 0.0); + a.setDistanceMatrix(distanceMatrix); + b.setDistanceMatrix(distanceMatrix); + + assertThat((int) a.getIndex(distanceMatrix)).isEqualTo(0); + assertThat((int) b.getIndex(distanceMatrix)).isEqualTo(1); + assertThat(distanceMatrix.get(a, b)).isEqualTo(7L); + assertThat(distanceMatrix.get(b, a)).isEqualTo(9L); + } + @Test void putNoResize() { LongArrayDistanceMatrix distanceMatrix = new LongArrayDistanceMatrix(4); From a576fada37a33700cf9fea556f09b247c7ebc131 Mon Sep 17 00:00:00 2001 From: larsbeck Date: Thu, 24 Sep 2026 15:42:36 +0200 Subject: [PATCH 2/2] test: use isZero for cached location index assertion --- .../solver/service/maps/impl/LongArrayDistanceMatrixTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java b/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java index c5cb02d9149..b2ea796fa1a 100644 --- a/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java +++ b/service/maps/api/src/test/java/ai/timefold/solver/service/maps/impl/LongArrayDistanceMatrixTest.java @@ -78,7 +78,7 @@ void cacheShortIndexInLocationAttachedAfterMatrixIsFilled() { a.setDistanceMatrix(distanceMatrix); b.setDistanceMatrix(distanceMatrix); - assertThat((int) a.getIndex(distanceMatrix)).isEqualTo(0); + assertThat((int) a.getIndex(distanceMatrix)).isZero(); assertThat((int) b.getIndex(distanceMatrix)).isEqualTo(1); assertThat(distanceMatrix.get(a, b)).isEqualTo(7L); assertThat(distanceMatrix.get(b, a)).isEqualTo(9L);