Skip to content

Commit

Permalink
Raptor benchmark setup
Browse files Browse the repository at this point in the history
- Benchmark class to run random requests and time them.
- Store results to CSVs.
- Add RaptorTest with small scenario via builder.
- Rename mapper to converter.
  • Loading branch information
munterfi committed Apr 28, 2024
1 parent ea41ca7 commit e7933bb
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ build/
.DS_Store

# Benchmark test files
benchmark/input/*.zip
benchmark/
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ch.naviqore;
package ch.naviqore.raptor;

import ch.naviqore.gtfs.schedule.model.*;
import ch.naviqore.raptor.model.Raptor;
Expand All @@ -18,21 +18,22 @@
*/
@RequiredArgsConstructor
@Log4j2
public class GtfsToRaptorMapper {
public class GtfsToRaptorConverter {

private final Set<Stop> stops = new HashSet<>();
private final Set<Route> routes = new HashSet<>();
private final RaptorBuilder builder;

public Raptor map(GtfsSchedule schedule, LocalDate date) {
public Raptor convert(GtfsSchedule schedule, LocalDate date) {
List<Trip> activeTrips = schedule.getActiveTrips(date);
log.info("Mapping {} active trips from GTFS schedule to Raptor model", activeTrips.size());
log.info("Converting {} active trips from GTFS schedule to Raptor model", activeTrips.size());
for (Trip trip : activeTrips) {
Route route = trip.getRoute();
if (!routes.contains(route)) {
routes.add(route);
builder.addRoute(route.getId());
// TODO: Add test for consistency of route stops
// TODO: Add test for consistency of route stops. Since in GTFS are defined per trip, but Raptor
// builder expects them to be the same for all trips of a route.
for (StopTime stopTime : trip.getStopTimes()) {
if (!stops.contains(stopTime.stop())) {
stops.add(stopTime.stop());
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/ch/naviqore/raptor/model/Raptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public static RaptorBuilder builder() {
}

public void routeEarliestArrival(String sourceStop, String targetStop, int departureTime) {
log.info("Routing earliest arrival from {} to {} at {}", sourceStop, targetStop, departureTime);
log.debug("Routing earliest arrival from {} to {} at {}", sourceStop, targetStop, departureTime);
}
}
4 changes: 2 additions & 2 deletions src/main/java/ch/naviqore/raptor/model/RaptorBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public class RaptorBuilder {
private final Map<String, List<String>> routeStops = new HashMap<>();
private final Map<String, List<StopTime>> stopTimes = new HashMap<>();

private int routeSize = 0;
private int stopSize = 0;
private int routeSize = 0;
private int routeStopSize = 0;
private int stopTimeSize = 0;

Expand Down Expand Up @@ -64,7 +64,7 @@ public RaptorBuilder addRouteStop(String stopId, String routeId) {
}

public RaptorBuilder addStopTime(String stopId, String routeId, int arrival, int departure) {
log.info("Adding stop time: stopId={}, routeId={}, arrival={}, departure={}", stopId, routeId, arrival,
log.debug("Adding stop time: stopId={}, routeId={}, arrival={}, departure={}", stopId, routeId, arrival,
departure);
stopTimes.computeIfAbsent(routeId, k -> new ArrayList<>()).add(new StopTime(arrival, departure));
stopTimeSize++;
Expand Down
130 changes: 130 additions & 0 deletions src/test/java/ch/naviqore/Benchmark.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package ch.naviqore;

import ch.naviqore.BenchmarkData.Dataset;
import ch.naviqore.gtfs.schedule.GtfsScheduleReader;
import ch.naviqore.gtfs.schedule.model.GtfsSchedule;
import ch.naviqore.raptor.GtfsToRaptorConverter;
import ch.naviqore.raptor.model.Raptor;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
* Benchmark for Raptor routing algorithm.
* <p>
* Measures the time it takes to route a number of requests using Raptor algorithm on large GTFS datasets.
*
* @author munterfi
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
final class Benchmark {

private static final int N = 10000;
private static final Dataset DATASET = Dataset.SWITZERLAND;
private static final LocalDate DATE = LocalDate.of(2024, 4, 26);
private static final int MAX_SECONDS_IN_DAY = 86400;
private static final long MONITORING_INTERVAL_MS = 30000;
private static final int NS_TO_MS_CONVERSION_FACTOR = 1_000_000;

public static void main(String[] args) throws IOException, InterruptedException {
GtfsSchedule schedule = initializeSchedule();
Raptor raptor = initializeRaptor(schedule);
List<String> stopIds = new ArrayList<>(schedule.getStops().keySet());
RouteRequest[] requests = sampleRouteRequests(stopIds);
RoutingResult[] results = processRequests(raptor, requests);
writeResultsToCsv(results);

}

private static GtfsSchedule initializeSchedule() throws IOException, InterruptedException {
String path = BenchmarkData.get(DATASET);
GtfsSchedule schedule = new GtfsScheduleReader().read(path);
manageResources();
return schedule;
}

private static Raptor initializeRaptor(GtfsSchedule schedule) throws InterruptedException {
Raptor raptor = new GtfsToRaptorConverter(Raptor.builder()).convert(schedule, DATE);
manageResources();
return raptor;
}

private static void manageResources() throws InterruptedException {
System.gc();
Thread.sleep(MONITORING_INTERVAL_MS);
}

private static RouteRequest[] sampleRouteRequests(List<String> stopIds) {
Random random = new Random();
RouteRequest[] requests = new RouteRequest[Benchmark.N];
for (int i = 0; i < Benchmark.N; i++) {
int sourceIndex = random.nextInt(stopIds.size());
int destinationIndex = getRandomDestinationIndex(stopIds.size(), sourceIndex, random);
requests[i] = new RouteRequest(stopIds.get(sourceIndex), stopIds.get(destinationIndex),
random.nextInt(MAX_SECONDS_IN_DAY));
}
return requests;
}

private static int getRandomDestinationIndex(int size, int exclude, Random random) {
int index = random.nextInt(size - 1);
if (index >= exclude) index++;
return index;
}

private static RoutingResult[] processRequests(Raptor raptor, RouteRequest[] requests) {
RoutingResult[] responses = new RoutingResult[requests.length];
for (int i = 0; i < requests.length; i++) {
long startTime = System.nanoTime();
// TODO: RaptorResponse result =
raptor.routeEarliestArrival(requests[i].sourceStop(), requests[i].targetStop(),
requests[i].departureTime());
long endTime = System.nanoTime();
responses[i] = new RoutingResult(requests[i].sourceStop(), requests[i].targetStop(),
requests[i].departureTime(), 0, 0, 0, (endTime - startTime) / NS_TO_MS_CONVERSION_FACTOR);
}
return responses;
}

private static void writeResultsToCsv(RoutingResult[] results) throws IOException {
String header = "source_stop,target_stop,requested_departure_time,departure_time,arrival_time,transfers,processing_time_ms";
String folderPath = String.format("benchmark/output/%s", DATASET.name().toLowerCase());
String fileName = String.format("%s_raptor_results.csv",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd_HH_mm_ss")));
Path directoryPath = Paths.get(folderPath);
if (!Files.exists(directoryPath)) {
Files.createDirectories(directoryPath);
}
Path filePath = directoryPath.resolve(fileName);

try (PrintWriter writer = new PrintWriter(
Files.newBufferedWriter(filePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))) {
writer.println(header);

for (RoutingResult result : results) {
writer.printf("%s,%s,%d,%d,%d,%d,%d%n", result.sourceStop(), result.targetStop(),
result.requestedDepartureTime(), result.departureTime(), result.arrivalTime(),
result.transfers(), result.time());
}
}
}

record RouteRequest(String sourceStop, String targetStop, int departureTime) {
}

record RoutingResult(String sourceStop, String targetStop, int requestedDepartureTime, int departureTime,
int arrivalTime, int transfers, long time) {
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ch.naviqore.gtfs.schedule;
package ch.naviqore;

import lombok.AccessLevel;
import lombok.Getter;
Expand All @@ -21,7 +21,7 @@
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Log4j2
final class GtfsScheduleBenchmarkData {
final class BenchmarkData {
private static final Path DATA_DIRECTORY = Path.of("benchmark/input");
private static final HttpClient httpClient = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
Expand Down Expand Up @@ -66,7 +66,7 @@ public static String get(Dataset dataset) throws IOException, InterruptedExcepti
}

/**
* GTFS Schedule Datasets
* GTFS schedule datasets
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
Expand Down
27 changes: 0 additions & 27 deletions src/test/java/ch/naviqore/gtfs/schedule/GtfsScheduleBenchmark.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ch.naviqore;
package ch.naviqore.raptor;

import ch.naviqore.gtfs.schedule.GtfsScheduleReader;
import ch.naviqore.gtfs.schedule.GtfsScheduleTestData;
Expand All @@ -13,7 +13,7 @@
import java.nio.file.Path;
import java.time.LocalDate;

class GtfsToRaptorMapperIT {
class GtfsToRaptorConverterIT {

private static final LocalDate DATE = LocalDate.of(2009, 4, 26);
private GtfsSchedule schedule;
Expand All @@ -26,7 +26,7 @@ void setUp(@TempDir Path tempDir) throws IOException {

@Test
void shouldConvertGtfsScheduleToRaptor() {
GtfsToRaptorMapper mapper = new GtfsToRaptorMapper(Raptor.builder());
Raptor raptor = mapper.map(schedule, DATE);
GtfsToRaptorConverter mapper = new GtfsToRaptorConverter(Raptor.builder());
Raptor raptor = mapper.convert(schedule, DATE);
}
}
59 changes: 59 additions & 0 deletions src/test/java/ch/naviqore/raptor/model/RaptorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package ch.naviqore.raptor.model;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class RaptorTest {

private Raptor raptor;

@BeforeEach
void setUp() {
RaptorBuilder builder = Raptor.builder();

// add stops
builder.addStop("A")
.addStop("B")
.addStop("C")
.addStop("D")
.addStop("E")

// add routes
.addRoute("Route1")
.addRoute("Route2")

// add route stops for Route1
.addRouteStop("A", "Route1")
.addRouteStop("B", "Route1")
.addRouteStop("C", "Route1")

// add stop times for Route1
.addStopTime("A", "Route1", 300, 320)
.addStopTime("B", "Route1", 500, 520)
.addStopTime("C", "Route1", 700, 720)

// add route stops for Route2
.addRouteStop("D", "Route2")
.addRouteStop("B", "Route2")
.addRouteStop("E", "Route2")

// add stop times for Route2
.addStopTime("D", "Route2", 250, 270)
.addStopTime("B", "Route2", 550, 570)
.addStopTime("E", "Route2", 750, 770);

raptor = builder.build();
}

@Nested
class EarliestArrival {
@Test
void testRoutingBetweenIntersectingRoutes() {
raptor.routeEarliestArrival("D", "C", 250);

// TODO: assertThat...
}
}

}

0 comments on commit e7933bb

Please sign in to comment.