Skip to content

Commit 41a3e99

Browse files
authored
Merge pull request opentripplanner#7440 from entur/fix/mc-pareto-comparator-regression
Fix MC Raptor Pareto comparator performance regression
2 parents e1cf7d9 + 8d492ed commit 41a3e99

50 files changed

Lines changed: 574 additions & 355 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public RaptorRequest<T> mapRaptorRequest() {
180180

181181
if (raptorDebugging.isEnabled()) {
182182
var debug = builder.debug();
183-
var debugLogger = new SystemErrDebugLogger(true, false);
183+
var debugLogger = new SystemErrDebugLogger(null, true);
184184

185185
debug
186186
.withStops(mapDebugStopList(raptorDebugging.stops()))

raptor/src/main/java/org/opentripplanner/raptor/api/view/ArrivalView.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,6 @@ default RaptorTransfer transfer() {
140140

141141
/* Egress */
142142

143-
/** @return true if destination arrival, otherwise false. */
144-
default boolean arrivedAtDestination() {
145-
return false;
146-
}
147-
148143
default EgressPathView egressPath() {
149144
throw new UnsupportedOperationException();
150145
}

raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/RangeRaptor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
* (generating randomized schedules)
4141
* </ul>
4242
* <p>
43-
* This class originated as a rewrite of Conveyals RAPTOR code: https://github.com/conveyal/r5.
43+
* This class originated as a rewrite of
44+
* <a href="https://github.com/conveyal/r5">Conveyals RAPTOR code</a>.
4445
*
4546
* @param <T> The TripSchedule type defined by the user of the raptor API.
4647
*/
@@ -84,7 +85,7 @@ public RangeRaptor(
8485
this.calculator = requireNonNull(calculator);
8586
this.timers = requireNonNull(timers);
8687
this.accessPaths = requireNonNull(accessPaths);
87-
this.minNumberOfRounds = accessPaths.calculateMaxNumberOfRides();
88+
this.minNumberOfRounds = accessPaths.maxNumberOfRides();
8889
this.roundTracker = requireNonNull(roundTracker);
8990
this.lifeCycle = requireNonNull(lifeCyclePublisher);
9091
this.timeoutHook = requireNonNull(timeoutHook);

raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/SystemErrDebugLogger.java

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.opentripplanner.raptor.api.view.ArrivalView;
2222
import org.opentripplanner.raptor.api.view.PatternRideView;
2323
import org.opentripplanner.raptor.rangeraptor.transit.TripTimesSearch;
24-
import org.opentripplanner.utils.lang.IntUtils;
24+
import org.opentripplanner.raptor.spi.RaptorStopNameResolver;
2525
import org.opentripplanner.utils.lang.OtpNumberFormat;
2626
import org.opentripplanner.utils.lang.StringUtils;
2727
import org.opentripplanner.utils.text.Table;
@@ -38,32 +38,29 @@ public class SystemErrDebugLogger implements DebugLogger {
3838
private static final int NOT_SET = Integer.MIN_VALUE;
3939

4040
private final boolean enableDebugLogging;
41-
private final boolean eventLoggingDryRun;
41+
private final RaptorStopNameResolver stopNameResolver;
4242
private final NumberFormat numFormat = NumberFormat.getInstance(Locale.FRANCE);
4343
private final Table arrivalTable = Table.of()
4444
.withAlights(Center, Center, Right, Right, Right, Right, Left, Left)
4545
.withHeaders("ARRIVAL", "LEG", "RND", "STOP", "ARRIVE", "C₁", "TRIP", "DETAILS")
46-
.withMinWidths(9, 7, 3, 5, 8, 9, 24, 0)
46+
.withMinWidths(9, 7, 3, 5, 11, 9, 24, 0)
4747
.build();
4848
private final Table pathTable = Table.of()
4949
.withAlights(Center, Center, Right, Right, Right, Right, Right, Right, Left)
5050
.withHeaders(">>> PATH", "TR", "FROM", "TO", "START", "END", "DURATION", "C₁", "DETAILS")
51-
.withMinWidths(9, 2, 5, 5, 8, 8, 8, 9, 0)
51+
.withMinWidths(9, 2, 5, 5, 11, 11, 8, 9, 0)
5252
.build();
5353
private boolean forwardSearch = true;
5454
private int lastIterationTime = NOT_SET;
5555
private int lastRound = NOT_SET;
5656
private boolean printPathHeader = true;
5757

5858
/**
59-
* @param enableDebugLogging Log debug information on {@link DebugTopic}s.
60-
* @param eventLoggingDryRun DryRun will do the Raptor event logging, except printing the result.
61-
* This is used to test the logging framework without logging. To turn
62-
* off logging completely used the {@link #noop()} logger.
59+
* @param enableDebugLogging Enable debug information on {@link DebugTopic}s.
6360
*/
64-
public SystemErrDebugLogger(boolean enableDebugLogging, boolean eventLoggingDryRun) {
61+
public SystemErrDebugLogger(RaptorStopNameResolver stopNameResolver, boolean enableDebugLogging) {
62+
this.stopNameResolver = stopNameResolver;
6563
this.enableDebugLogging = enableDebugLogging;
66-
this.eventLoggingDryRun = eventLoggingDryRun;
6764
}
6865

6966
/**
@@ -138,7 +135,7 @@ public void setSearchDirection(boolean forward) {
138135

139136
@Override
140137
public void debug(DebugTopic topic, String message) {
141-
if (enableDebugLogging) {
138+
if (isEnabled()) {
142139
// We log to info - since debugging is controlled by the application
143140
if (message.contains("\n")) {
144141
System.err.printf("%s\n%s", topic, message);
@@ -176,7 +173,7 @@ private static boolean isNotBlank(String text) {
176173
}
177174

178175
private void printIterationHeader(int iterationTime) {
179-
if (iterationTime == lastIterationTime) {
176+
if (!isEnabled() || iterationTime == lastIterationTime) {
180177
return;
181178
}
182179
lastIterationTime = iterationTime;
@@ -193,7 +190,7 @@ private void print(ArrivalView<?> a, String action, String optReason) {
193190
action,
194191
legType(a),
195192
a.round(),
196-
IntUtils.intToString(a.stop(), NOT_SET),
193+
stop(a.stop()),
197194
timeToStrLong(a.arrivalTime()),
198195
numFormat.format(a.c1()),
199196
pattern,
@@ -208,7 +205,7 @@ private void print(PatternRideView<?, ?> p, String action) {
208205
action,
209206
"OnRide",
210207
p.prevArrival().round() + 1,
211-
p.boardStopIndex(),
208+
stop(p.boardStopIndex()),
212209
timeToStrLong(p.boardTime()),
213210
numFormat.format(p.relativeC1()),
214211
p.trip().pattern().debugInfo(),
@@ -218,7 +215,7 @@ private void print(PatternRideView<?, ?> p, String action) {
218215
}
219216

220217
private String path(ArrivalView<?> a) {
221-
return path(a, new PathStringBuilder(null)).summary(a.c1(), a.c2()).toString();
218+
return path(a, new PathStringBuilder(stopNameResolver)).summary(a.c1(), a.c2()).toString();
222219
}
223220

224221
private PathStringBuilder path(ArrivalView<?> a, PathStringBuilder buf) {
@@ -250,7 +247,7 @@ private PathStringBuilder path(ArrivalView<?> a, PathStringBuilder buf) {
250247
}
251248

252249
private void printRoundHeader(int round) {
253-
if (round == lastRound) {
250+
if (!isEnabled() || round == lastRound) {
254251
return;
255252
}
256253
lastRound = round;
@@ -269,14 +266,19 @@ private String legType(ArrivalView<?> a) {
269266
}
270267

271268
private void println() {
272-
if (!eventLoggingDryRun) {
273-
System.err.println();
274-
}
269+
System.err.println();
275270
}
276271

277272
private void println(String text) {
278-
if (!eventLoggingDryRun) {
279-
System.err.println(text);
273+
System.err.println(text);
274+
}
275+
276+
String stop(int stopIndex) {
277+
if (stopIndex == NOT_SET) {
278+
return "NA";
280279
}
280+
return stopNameResolver == null
281+
? Integer.toString(stopIndex)
282+
: stopNameResolver.apply(stopIndex);
281283
}
282284
}

raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/context/SearchContextViaSegments.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* following segment. This is implemented using Pareto set event listeners.
2828
* </p>
2929
*/
30+
@SuppressWarnings("ClassCanBeRecord")
3031
public class SearchContextViaSegments<T extends RaptorTripSchedule> {
3132

3233
private final SearchContext<T> parent;
@@ -76,7 +77,6 @@ public ViaConnections viaConnections() {
7677
/**
7778
* Returns the set of egress paths that lead directly to the destination in this segment.
7879
*/
79-
@Nullable
8080
public EgressPaths egressPaths() {
8181
return egressPaths;
8282
}

raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalParetoSetComparatorFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public interface ArrivalParetoSetComparatorFactory<T extends McStopArrival<?>> {
1111
* {@code paretoRound} and {@code c1} to compare arrivals. It does NOT include
1212
* {@code arrivedOnBoard}. Normally arriving on-board should give the arrival an advantage
1313
* - you can continue on foot, walking to the next stop. But, we only do this if it happens
14-
* in the same Raptor iteration and round - if it does it is taken care of by the order
14+
* in the same Raptor iteration and round - if it does, it is taken care of by the order
1515
* which the algorithm works - not by this comparator.
1616
*/
1717
ParetoComparator<T> compareArrivalTimeRoundAndCost();
@@ -40,7 +40,7 @@ static <T extends McStopArrival<?>> ArrivalParetoSetComparatorFactory<T> factory
4040
private static <T extends McStopArrival<?>> ArrivalParetoSetComparatorFactory<
4141
T
4242
> createFactoryC1() {
43-
return new ArrivalParetoSetComparatorFactory<T>() {
43+
return new ArrivalParetoSetComparatorFactory<>() {
4444
@Override
4545
public ParetoComparator<T> compareArrivalTimeRoundAndCost() {
4646
return McStopArrival::compareBase;
@@ -57,7 +57,7 @@ public ParetoComparator<T> compareArrivalTimeRoundCostAndOnBoardArrival() {
5757
private static <T extends McStopArrival<?>> ArrivalParetoSetComparatorFactory<
5858
T
5959
> createFactoryC1AndC2(DominanceFunction c2DominanceFunction) {
60-
return new ArrivalParetoSetComparatorFactory<T>() {
60+
return new ArrivalParetoSetComparatorFactory<>() {
6161
@Override
6262
public ParetoComparator<T> compareArrivalTimeRoundAndCost() {
6363
return (l, r) ->

raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/ArrivalsEventListenerMapper.java renamed to raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McArrivalsEventListenerFactory.java

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
import gnu.trove.map.TIntObjectMap;
44
import gnu.trove.map.hash.TIntObjectHashMap;
5+
import gnu.trove.set.TIntSet;
6+
import gnu.trove.set.hash.TIntHashSet;
57
import java.util.List;
6-
import javax.annotation.Nullable;
78
import org.opentripplanner.raptor.api.model.RaptorAccessEgress;
89
import org.opentripplanner.raptor.api.view.ArrivalView;
910
import org.opentripplanner.raptor.rangeraptor.debug.DebugHandlerFactory;
@@ -15,77 +16,82 @@
1516

1617
/// This class creates {@link ParetoSetEventListener}s to glue a stop-arrival to (in order):
1718
/// - debug stop arrival events
18-
/// - updating next connection event
19+
/// - updating next via connection event
1920
/// - updating destination path collection using egress paths.
2021
///
2122
/// If the debugger is not done first, then the logging will be strange -> events
2223
/// arriving at the destination, before arriving at the egress stop.
23-
public class ArrivalsEventListenerMapper<T extends RaptorTripSchedule> {
24+
public class McArrivalsEventListenerFactory<T extends RaptorTripSchedule> {
2425

2526
private final DebugHandlerFactory<T> debugHandlerFactory;
2627
private final TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> nextConnectionListener;
27-
private final @Nullable EgressPaths egressPaths;
28-
private final DestinationArrivalPaths<T> paths;
28+
private final EgressPaths egressPaths;
29+
private final DestinationArrivalPaths<T> destinationPaths;
30+
private boolean processed = false;
2931

30-
private final TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> result =
32+
private final TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> arrivalListeners =
3133
new TIntObjectHashMap<>();
3234

33-
private ArrivalsEventListenerMapper(
35+
public McArrivalsEventListenerFactory(
3436
DebugHandlerFactory<T> debugHandlerFactory,
3537
TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> nextConnectionListener,
36-
@Nullable EgressPaths egressPaths,
37-
DestinationArrivalPaths<T> paths
38+
EgressPaths egressPaths,
39+
DestinationArrivalPaths<T> destinationPaths
3840
) {
3941
this.debugHandlerFactory = debugHandlerFactory;
4042
this.nextConnectionListener = nextConnectionListener;
4143
this.egressPaths = egressPaths;
42-
this.paths = paths;
44+
this.destinationPaths = destinationPaths;
4345
}
4446

45-
public static <T extends RaptorTripSchedule> TIntObjectMap<
46-
ParetoSetEventListener<ArrivalView<T>>
47-
> map(
48-
DebugHandlerFactory<T> debugHandlerFactory,
49-
TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> connectionListeners,
50-
EgressPaths egressPaths,
51-
DestinationArrivalPaths<T> destinationPaths
52-
) {
53-
return new ArrivalsEventListenerMapper<>(
54-
debugHandlerFactory,
55-
connectionListeners,
56-
egressPaths,
57-
destinationPaths
58-
).map();
47+
public TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> arrivalListeners() {
48+
return assertProcessed(arrivalListeners);
5949
}
6050

6151
/**
6252
* This method creates a ParetoSet for the given egress stop. When arrivals are added to the stop,
6353
* the "glue" make sure new destination arrivals are added to the destination arrivals.
6454
*/
65-
private TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> map() {
66-
for (int stop : nextConnectionListener.keys()) {
67-
append(stop, nextConnectionListener.get(stop));
68-
}
55+
public McArrivalsEventListenerFactory<T> create() {
6956
var egressByStop = egressPaths.byStop();
7057

71-
for (int stop : egressByStop.keys()) {
72-
List<RaptorAccessEgress> egressList = egressByStop.get(stop);
73-
append(stop, new CalculateTransferToDestination<>(egressList, paths));
58+
TIntSet stops = new TIntHashSet();
59+
stops.addAll(nextConnectionListener.keySet());
60+
stops.addAll(egressByStop.keySet());
61+
62+
var it = stops.iterator();
63+
while (it.hasNext()) {
64+
createListenerForStop(it.next(), egressByStop);
7465
}
75-
return result;
66+
67+
this.processed = true;
68+
return this;
69+
}
70+
71+
private void createListenerForStop(
72+
int stop,
73+
TIntObjectMap<List<RaptorAccessEgress>> egressByStop
74+
) {
75+
var l = ParetoSetEventListenerComposite.of(
76+
debugHandlerFactory.paretoSetStopArrivalListener(stop),
77+
nextConnectionListener.get(stop),
78+
egressStopListeners(egressByStop.get(stop))
79+
);
80+
this.arrivalListeners.put(stop, l);
7681
}
7782

78-
private void append(int stop, ParetoSetEventListener<ArrivalView<T>> listener) {
79-
var e = result.get(stop);
80-
var l = listener;
81-
if (e != null) {
82-
// existing listeners should be inserted before new one
83-
l = ParetoSetEventListenerComposite.of(e, listener);
84-
} else if (debugHandlerFactory.isDebugStopArrival(stop)) {
85-
// Debug listerner is inserted first
86-
var debug = debugHandlerFactory.paretoSetStopArrivalListener(stop);
87-
l = ParetoSetEventListenerComposite.of(debug, l);
83+
private ParetoSetEventListener<ArrivalView<T>> egressStopListeners(
84+
List<RaptorAccessEgress> egressList
85+
) {
86+
return egressList == null
87+
? null
88+
: new CalculateTransferToDestination<>(egressList, destinationPaths);
89+
}
90+
91+
private <R> R assertProcessed(R result) {
92+
if (!processed) {
93+
throw new IllegalStateException("Call the create() method before accessing the result!");
8894
}
89-
result.put(stop, l);
95+
return result;
9096
}
9197
}

raptor/src/main/java/org/opentripplanner/raptor/rangeraptor/multicriteria/arrivals/McStopArrivals.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.opentripplanner.raptor.api.view.PathLegType.TRANSIT;
44

55
import gnu.trove.map.TIntObjectMap;
6+
import gnu.trove.set.TIntSet;
67
import java.util.BitSet;
78
import java.util.Collections;
89
import java.util.function.Function;
@@ -32,28 +33,36 @@ public final class McStopArrivals<T extends RaptorTripSchedule> {
3233
private final DebugStopArrivalsStatistics debugStats;
3334
private final ParetoComparator<McStopArrival<T>> comparator;
3435

35-
/**
36-
* Set the time at a transit index if it is optimal. This sets both the best time and the
37-
* transfer time.
38-
*
39-
* @param nextLegArrivals When chaining two Raptor searches together, the next-leg is the next
40-
* search we are copying state into.
41-
*/
4236
public McStopArrivals(
4337
int nStops,
38+
TIntSet onBoardArrivalStops,
4439
TIntObjectMap<ParetoSetEventListener<ArrivalView<T>>> arrivalListeners,
4540
ArrivalParetoSetComparatorFactory<McStopArrival<T>> comparatorFactory,
4641
DebugHandlerFactory<T> debugHandlerFactory
4742
) {
4843
//noinspection unchecked
4944
this.arrivals = (ParetoSet<McStopArrival<T>>[]) new ParetoSet[nStops];
5045
this.touchedStops = new BitSet(nStops);
51-
this.comparator = comparatorFactory.compareArrivalTimeRoundCostAndOnBoardArrival();
46+
this.comparator = comparatorFactory.compareArrivalTimeRoundAndCost();
5247
this.debugHandlerFactory = debugHandlerFactory;
5348
this.debugStats = new DebugStopArrivalsStatistics(debugHandlerFactory.debugLogger());
5449

50+
// Comparator for stops that have arrival listeners with a on-board trip-to-trip via
51+
// connection.
52+
var onBoardComparator = comparatorFactory.compareArrivalTimeRoundCostAndOnBoardArrival();
5553
for (int stop : arrivalListeners.keys()) {
56-
this.arrivals[stop] = ParetoSet.of(comparator, arrivalListeners.get(stop));
54+
var comp = onBoardArrivalStops.contains(stop) ? onBoardComparator : comparator;
55+
this.arrivals[stop] = ParetoSet.of(comp, arrivalListeners.get(stop));
56+
}
57+
58+
for (var it = onBoardArrivalStops.iterator(); it.hasNext(); ) {
59+
int stop = it.next();
60+
if (this.arrivals[stop] == null) {
61+
this.arrivals[stop] = ParetoSet.of(
62+
onBoardComparator,
63+
debugHandlerFactory.paretoSetStopArrivalListener(stop)
64+
);
65+
}
5766
}
5867
}
5968

0 commit comments

Comments
 (0)