mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-27 15:03:20 +03:00
Cache the Dijkstra pathfinder for custom RoutingParams. This dramatically speeds up the bike network routing tool, since it's now restricted to just a few params. #743
This commit is contained in:
parent
7672cc3ced
commit
2905ca605d
@ -55,7 +55,12 @@ impl RouteExplorer {
|
|||||||
if let Some((ref goal, _, ref mut preview)) = self.goal {
|
if let Some((ref goal, _, ref mut preview)) = self.goal {
|
||||||
*preview = Drawable::empty(ctx);
|
*preview = Drawable::empty(ctx);
|
||||||
if let Some(polygon) = TripEndpoint::path_req(self.start, *goal, mode, &app.primary.map)
|
if let Some(polygon) = TripEndpoint::path_req(self.start, *goal, mode, &app.primary.map)
|
||||||
.and_then(|req| app.primary.map.pathfind_with_params(req, ¶ms).ok())
|
.and_then(|req| {
|
||||||
|
app.primary
|
||||||
|
.map
|
||||||
|
.pathfind_with_params(req, ¶ms, false)
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
.and_then(|path| path.trace(&app.primary.map))
|
.and_then(|path| path.trace(&app.primary.map))
|
||||||
.map(|pl| pl.make_polygons(NORMAL_LANE_THICKNESS))
|
.map(|pl| pl.make_polygons(NORMAL_LANE_THICKNESS))
|
||||||
{
|
{
|
||||||
|
@ -89,7 +89,7 @@ impl RouteResults {
|
|||||||
|
|
||||||
for pair in waypoints.windows(2) {
|
for pair in waypoints.windows(2) {
|
||||||
if let Some(path) = TripEndpoint::path_req(pair[0], pair[1], TripMode::Bike, map)
|
if let Some(path) = TripEndpoint::path_req(pair[0], pair[1], TripMode::Bike, map)
|
||||||
.and_then(|req| map.pathfind_with_params(req, &routing_params).ok())
|
.and_then(|req| map.pathfind_with_params(req, &routing_params, true).ok())
|
||||||
{
|
{
|
||||||
total_distance += path.total_length();
|
total_distance += path.total_length();
|
||||||
total_time += path.estimate_duration(map, Some(map_model::MAX_BIKE_SPEED));
|
total_time += path.estimate_duration(map, Some(map_model::MAX_BIKE_SPEED));
|
||||||
|
@ -570,8 +570,14 @@ impl Map {
|
|||||||
pub fn pathfind(&self, req: PathRequest) -> Result<Path> {
|
pub fn pathfind(&self, req: PathRequest) -> Result<Path> {
|
||||||
self.pathfind_v2(req)?.into_v1(self)
|
self.pathfind_v2(req)?.into_v1(self)
|
||||||
}
|
}
|
||||||
pub fn pathfind_with_params(&self, req: PathRequest, params: &RoutingParams) -> Result<Path> {
|
pub fn pathfind_with_params(
|
||||||
self.pathfind_v2_with_params(req, params)?.into_v1(self)
|
&self,
|
||||||
|
req: PathRequest,
|
||||||
|
params: &RoutingParams,
|
||||||
|
cache_custom: bool,
|
||||||
|
) -> Result<Path> {
|
||||||
|
self.pathfind_v2_with_params(req, params, cache_custom)?
|
||||||
|
.into_v1(self)
|
||||||
}
|
}
|
||||||
pub fn pathfind_v2(&self, req: PathRequest) -> Result<PathV2> {
|
pub fn pathfind_v2(&self, req: PathRequest) -> Result<PathV2> {
|
||||||
assert!(!self.pathfinder_dirty);
|
assert!(!self.pathfinder_dirty);
|
||||||
@ -583,10 +589,11 @@ impl Map {
|
|||||||
&self,
|
&self,
|
||||||
req: PathRequest,
|
req: PathRequest,
|
||||||
params: &RoutingParams,
|
params: &RoutingParams,
|
||||||
|
cache_custom: bool,
|
||||||
) -> Result<PathV2> {
|
) -> Result<PathV2> {
|
||||||
assert!(!self.pathfinder_dirty);
|
assert!(!self.pathfinder_dirty);
|
||||||
self.pathfinder
|
self.pathfinder
|
||||||
.pathfind_with_params(req.clone(), params, self)
|
.pathfind_with_params(req.clone(), params, cache_custom, self)
|
||||||
.ok_or_else(|| anyhow!("can't fulfill {}", req))
|
.ok_or_else(|| anyhow!("can't fulfill {}", req))
|
||||||
}
|
}
|
||||||
pub fn should_use_transit(
|
pub fn should_use_transit(
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
use abstutil::Timer;
|
use abstutil::{Timer, VecMap};
|
||||||
use geom::Duration;
|
use geom::Duration;
|
||||||
|
|
||||||
use crate::pathfind::engine::CreateEngine;
|
use crate::pathfind::engine::CreateEngine;
|
||||||
@ -13,7 +15,7 @@ use crate::{
|
|||||||
RoutingParams,
|
RoutingParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Pathfinder {
|
pub struct Pathfinder {
|
||||||
car_graph: VehiclePathfinder,
|
car_graph: VehiclePathfinder,
|
||||||
bike_graph: VehiclePathfinder,
|
bike_graph: VehiclePathfinder,
|
||||||
@ -22,7 +24,29 @@ pub struct Pathfinder {
|
|||||||
walking_graph: SidewalkPathfinder,
|
walking_graph: SidewalkPathfinder,
|
||||||
walking_with_transit_graph: SidewalkPathfinder,
|
walking_with_transit_graph: SidewalkPathfinder,
|
||||||
|
|
||||||
|
// These params cover the main graphs
|
||||||
params: RoutingParams,
|
params: RoutingParams,
|
||||||
|
|
||||||
|
// Callers can opt into caching with pathfind_with_params
|
||||||
|
// TODO VecMap is probably fast enough. RoutingParams is annoying to implement Hash.
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
|
cached_alternatives: ThreadLocal<RefCell<VecMap<(PathConstraints, RoutingParams), Pathfinder>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implemented manually to deal with the ThreadLocal
|
||||||
|
impl Clone for Pathfinder {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
car_graph: self.car_graph.clone(),
|
||||||
|
bike_graph: self.bike_graph.clone(),
|
||||||
|
bus_graph: self.bus_graph.clone(),
|
||||||
|
train_graph: self.train_graph.clone(),
|
||||||
|
walking_graph: self.walking_graph.clone(),
|
||||||
|
walking_with_transit_graph: self.walking_with_transit_graph.clone(),
|
||||||
|
params: self.params.clone(),
|
||||||
|
cached_alternatives: ThreadLocal::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pathfinder {
|
impl Pathfinder {
|
||||||
@ -37,6 +61,7 @@ impl Pathfinder {
|
|||||||
walking_graph: SidewalkPathfinder::empty(),
|
walking_graph: SidewalkPathfinder::empty(),
|
||||||
walking_with_transit_graph: SidewalkPathfinder::empty(),
|
walking_with_transit_graph: SidewalkPathfinder::empty(),
|
||||||
params: RoutingParams::default(),
|
params: RoutingParams::default(),
|
||||||
|
cached_alternatives: ThreadLocal::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +119,7 @@ impl Pathfinder {
|
|||||||
walking_with_transit_graph,
|
walking_with_transit_graph,
|
||||||
|
|
||||||
params,
|
params,
|
||||||
|
cached_alternatives: ThreadLocal::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,40 +157,58 @@ impl Pathfinder {
|
|||||||
|
|
||||||
/// Finds a path from a start to an end for a certain type of agent.
|
/// Finds a path from a start to an end for a certain type of agent.
|
||||||
pub fn pathfind(&self, req: PathRequest, map: &Map) -> Option<PathV2> {
|
pub fn pathfind(&self, req: PathRequest, map: &Map) -> Option<PathV2> {
|
||||||
self.pathfind_with_params(req, map.routing_params(), map)
|
self.pathfind_with_params(req, map.routing_params(), false, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds a path from a start to an end for a certain type of agent. May use custom routing
|
/// Finds a path from a start to an end for a certain type of agent. May use custom routing
|
||||||
/// parameters.
|
/// parameters. If caching is requested and custom routing parameters are used, then the
|
||||||
|
/// intermediate graph is saved to speed up future calls with the same routing parameters.
|
||||||
pub fn pathfind_with_params(
|
pub fn pathfind_with_params(
|
||||||
&self,
|
&self,
|
||||||
req: PathRequest,
|
req: PathRequest,
|
||||||
params: &RoutingParams,
|
params: &RoutingParams,
|
||||||
|
cache_custom: bool,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
) -> Option<PathV2> {
|
) -> Option<PathV2> {
|
||||||
if params != &self.params {
|
let constraints = req.constraints;
|
||||||
// If the params differ from the ones baked into the map, the CHs won't match. This
|
if params == &self.params {
|
||||||
// should only be happening from the debug UI; be very obnoxious if we start calling it
|
return match constraints {
|
||||||
// from the simulation or something else.
|
PathConstraints::Pedestrian => self.walking_graph.pathfind(req, map),
|
||||||
let mut timer =
|
PathConstraints::Car => self.car_graph.pathfind(req, map),
|
||||||
Timer::new(format!("Pathfinding slowly for {} with custom params", req));
|
PathConstraints::Bike => self.bike_graph.pathfind(req, map),
|
||||||
let tmp_pathfinder = Pathfinder::new_for_one_mode(
|
PathConstraints::Bus => self.bus_graph.pathfind(req, map),
|
||||||
map,
|
PathConstraints::Train => self.train_graph.pathfind(req, map),
|
||||||
params.clone(),
|
};
|
||||||
CreateEngine::Dijkstra,
|
|
||||||
req.constraints,
|
|
||||||
&mut timer,
|
|
||||||
);
|
|
||||||
return tmp_pathfinder.pathfind_with_params(req, params, map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match req.constraints {
|
// If the params differ from the ones baked into the map, the CHs won't match. Do we have a
|
||||||
PathConstraints::Pedestrian => self.walking_graph.pathfind(req, map),
|
// cached alternative?
|
||||||
PathConstraints::Car => self.car_graph.pathfind(req, map),
|
if let Some(alt) = self
|
||||||
PathConstraints::Bike => self.bike_graph.pathfind(req, map),
|
.cached_alternatives
|
||||||
PathConstraints::Bus => self.bus_graph.pathfind(req, map),
|
.get_or(|| RefCell::new(VecMap::new()))
|
||||||
PathConstraints::Train => self.train_graph.pathfind(req, map),
|
.borrow()
|
||||||
|
.get(&(constraints, params.clone()))
|
||||||
|
{
|
||||||
|
return alt.pathfind_with_params(req, params, false, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If somebody's repeatedly calling this without caching, log very obnoxiously.
|
||||||
|
let mut timer = Timer::new(format!("Pathfinding slowly for {} with custom params", req));
|
||||||
|
let tmp_pathfinder = Pathfinder::new_for_one_mode(
|
||||||
|
map,
|
||||||
|
params.clone(),
|
||||||
|
CreateEngine::Dijkstra,
|
||||||
|
constraints,
|
||||||
|
&mut timer,
|
||||||
|
);
|
||||||
|
let result = tmp_pathfinder.pathfind_with_params(req, params, false, map);
|
||||||
|
if cache_custom {
|
||||||
|
self.cached_alternatives
|
||||||
|
.get_or(|| RefCell::new(VecMap::new()))
|
||||||
|
.borrow_mut()
|
||||||
|
.push((constraints, params.clone()), tmp_pathfinder);
|
||||||
|
}
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_costs_from(
|
pub fn all_costs_from(
|
||||||
|
Loading…
Reference in New Issue
Block a user