diff --git a/.github/workflows/osrm-backend.yml b/.github/workflows/osrm-backend.yml index eabe3b813..98c6fbd0f 100644 --- a/.github/workflows/osrm-backend.yml +++ b/.github/workflows/osrm-backend.yml @@ -46,15 +46,32 @@ jobs: echo PUBLISH=$([[ "${GITHUB_REF:-}" == "refs/tags/v${PACKAGE_JSON_VERSION}" ]] && echo "On" || echo "Off") >> $GITHUB_ENV - run: npm install --ignore-scripts - run: npm link --ignore-scripts - - uses: microsoft/setup-msbuild@v2 - name: Build - run: | - .\scripts\ci\windows-build.bat - - name: Run node tests shell: bash run: | - ./lib/binding/osrm-datastore.exe test/data/ch/monaco.osrm - node test/nodejs/index.js + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_CONAN=ON -DENABLE_NODE_BINDINGS=ON .. + cmake --build . --config Release + + # TODO: MSVC goes out of memory when building our tests + # - name: Run tests + # shell: bash + # run: | + # cd build + # cmake --build . --config Release --target tests + # # TODO: run tests + # - name: Run node tests + # shell: bash + # run: | + # ./lib/binding/osrm-extract.exe -p profiles/car.lua test/data/monaco.osm.pbf + + # mkdir -p test/data/ch + # cp test/data/monaco.osrm* test/data/ch/ + # ./lib/binding/osrm-contract.exe test/data/ch/monaco.osrm + + # ./lib/binding/osrm-datastore.exe test/data/ch/monaco.osrm + # node test/nodejs/index.js - name: Build Node package shell: bash run: ./scripts/ci/node_package.sh @@ -203,6 +220,7 @@ jobs: CUCUMBER_TIMEOUT: 60000 ENABLE_CLANG_TIDY: ON + - name: clang-14-release continue-on-error: false node: 18 @@ -627,8 +645,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} GITHUB_REPOSITORY: ${{ github.repository }} + RUN_BIG_BENCHMARK: ${{ contains(github.event.pull_request.labels.*.name, 'Performance') }} steps: - name: Enable data.osm.pbf cache + if: ${{ ! env.RUN_BIG_BENCHMARK }} uses: actions/cache@v4 with: path: ~/data.osm.pbf @@ -660,10 +680,18 @@ jobs: sudo apt-get update -y && sudo apt-get install ccache - name: Prepare data run: | - if [ ! -f "~/data.osm.pbf" ]; then - wget http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf -O ~/data.osm.pbf + if [ "$RUN_BIG_BENCHMARK" = "true" ]; then + rm -rf ~/data.osm.pbf + wget http://download.geofabrik.de/europe/poland-latest.osm.pbf -O ~/data.osm.pbf --quiet + gunzip -c ./pr/test/data/poland_gps_traces.csv.gz > ~/gps_traces.csv + else + if [ ! -f "~/data.osm.pbf" ]; then + wget http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf -O ~/data.osm.pbf + else + echo "Using cached data.osm.pbf" + fi + gunzip -c ./pr/test/data/berlin_gps_traces.csv.gz > ~/gps_traces.csv fi - gunzip -c ./pr/test/data/berlin_gps_traces.csv.gz > ~/gps_traces.csv - name: Prepare environment run: | echo "CCACHE_DIR=$HOME/.ccache" >> $GITHUB_ENV diff --git a/cmake/conan.cmake b/cmake/conan.cmake index 4f5f67e74..7c78f7415 100644 --- a/cmake/conan.cmake +++ b/cmake/conan.cmake @@ -55,7 +55,7 @@ function(_get_msvc_ide_version result) set(${result} 15 PARENT_SCOPE) elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) set(${result} 16 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) + elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1950) set(${result} 17 PARENT_SCOPE) else() message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") diff --git a/scripts/ci/download_gps_traces.py b/scripts/ci/download_gps_traces.py index 961acd532..7974833ac 100644 --- a/scripts/ci/download_gps_traces.py +++ b/scripts/ci/download_gps_traces.py @@ -4,41 +4,43 @@ import csv import sys import argparse -def get_osm_gps_traces(min_lon, min_lat, max_lon, max_lat): +def get_osm_gps_traces(bboxes): url = 'https://api.openstreetmap.org/api/0.6/trackpoints' traces = [] lon_step = 0.25 lat_step = 0.25 - - current_min_lon = min_lon - while current_min_lon < max_lon: - current_max_lon = min(current_min_lon + lon_step, max_lon) + for bbox in bboxes: + min_lon, min_lat, max_lon, max_lat = map(float, bbox.split(',')) - current_min_lat = min_lat - while current_min_lat < max_lat: - current_max_lat = min(current_min_lat + lat_step, max_lat) + current_min_lon = min_lon + while current_min_lon < max_lon: + current_max_lon = min(current_min_lon + lon_step, max_lon) - bbox = f'{current_min_lon},{current_min_lat},{current_max_lon},{current_max_lat}' - print(f"Requesting bbox: {bbox}", file=sys.stderr) - - params = { - 'bbox': bbox, - 'page': 0 - } - headers = { - 'Accept': 'application/xml' - } - - response = requests.get(url, params=params, headers=headers) - if response.status_code == 200: - traces.append(response.content) - else: - print(f"Error fetching data for bbox {bbox}: {response.status_code} {response.text}", file=sys.stderr) - - current_min_lat += lat_step - current_min_lon += lon_step + current_min_lat = min_lat + while current_min_lat < max_lat: + current_max_lat = min(current_min_lat + lat_step, max_lat) + + bbox_str = f'{current_min_lon},{current_min_lat},{current_max_lon},{current_max_lat}' + print(f"Requesting bbox: {bbox_str}", file=sys.stderr) + + params = { + 'bbox': bbox_str, + 'page': 0 + } + headers = { + 'Accept': 'application/xml' + } + + response = requests.get(url, params=params, headers=headers) + if response.status_code == 200: + traces.append(response.content) + else: + print(f"Error fetching data for bbox {bbox_str}: {response.status_code} {response.text}", file=sys.stderr) + + current_min_lat += lat_step + current_min_lon += lon_step return traces @@ -68,15 +70,12 @@ def save_to_csv(data, file): writer.writerows(data) if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Fetch and output OSM GPS traces for a given bounding box.') - parser.add_argument('min_lon', type=float, help='Minimum longitude of the bounding box') - parser.add_argument('min_lat', type=float, help='Minimum latitude of the bounding box') - parser.add_argument('max_lon', type=float, help='Maximum longitude of the bounding box') - parser.add_argument('max_lat', type=float, help='Maximum latitude of the bounding box') + parser = argparse.ArgumentParser(description='Fetch and output OSM GPS traces for given bounding boxes.') + parser.add_argument('bboxes', nargs='+', help='Bounding boxes in the format min_lon,min_lat,max_lon,max_lat') args = parser.parse_args() - gpx_data_traces = get_osm_gps_traces(args.min_lon, args.min_lat, args.max_lon, args.max_lat) + gpx_data_traces = get_osm_gps_traces(args.bboxes) print(f"Collected {len(gpx_data_traces)} trace segments", file=sys.stderr) all_data = [] diff --git a/scripts/ci/run_benchmarks.sh b/scripts/ci/run_benchmarks.sh index ffde03bf0..2affff0f7 100755 --- a/scripts/ci/run_benchmarks.sh +++ b/scripts/ci/run_benchmarks.sh @@ -42,6 +42,12 @@ function run_benchmarks_for_folder { measure_peak_ram_and_time "$BINARIES_FOLDER/osrm-customize $FOLDER/data.osrm" "$RESULTS_FOLDER/osrm_customize.bench" measure_peak_ram_and_time "$BINARIES_FOLDER/osrm-contract $FOLDER/data.osrm" "$RESULTS_FOLDER/osrm_contract.bench" + for BENCH in nearest table trip route match; do + ./$BENCHMARKS_FOLDER/bench "$FOLDER/data.osrm" mld ~/gps_traces.csv ${BENCH} > "$RESULTS_FOLDER/random_${BENCH}_mld.bench" || true + ./$BENCHMARKS_FOLDER/bench "$FOLDER/data.osrm" ch ~/gps_traces.csv ${BENCH} > "$RESULTS_FOLDER/random_${BENCH}_ch.bench" || true + done + + for ALGORITHM in ch mld; do $BINARIES_FOLDER/osrm-routed --algorithm $ALGORITHM $FOLDER/data.osrm & OSRM_ROUTED_PID=$! @@ -59,8 +65,6 @@ function run_benchmarks_for_folder { kill -9 $OSRM_ROUTED_PID done - - } run_benchmarks_for_folder $1 "${1}_results" $2 diff --git a/scripts/ci/windows-build.bat b/scripts/ci/windows-build.bat deleted file mode 100644 index bc1fc0a8f..000000000 --- a/scripts/ci/windows-build.bat +++ /dev/null @@ -1,89 +0,0 @@ -@ECHO OFF -SETLOCAL -SET EL=0 - -ECHO NUMBER_OF_PROCESSORS^: %NUMBER_OF_PROCESSORS% - -SET PROJECT_DIR=%CD% -SET CONFIGURATION=Release - -mkdir build -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -cd build -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -cmake -DENABLE_CONAN=ON -DENABLE_NODE_BINDINGS=ON -DCMAKE_BUILD_TYPE=%CONFIGURATION% -G "Visual Studio 17 2022" .. -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -msbuild OSRM.sln ^ -/p:Configuration=%CONFIGURATION% ^ -/p:Platform=x64 ^ -/t:rebuild ^ -/p:BuildInParallel=true ^ -/m:%NUMBER_OF_PROCESSORS% ^ -/toolsversion:Current ^ -/clp:Verbosity=quiet ^ -/nologo -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -CD %PROJECT_DIR%\build -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO running extractor-tests.exe ... -unit_tests\%CONFIGURATION%\extractor-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO running contractor-tests.exe ... -unit_tests\%CONFIGURATION%\contractor-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO running engine-tests.exe ... -unit_tests\%CONFIGURATION%\engine-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO running util-tests.exe ... -unit_tests\%CONFIGURATION%\util-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO running server-tests.exe ... -unit_tests\%CONFIGURATION%\server-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO running partitioner-tests.exe ... -unit_tests\%CONFIGURATION%\partitioner-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO running customizer-tests.exe ... -unit_tests\%CONFIGURATION%\customizer-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -SET test_region=monaco -SET test_region_ch=ch\monaco -SET test_region_mld=mld\monaco -SET test_osm=%test_region%.osm.pbf -COPY %PROJECT_DIR%\test\data\%test_region%.osm.pbf %test_osm% -%CONFIGURATION%\osrm-extract.exe -p %PROJECT_DIR%\profiles\car.lua %test_osm% -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -MKDIR ch -XCOPY %test_region%.osrm.* ch\ -XCOPY %test_region%.osrm ch\ -MKDIR mld -XCOPY %test_region%.osrm.* mld\ -XCOPY %test_region%.osrm mld\ -%CONFIGURATION%\osrm-contract.exe %test_region_ch%.osrm -%CONFIGURATION%\osrm-partition.exe %test_region_mld%.osrm -%CONFIGURATION%\osrm-customize.exe %test_region_mld%.osrm -XCOPY /Y ch\*.* ..\test\data\ch\ -XCOPY /Y mld\*.* ..\test\data\mld\ -unit_tests\%CONFIGURATION%\library-tests.exe -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -:ERROR -ECHO ~~~~~~~~~~~~~~~~~~~~~~ ERROR %~f0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -ECHO ERRORLEVEL^: %ERRORLEVEL% -SET EL=%ERRORLEVEL% - -:DONE -ECHO ~~~~~~~~~~~~~~~~~~~~~~ DONE %~f0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -EXIT /b %EL% diff --git a/src/benchmarks/CMakeLists.txt b/src/benchmarks/CMakeLists.txt index 86353dbbf..d2478ab4a 100644 --- a/src/benchmarks/CMakeLists.txt +++ b/src/benchmarks/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries(rtree-bench ${TBB_LIBRARIES} ${MAYBE_SHAPEFILE}) + add_executable(match-bench EXCLUDE_FROM_ALL ${MatchBenchmarkSources} @@ -35,6 +36,7 @@ add_executable(route-bench route.cpp $) + target_link_libraries(route-bench osrm ${BOOST_BASE_LIBRARIES} @@ -42,6 +44,18 @@ target_link_libraries(route-bench ${TBB_LIBRARIES} ${MAYBE_SHAPEFILE}) +add_executable(bench + EXCLUDE_FROM_ALL + bench.cpp + $) + +target_link_libraries(bench + osrm + ${BOOST_BASE_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${TBB_LIBRARIES} + ${MAYBE_SHAPEFILE}) + add_executable(json-render-bench EXCLUDE_FROM_ALL json_render.cpp @@ -85,5 +99,6 @@ add_custom_target(benchmarks packedvector-bench match-bench route-bench + bench json-render-bench alias-bench) diff --git a/src/benchmarks/bench.cpp b/src/benchmarks/bench.cpp new file mode 100644 index 000000000..ef53c398d --- /dev/null +++ b/src/benchmarks/bench.cpp @@ -0,0 +1,573 @@ +#include "osrm/match_parameters.hpp" +#include "osrm/nearest_parameters.hpp" +#include "osrm/table_parameters.hpp" +#include "osrm/trip_parameters.hpp" + +#include "engine/engine_config.hpp" +#include "util/coordinate.hpp" +#include "util/timing_util.hpp" + +#include "osrm/route_parameters.hpp" + +#include "osrm/coordinate.hpp" +#include "osrm/engine_config.hpp" +#include "osrm/json_container.hpp" + +#include "osrm/osrm.hpp" +#include "osrm/status.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osrm; + +namespace +{ + +class GPSTraces +{ + private: + std::set trackIDs; + std::unordered_map> traces; + std::vector coordinates; + mutable std::mt19937 gen; + + public: + GPSTraces(int seed) : gen(std::random_device{}()) { gen.seed(seed); } + + bool readCSV(const std::string &filename) + { + std::ifstream file(filename); + if (!file.is_open()) + { + std::cerr << "Error opening file: " << filename << std::endl; + return false; + } + + std::string line; + std::getline(file, line); + + while (std::getline(file, line)) + { + std::istringstream ss(line); + std::string token; + + int trackID; + double latitude, longitude; + std::string time; + + std::getline(ss, token, ','); + trackID = std::stoi(token); + + std::getline(ss, token, ','); + latitude = std::stod(token); + + std::getline(ss, token, ','); + longitude = std::stod(token); + + // handle empty fields + if (std::getline(ss, token, ',')) + { + time = token; + } + + trackIDs.insert(trackID); + traces[trackID].emplace_back(osrm::util::Coordinate{ + osrm::util::FloatLongitude{longitude}, osrm::util::FloatLatitude{latitude}}); + coordinates.emplace_back(osrm::util::Coordinate{osrm::util::FloatLongitude{longitude}, + osrm::util::FloatLatitude{latitude}}); + } + + file.close(); + return true; + } + + const osrm::util::Coordinate &getRandomCoordinate() const + { + std::uniform_int_distribution<> dis(0, coordinates.size() - 1); + return coordinates[dis(gen)]; + } + + const std::vector &getRandomTrace() const + { + std::uniform_int_distribution<> dis(0, trackIDs.size() - 1); + auto it = trackIDs.begin(); + std::advance(it, dis(gen)); + return traces.at(*it); + } +}; + +class Statistics +{ + public: + void push(double timeMs) + { + times.push_back(timeMs); + sorted = false; + } + + double mean() { return sum() / times.size(); } + + double sum() + { + double sum = 0; + for (auto time : times) + { + sum += time; + } + return sum; + } + + double min() { return *std::min_element(times.begin(), times.end()); } + + double max() { return *std::max_element(times.begin(), times.end()); } + + double percentile(double p) + { + const auto × = getTimes(); + return times[static_cast(p * times.size())]; + } + + private: + std::vector getTimes() + { + if (!sorted) + { + std::sort(times.begin(), times.end()); + sorted = true; + } + return times; + } + + std::vector times; + + bool sorted = false; +}; + +std::ostream &operator<<(std::ostream &os, Statistics &statistics) +{ + os << std::fixed << std::setprecision(2); + os << "total: " << statistics.sum() << "ms" << std::endl; + os << "avg: " << statistics.mean() << "ms" << std::endl; + os << "min: " << statistics.min() << "ms" << std::endl; + os << "max: " << statistics.max() << "ms" << std::endl; + os << "p99: " << statistics.percentile(0.99) << "ms" << std::endl; + return os; +} + +void runRouteBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) +{ + struct Benchmark + { + std::string name; + size_t coordinates; + RouteParameters::OverviewType overview; + bool steps = false; + std::optional alternatives = std::nullopt; + std::optional radius = std::nullopt; + }; + + auto run_benchmark = [&](const Benchmark &benchmark) + { + Statistics statistics; + + auto NUM = 10000; + for (int i = 0; i < NUM; ++i) + { + RouteParameters params; + params.overview = benchmark.overview; + params.steps = benchmark.steps; + + for (size_t i = 0; i < benchmark.coordinates; ++i) + { + params.coordinates.push_back(gpsTraces.getRandomCoordinate()); + } + + if (benchmark.alternatives) + { + params.alternatives = *benchmark.alternatives; + } + + if (benchmark.radius) + { + params.radiuses = std::vector>( + params.coordinates.size(), boost::make_optional(*benchmark.radius)); + } + + engine::api::ResultT result = json::Object(); + TIMER_START(routes); + const auto rc = osrm.Route(params, result); + TIMER_STOP(routes); + + statistics.push(TIMER_MSEC(routes)); + + auto &json_result = std::get(result); + if (rc != Status::Ok || json_result.values.find("routes") == json_result.values.end()) + { + auto code = std::get(json_result.values["code"]).value; + if (code != "NoSegment" && code != "NoRoute") + { + throw std::runtime_error{"Couldn't route: " + code}; + } + } + } + std::cout << benchmark.name << std::endl; + std::cout << statistics << std::endl; + }; + + std::vector benchmarks = { + {"10000 routes, 3 coordinates, no alternatives, overview=full, steps=true", + 3, + RouteParameters::OverviewType::Full, + true, + std::nullopt}, + {"10000 routes, 2 coordinates, no alternatives, overview=full, steps=true", + 2, + RouteParameters::OverviewType::Full, + true, + std::nullopt}, + {"10000 routes, 2 coordinates, 3 alternatives, overview=full, steps=true", + 2, + RouteParameters::OverviewType::Full, + true, + 3}, + {"10000 routes, 3 coordinates, no alternatives, overview=false, steps=false", + 3, + RouteParameters::OverviewType::False, + false, + std::nullopt}, + {"10000 routes, 2 coordinates, no alternatives, overview=false, steps=false", + 2, + RouteParameters::OverviewType::False, + false, + std::nullopt}, + {"10000 routes, 2 coordinates, 3 alternatives, overview=false, steps=false", + 2, + RouteParameters::OverviewType::False, + false, + 3}, + {"10000 routes, 3 coordinates, no alternatives, overview=false, steps=false, radius=750", + 3, + RouteParameters::OverviewType::False, + false, + std::nullopt, + 750}, + {"10000 routes, 2 coordinates, no alternatives, overview=false, steps=false, radius=750", + 2, + RouteParameters::OverviewType::False, + false, + std::nullopt, + 750}, + {"10000 routes, 2 coordinates, 3 alternatives, overview=false, steps=false, radius=750", + 2, + RouteParameters::OverviewType::False, + false, + 3, + 750} + + }; + + for (const auto &benchmark : benchmarks) + { + run_benchmark(benchmark); + } +} + +void runMatchBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) +{ + struct Benchmark + { + std::string name; + std::optional radius = std::nullopt; + }; + + auto run_benchmark = [&](const Benchmark &benchmark) + { + Statistics statistics; + + auto NUM = 1000; + for (int i = 0; i < NUM; ++i) + { + engine::api::ResultT result = json::Object(); + + engine::api::MatchParameters params; + params.coordinates = gpsTraces.getRandomTrace(); + params.radiuses = {}; + if (benchmark.radius) + { + for (size_t index = 0; index < params.coordinates.size(); ++index) + { + params.radiuses.emplace_back(*benchmark.radius); + } + } + + TIMER_START(match); + const auto rc = osrm.Match(params, result); + TIMER_STOP(match); + + statistics.push(TIMER_MSEC(match)); + + auto &json_result = std::get(result); + if (rc != Status::Ok || + json_result.values.find("matchings") == json_result.values.end()) + { + auto code = std::get(json_result.values["code"]).value; + if (code != "NoSegment" && code != "NoMatch") + { + throw std::runtime_error{"Couldn't route: " + code}; + } + } + } + + std::cout << benchmark.name << std::endl; + std::cout << statistics << std::endl; + }; + + std::vector benchmarks = {{"1000 matches, default radius"}, + {"1000 matches, radius=10", 10}, + {"1000 matches, radius=20", 20}}; + + for (const auto &benchmark : benchmarks) + { + run_benchmark(benchmark); + } +} + +void runNearestBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) +{ + struct Benchmark + { + std::string name; + std::optional number_of_results = std::nullopt; + }; + + auto run_benchmark = [&](const Benchmark &benchmark) + { + Statistics statistics; + auto NUM = 10000; + for (int i = 0; i < NUM; ++i) + { + engine::api::ResultT result = json::Object(); + NearestParameters params; + params.coordinates.push_back(gpsTraces.getRandomCoordinate()); + + if (benchmark.number_of_results) + { + params.number_of_results = *benchmark.number_of_results; + } + + TIMER_START(nearest); + const auto rc = osrm.Nearest(params, result); + TIMER_STOP(nearest); + + statistics.push(TIMER_MSEC(nearest)); + + auto &json_result = std::get(result); + if (rc != Status::Ok || + json_result.values.find("waypoints") == json_result.values.end()) + { + auto code = std::get(json_result.values["code"]).value; + if (code != "NoSegment") + { + throw std::runtime_error{"Couldn't find nearest point"}; + } + } + } + + std::cout << benchmark.name << std::endl; + std::cout << statistics << std::endl; + }; + + std::vector benchmarks = {{"10000 nearest, number_of_results=1", 1}, + {"10000 nearest, number_of_results=5", 5}, + {"10000 nearest, number_of_results=10", 10}}; + + for (const auto &benchmark : benchmarks) + { + run_benchmark(benchmark); + } +} + +void runTripBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) +{ + struct Benchmark + { + std::string name; + size_t coordinates; + }; + + auto run_benchmark = [&](const Benchmark &benchmark) + { + Statistics statistics; + auto NUM = 1000; + for (int i = 0; i < NUM; ++i) + { + engine::api::ResultT result = json::Object(); + TripParameters params; + params.roundtrip = true; + + for (size_t i = 0; i < benchmark.coordinates; ++i) + { + params.coordinates.push_back(gpsTraces.getRandomCoordinate()); + } + + TIMER_START(trip); + const auto rc = osrm.Trip(params, result); + TIMER_STOP(trip); + + statistics.push(TIMER_MSEC(trip)); + + auto &json_result = std::get(result); + if (rc != Status::Ok || json_result.values.find("trips") == json_result.values.end()) + { + auto code = std::get(json_result.values["code"]).value; + if (code != "NoSegment") + { + throw std::runtime_error{"Couldn't find trip"}; + } + } + } + + std::cout << benchmark.name << std::endl; + std::cout << statistics << std::endl; + }; + + std::vector benchmarks = { + {"1000 trips, 3 coordinates", 3}, + {"1000 trips, 4 coordinates", 4}, + {"1000 trips, 5 coordinates", 5}, + }; + + for (const auto &benchmark : benchmarks) + { + run_benchmark(benchmark); + } +} +void runTableBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) +{ + struct Benchmark + { + std::string name; + size_t coordinates; + }; + + auto run_benchmark = [&](const Benchmark &benchmark) + { + Statistics statistics; + auto NUM = 250; + for (int i = 0; i < NUM; ++i) + { + engine::api::ResultT result = json::Object(); + TableParameters params; + + for (size_t i = 0; i < benchmark.coordinates; ++i) + { + params.coordinates.push_back(gpsTraces.getRandomCoordinate()); + } + + TIMER_START(table); + const auto rc = osrm.Table(params, result); + TIMER_STOP(table); + + statistics.push(TIMER_MSEC(table)); + + auto &json_result = std::get(result); + if (rc != Status::Ok || + json_result.values.find("durations") == json_result.values.end()) + { + auto code = std::get(json_result.values["code"]).value; + if (code != "NoSegment") + { + throw std::runtime_error{"Couldn't compute table"}; + } + } + } + + std::cout << benchmark.name << std::endl; + std::cout << statistics << std::endl; + }; + + std::vector benchmarks = {{"250 tables, 3 coordinates", 3}, + {"250 tables, 25 coordinates", 25}, + {"250 tables, 50 coordinates", 50}, + {"250 tables, 100 coordinates", 100}}; + + for (const auto &benchmark : benchmarks) + { + run_benchmark(benchmark); + } +} + +} // namespace + +int main(int argc, const char *argv[]) +try +{ + if (argc < 5) + { + std::cerr + << "Usage: " << argv[0] + << " data.osrm \n"; + return EXIT_FAILURE; + } + + // Configure based on a .osrm base path, and no datasets in shared mem from osrm-datastore + EngineConfig config; + config.storage_config = {argv[1]}; + config.algorithm = + std::string{argv[2]} == "mld" ? EngineConfig::Algorithm::MLD : EngineConfig::Algorithm::CH; + config.use_shared_memory = false; + + // Routing machine with several services (such as Route, Table, Nearest, Trip, Match) + OSRM osrm{config}; + + GPSTraces gpsTraces{42}; + gpsTraces.readCSV(argv[3]); + + const auto benchmarkToRun = std::string{argv[4]}; + + if (benchmarkToRun == "route") + { + runRouteBenchmark(osrm, gpsTraces); + } + else if (benchmarkToRun == "match") + { + runMatchBenchmark(osrm, gpsTraces); + } + else if (benchmarkToRun == "nearest") + { + runNearestBenchmark(osrm, gpsTraces); + } + else if (benchmarkToRun == "trip") + { + runTripBenchmark(osrm, gpsTraces); + } + else if (benchmarkToRun == "table") + { + runTableBenchmark(osrm, gpsTraces); + } + else + { + std::cerr << "Unknown benchmark: " << benchmarkToRun << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} +catch (const std::exception &e) +{ + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; +} diff --git a/test/data/poland_gps_traces.csv.gz b/test/data/poland_gps_traces.csv.gz new file mode 100644 index 000000000..6d1e8a16e Binary files /dev/null and b/test/data/poland_gps_traces.csv.gz differ