This commit is contained in:
Siarhei Fedartsou 2024-06-06 21:13:43 +02:00
parent 3e04c9dda4
commit 8aeea8a13a

View File

@ -1,7 +1,7 @@
#include "osrm/match_parameters.hpp" #include "osrm/match_parameters.hpp"
#include "osrm/nearest_parameters.hpp" #include "osrm/nearest_parameters.hpp"
#include "osrm/trip_parameters.hpp"
#include "osrm/table_parameters.hpp" #include "osrm/table_parameters.hpp"
#include "osrm/trip_parameters.hpp"
#include "engine/engine_config.hpp" #include "engine/engine_config.hpp"
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
@ -21,35 +21,36 @@
#include <boost/optional/optional.hpp> #include <boost/optional/optional.hpp>
#include <cstdlib> #include <cstdlib>
#include <exception> #include <exception>
#include <fstream>
#include <iostream> #include <iostream>
#include <optional> #include <optional>
#include <random>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <fstream>
#include <random>
using namespace osrm; using namespace osrm;
namespace { namespace
{
class GPSTraces
class GPSTraces { {
private: private:
std::set<int> trackIDs; std::set<int> trackIDs;
std::unordered_map<int /* track id */, std::vector<osrm::util::Coordinate>> traces; std::unordered_map<int /* track id */, std::vector<osrm::util::Coordinate>> traces;
std::vector<osrm::util::Coordinate> coordinates; std::vector<osrm::util::Coordinate> coordinates;
mutable std::mt19937 gen; mutable std::mt19937 gen;
public: public:
GPSTraces(int seed) : gen(std::random_device{}()) { GPSTraces(int seed) : gen(std::random_device{}()) { gen.seed(seed); }
gen.seed(seed);
}
bool readCSV(const std::string& filename) { bool readCSV(const std::string &filename)
{
std::ifstream file(filename); std::ifstream file(filename);
if (!file.is_open()) { if (!file.is_open())
{
std::cerr << "Error opening file: " << filename << std::endl; std::cerr << "Error opening file: " << filename << std::endl;
return false; return false;
} }
@ -57,8 +58,8 @@ public:
std::string line; std::string line;
std::getline(file, line); std::getline(file, line);
while (std::getline(file, line))
while (std::getline(file, line)) { {
std::istringstream ss(line); std::istringstream ss(line);
std::string token; std::string token;
@ -76,25 +77,30 @@ public:
longitude = std::stod(token); longitude = std::stod(token);
// handle empty fields // handle empty fields
if (std::getline(ss, token, ',')) { if (std::getline(ss, token, ','))
{
time = token; time = token;
} }
trackIDs.insert(trackID); trackIDs.insert(trackID);
traces[trackID].emplace_back(osrm::util::Coordinate{osrm::util::FloatLongitude{longitude}, osrm::util::FloatLatitude{latitude}}); traces[trackID].emplace_back(osrm::util::Coordinate{
coordinates.emplace_back(osrm::util::Coordinate{osrm::util::FloatLongitude{longitude}, osrm::util::FloatLatitude{latitude}}); osrm::util::FloatLongitude{longitude}, osrm::util::FloatLatitude{latitude}});
coordinates.emplace_back(osrm::util::Coordinate{osrm::util::FloatLongitude{longitude},
osrm::util::FloatLatitude{latitude}});
} }
file.close(); file.close();
return true; return true;
} }
const osrm::util::Coordinate& getRandomCoordinate() const { const osrm::util::Coordinate &getRandomCoordinate() const
{
std::uniform_int_distribution<> dis(0, coordinates.size() - 1); std::uniform_int_distribution<> dis(0, coordinates.size() - 1);
return coordinates[dis(gen)]; return coordinates[dis(gen)];
} }
const std::vector<osrm::util::Coordinate>& getRandomTrace() const { const std::vector<osrm::util::Coordinate> &getRandomTrace() const
{
std::uniform_int_distribution<> dis(0, trackIDs.size() - 1); std::uniform_int_distribution<> dis(0, trackIDs.size() - 1);
auto it = trackIDs.begin(); auto it = trackIDs.begin();
std::advance(it, dis(gen)); std::advance(it, dis(gen));
@ -102,40 +108,42 @@ public:
} }
}; };
class Statistics { class Statistics
public: {
void push(double timeMs) { public:
void push(double timeMs)
{
times.push_back(timeMs); times.push_back(timeMs);
sorted = false; sorted = false;
} }
double mean() { double mean() { return sum() / times.size(); }
return sum() / times.size();
}
double sum() { double sum()
{
double sum = 0; double sum = 0;
for (auto time : times) { for (auto time : times)
{
sum += time; sum += time;
} }
return sum; return sum;
} }
double min() { double min() { return *std::min_element(times.begin(), times.end()); }
return *std::min_element(times.begin(), times.end());
}
double max() { double max() { return *std::max_element(times.begin(), times.end()); }
return *std::max_element(times.begin(), times.end());
}
double percentile(double p) { double percentile(double p)
const auto& times = getTimes(); {
const auto &times = getTimes();
return times[static_cast<size_t>(p * times.size())]; return times[static_cast<size_t>(p * times.size())];
} }
private:
std::vector<double> getTimes() { private:
if (!sorted) { std::vector<double> getTimes()
{
if (!sorted)
{
std::sort(times.begin(), times.end()); std::sort(times.begin(), times.end());
sorted = true; sorted = true;
} }
@ -147,8 +155,9 @@ private:
bool sorted = false; bool sorted = false;
}; };
void runRouteBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) { void runRouteBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces)
struct Benchmark {
struct Benchmark
{ {
std::string name; std::string name;
size_t coordinates; size_t coordinates;
@ -160,32 +169,30 @@ void runRouteBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
auto run_benchmark = [&](const Benchmark &benchmark) auto run_benchmark = [&](const Benchmark &benchmark)
{ {
Statistics statistics; Statistics statistics;
auto NUM = 10000; auto NUM = 10000;
for (int i = 0; i < NUM; ++i) for (int i = 0; i < NUM; ++i)
{ {
RouteParameters params; RouteParameters params;
params.overview = benchmark.overview; params.overview = benchmark.overview;
params.steps = benchmark.steps; params.steps = benchmark.steps;
for (size_t i = 0; i < benchmark.coordinates; ++i) for (size_t i = 0; i < benchmark.coordinates; ++i)
{ {
params.coordinates.push_back(gpsTraces.getRandomCoordinate()); params.coordinates.push_back(gpsTraces.getRandomCoordinate());
} }
if (benchmark.alternatives) if (benchmark.alternatives)
{ {
params.alternatives = *benchmark.alternatives; params.alternatives = *benchmark.alternatives;
} }
if (benchmark.radius)
{
params.radiuses = std::vector<boost::optional<double>>(
params.coordinates.size(), boost::make_optional(*benchmark.radius));
}
if (benchmark.radius)
{
params.radiuses = std::vector<boost::optional<double>>(
params.coordinates.size(), boost::make_optional(*benchmark.radius));
}
engine::api::ResultT result = json::Object(); engine::api::ResultT result = json::Object();
TIMER_START(routes); TIMER_START(routes);
@ -198,10 +205,10 @@ void runRouteBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
if (rc != Status::Ok || json_result.values.find("routes") == json_result.values.end()) if (rc != Status::Ok || json_result.values.find("routes") == json_result.values.end())
{ {
auto code = std::get<json::String>(json_result.values["code"]).value; auto code = std::get<json::String>(json_result.values["code"]).value;
if (code != "NoSegment") { if (code != "NoSegment")
{
throw std::runtime_error{"Couldn't route"}; throw std::runtime_error{"Couldn't route"};
} }
} }
} }
std::cout << benchmark.name << std::endl; std::cout << benchmark.name << std::endl;
@ -269,32 +276,33 @@ void runRouteBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
} }
} }
void runMatchBenchmark(const OSRM& osrm, const GPSTraces& gpsTraces) { void runMatchBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces)
struct Benchmark { {
struct Benchmark
{
std::string name; std::string name;
std::optional<size_t> radius = std::nullopt; std::optional<size_t> radius = std::nullopt;
}; };
auto run_benchmark = [&](const Benchmark &benchmark) { auto run_benchmark = [&](const Benchmark &benchmark)
{
Statistics statistics; Statistics statistics;
auto NUM = 1000; auto NUM = 1000;
for (int i = 0; i < NUM; ++i) { for (int i = 0; i < NUM; ++i)
{
engine::api::ResultT result = json::Object(); engine::api::ResultT result = json::Object();
engine::api::MatchParameters params; engine::api::MatchParameters params;
params.coordinates = gpsTraces.getRandomTrace(); params.coordinates = gpsTraces.getRandomTrace();
params.radiuses = {}; params.radiuses = {};
if (benchmark.radius) if (benchmark.radius)
{
for (size_t index = 0; index < params.coordinates.size(); ++index)
{ {
params.radiuses.emplace_back(*benchmark.radius); for (size_t index = 0; index < params.coordinates.size(); ++index)
{
params.radiuses.emplace_back(*benchmark.radius);
}
} }
}
TIMER_START(match); TIMER_START(match);
const auto rc = osrm.Match(params, result); const auto rc = osrm.Match(params, result);
@ -303,9 +311,12 @@ void runMatchBenchmark(const OSRM& osrm, const GPSTraces& gpsTraces) {
statistics.push(TIMER_MSEC(match)); statistics.push(TIMER_MSEC(match));
auto &json_result = std::get<json::Object>(result); auto &json_result = std::get<json::Object>(result);
if (rc != Status::Ok || json_result.values.find("matchings") == json_result.values.end()) { if (rc != Status::Ok ||
json_result.values.find("matchings") == json_result.values.end())
{
auto code = std::get<json::String>(json_result.values["code"]).value; auto code = std::get<json::String>(json_result.values["code"]).value;
if (code != "NoSegment") { if (code != "NoSegment")
{
throw std::runtime_error{"Couldn't route"}; throw std::runtime_error{"Couldn't route"};
} }
} }
@ -318,33 +329,36 @@ void runMatchBenchmark(const OSRM& osrm, const GPSTraces& gpsTraces) {
std::cout << "max: " << statistics.max() << "ms" << std::endl; std::cout << "max: " << statistics.max() << "ms" << std::endl;
}; };
std::vector<Benchmark> benchmarks = { std::vector<Benchmark> benchmarks = {{"1000 matches, default radius"},
{"1000 matches, default radius"}, {"1000 matches, radius=10", 10},
{"1000 matches, radius=10", 10}, {"1000 matches, radius=20", 20}};
{"1000 matches, radius=20", 20}
};
for (const auto &benchmark : benchmarks) { for (const auto &benchmark : benchmarks)
{
run_benchmark(benchmark); run_benchmark(benchmark);
} }
} }
void runNearestBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) { void runNearestBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces)
struct Benchmark { {
struct Benchmark
{
std::string name; std::string name;
std::optional<size_t> number_of_results = std::nullopt; std::optional<size_t> number_of_results = std::nullopt;
}; };
auto run_benchmark = [&](const Benchmark &benchmark) { auto run_benchmark = [&](const Benchmark &benchmark)
{
Statistics statistics; Statistics statistics;
auto NUM = 10000; auto NUM = 10000;
for (int i = 0; i < NUM; ++i) { for (int i = 0; i < NUM; ++i)
{
engine::api::ResultT result = json::Object(); engine::api::ResultT result = json::Object();
NearestParameters params; NearestParameters params;
params.coordinates.push_back(gpsTraces.getRandomCoordinate()); params.coordinates.push_back(gpsTraces.getRandomCoordinate());
if (benchmark.number_of_results) { if (benchmark.number_of_results)
{
params.number_of_results = *benchmark.number_of_results; params.number_of_results = *benchmark.number_of_results;
} }
@ -355,9 +369,12 @@ void runNearestBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
statistics.push(TIMER_MSEC(nearest)); statistics.push(TIMER_MSEC(nearest));
auto &json_result = std::get<json::Object>(result); auto &json_result = std::get<json::Object>(result);
if (rc != Status::Ok || json_result.values.find("waypoints") == json_result.values.end()) { if (rc != Status::Ok ||
json_result.values.find("waypoints") == json_result.values.end())
{
auto code = std::get<json::String>(json_result.values["code"]).value; auto code = std::get<json::String>(json_result.values["code"]).value;
if (code != "NoSegment") { if (code != "NoSegment")
{
throw std::runtime_error{"Couldn't find nearest point"}; throw std::runtime_error{"Couldn't find nearest point"};
} }
} }
@ -370,32 +387,36 @@ void runNearestBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
std::cout << "max: " << statistics.max() << "ms" << std::endl; std::cout << "max: " << statistics.max() << "ms" << std::endl;
}; };
std::vector<Benchmark> benchmarks = { std::vector<Benchmark> benchmarks = {{"10000 nearest, number_of_results=1", 1},
{"10000 nearest, number_of_results=1", 1}, {"10000 nearest, number_of_results=5", 5},
{"10000 nearest, number_of_results=5", 5}, {"10000 nearest, number_of_results=10", 10}};
{"10000 nearest, number_of_results=10", 10}
};
for (const auto &benchmark : benchmarks) { for (const auto &benchmark : benchmarks)
{
run_benchmark(benchmark); run_benchmark(benchmark);
} }
} }
void runTripBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) { void runTripBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces)
struct Benchmark { {
struct Benchmark
{
std::string name; std::string name;
size_t coordinates; size_t coordinates;
}; };
auto run_benchmark = [&](const Benchmark &benchmark) { auto run_benchmark = [&](const Benchmark &benchmark)
{
Statistics statistics; Statistics statistics;
auto NUM = 10000; auto NUM = 10000;
for (int i = 0; i < NUM; ++i) { for (int i = 0; i < NUM; ++i)
{
engine::api::ResultT result = json::Object(); engine::api::ResultT result = json::Object();
TripParameters params; TripParameters params;
params.roundtrip = true; params.roundtrip = true;
for (size_t i = 0; i < benchmark.coordinates; ++i) { for (size_t i = 0; i < benchmark.coordinates; ++i)
{
params.coordinates.push_back(gpsTraces.getRandomCoordinate()); params.coordinates.push_back(gpsTraces.getRandomCoordinate());
} }
@ -406,9 +427,11 @@ void runTripBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
statistics.push(TIMER_MSEC(trip)); statistics.push(TIMER_MSEC(trip));
auto &json_result = std::get<json::Object>(result); auto &json_result = std::get<json::Object>(result);
if (rc != Status::Ok || json_result.values.find("trips") == json_result.values.end()) { if (rc != Status::Ok || json_result.values.find("trips") == json_result.values.end())
{
auto code = std::get<json::String>(json_result.values["code"]).value; auto code = std::get<json::String>(json_result.values["code"]).value;
if (code != "NoSegment") { if (code != "NoSegment")
{
throw std::runtime_error{"Couldn't find trip"}; throw std::runtime_error{"Couldn't find trip"};
} }
} }
@ -427,24 +450,30 @@ void runTripBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
{"10000 trips, 5 coordinates", 5}, {"10000 trips, 5 coordinates", 5},
}; };
for (const auto &benchmark : benchmarks) { for (const auto &benchmark : benchmarks)
{
run_benchmark(benchmark); run_benchmark(benchmark);
} }
} }
void runTableBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) { void runTableBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces)
struct Benchmark { {
struct Benchmark
{
std::string name; std::string name;
size_t coordinates; size_t coordinates;
}; };
auto run_benchmark = [&](const Benchmark &benchmark) { auto run_benchmark = [&](const Benchmark &benchmark)
{
Statistics statistics; Statistics statistics;
auto NUM = 1000; auto NUM = 1000;
for (int i = 0; i < NUM; ++i) { for (int i = 0; i < NUM; ++i)
{
engine::api::ResultT result = json::Object(); engine::api::ResultT result = json::Object();
TableParameters params; TableParameters params;
for (size_t i = 0; i < benchmark.coordinates; ++i) { for (size_t i = 0; i < benchmark.coordinates; ++i)
{
params.coordinates.push_back(gpsTraces.getRandomCoordinate()); params.coordinates.push_back(gpsTraces.getRandomCoordinate());
} }
@ -455,9 +484,12 @@ void runTableBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
statistics.push(TIMER_MSEC(table)); statistics.push(TIMER_MSEC(table));
auto &json_result = std::get<json::Object>(result); auto &json_result = std::get<json::Object>(result);
if (rc != Status::Ok || json_result.values.find("durations") == json_result.values.end()) { if (rc != Status::Ok ||
json_result.values.find("durations") == json_result.values.end())
{
auto code = std::get<json::String>(json_result.values["code"]).value; auto code = std::get<json::String>(json_result.values["code"]).value;
if (code != "NoSegment") { if (code != "NoSegment")
{
throw std::runtime_error{"Couldn't compute table"}; throw std::runtime_error{"Couldn't compute table"};
} }
} }
@ -470,15 +502,14 @@ void runTableBenchmark(const OSRM &osrm, const GPSTraces &gpsTraces) {
std::cout << "max: " << statistics.max() << "ms" << std::endl; std::cout << "max: " << statistics.max() << "ms" << std::endl;
}; };
std::vector<Benchmark> benchmarks = { std::vector<Benchmark> benchmarks = {{"1000 tables, 3 coordinates", 3},
{"1000 tables, 3 coordinates", 3}, {"1000 tables, 25 coordinates", 25},
{"1000 tables, 25 coordinates", 25}, {"1000 tables, 50 coordinates", 50},
{"1000 tables, 50 coordinates", 50}, {"1000 tables, 100 coordinates", 100},
{"1000 tables, 100 coordinates", 100}, {"1000 tables, 250 coordinates", 250}};
{"1000 tables, 250 coordinates", 250}
};
for (const auto &benchmark : benchmarks) { for (const auto &benchmark : benchmarks)
{
run_benchmark(benchmark); run_benchmark(benchmark);
} }
} }
@ -490,16 +521,17 @@ try
{ {
if (argc < 5) if (argc < 5)
{ {
std::cerr << "Usage: " << argv[0] << " data.osrm <mld|ch> <path to GPS traces.csv> <route|match|trip|table|nearest>\n"; std::cerr
<< "Usage: " << argv[0]
<< " data.osrm <mld|ch> <path to GPS traces.csv> <route|match|trip|table|nearest>\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Configure based on a .osrm base path, and no datasets in shared mem from osrm-datastore // Configure based on a .osrm base path, and no datasets in shared mem from osrm-datastore
EngineConfig config; EngineConfig config;
config.storage_config = {argv[1]}; config.storage_config = {argv[1]};
config.algorithm = std::string{argv[2]} == "mld" ? EngineConfig::Algorithm::MLD config.algorithm =
: EngineConfig::Algorithm::CH; std::string{argv[2]} == "mld" ? EngineConfig::Algorithm::MLD : EngineConfig::Algorithm::CH;
config.use_shared_memory = false; config.use_shared_memory = false;
// Routing machine with several services (such as Route, Table, Nearest, Trip, Match) // Routing machine with several services (such as Route, Table, Nearest, Trip, Match)