refactor parallelized pathfinding and use it in trip viz too

This commit is contained in:
Dustin Carlino 2019-05-24 12:33:48 -07:00
parent f674527ef2
commit 1cdbe0ea81
9 changed files with 57 additions and 45 deletions

View File

@ -18,19 +18,23 @@ pub struct TripsVisualizer {
impl TripsVisualizer {
pub fn new(ctx: &mut EventCtx, ui: &UI) -> TripsVisualizer {
let trips = ctx.loading_screen(|_, mut timer| {
let trips = ctx.loading_screen("load trip data", |_, mut timer| {
let popdat: PopDat = abstutil::read_binary("../data/shapes/popdat", &mut timer)
.expect("Couldn't load popdat");
let mut all_trips = clip_trips(&popdat, ui, &mut timer);
timer.start_iter("calculate routes", all_trips.len());
let requests = all_trips
.iter()
.map(|trip| trip.path_req(&ui.primary.map))
.collect();
let paths = ui.primary.map.calculate_paths(requests, &mut timer);
let mut final_trips = Vec::new();
for mut trip in all_trips.drain(..) {
timer.start_iter("route geometry", paths.len());
for (mut trip, (req, maybe_path)) in all_trips.drain(..).zip(paths) {
timer.next();
let req = trip.path_req(&ui.primary.map);
if let Some(route) = ui
.primary
.map
.pathfind(req.clone())
// TODO path.trace is slow too and could be parallelized. Generalize
// calculate_paths to just execute a callback and do the nice timer management.
if let Some(route) = maybe_path
.and_then(|path| path.trace(&ui.primary.map, req.start.dist_along(), None))
{
trip.route = Some(route);

View File

@ -59,7 +59,7 @@ impl ScenarioEditor {
} else if menu.action("edit") {
*self = ScenarioEditor::EditScenario(scenario.clone(), Wizard::new());
} else if menu.action("instantiate") {
ctx.loading_screen(|_, timer| {
ctx.loading_screen("instantiate scenario", |_, timer| {
scenario.instantiate(
&mut ui.primary.sim,
&ui.primary.map,

View File

@ -90,7 +90,7 @@ impl AgentSpawner {
if ui.primary.sim.is_empty() {
if sandbox_menu.action("seed the sim with agents") {
// TODO This covers up the map. :\
ctx.loading_screen(|_, timer| {
ctx.loading_screen("seed sim with agents", |_, timer| {
let map = &ui.primary.map;
let s = if let Some(n) = ui.primary.current_flags.num_agents {
Scenario::scaled_run(map, n)

View File

@ -395,7 +395,7 @@ pub struct PerMapUI {
impl PerMapUI {
pub fn new(flags: Flags, cs: &ColorScheme, ctx: &mut EventCtx) -> PerMapUI {
let (map, sim, draw_map) = ctx.loading_screen(|ctx, timer| {
let (map, sim, draw_map) = ctx.loading_screen("load map", |ctx, timer| {
let mut mem = MeasureMemory::new();
let (map, sim, _) = flags.sim_flags.load(Some(Duration::seconds(30.0)), timer);
mem.reset("Map and Sim", timer);

View File

@ -127,14 +127,19 @@ pub struct EventCtx<'a> {
}
impl<'a> EventCtx<'a> {
pub fn loading_screen<O, F: FnOnce(&mut EventCtx, &mut Timer) -> O>(&mut self, f: F) -> O {
pub fn loading_screen<O, F: FnOnce(&mut EventCtx, &mut Timer) -> O>(
&mut self,
timer_name: &str,
f: F,
) -> O {
let mut timer = Timer::new_with_sink(
"Loading...",
timer_name,
Box::new(LoadingScreen::new(
self.prerender,
self.program,
self.canvas.window_width,
self.canvas.window_height,
timer_name.to_string(),
)),
);
f(self, &mut timer)
@ -148,6 +153,7 @@ pub struct LoadingScreen<'a> {
lines: VecDeque<String>,
max_capacity: usize,
last_drawn: Option<Instant>,
title: String,
}
impl<'a> LoadingScreen<'a> {
@ -156,6 +162,7 @@ impl<'a> LoadingScreen<'a> {
program: &'a glium::Program,
initial_width: f64,
initial_height: f64,
title: String,
) -> LoadingScreen<'a> {
// TODO Ew! Expensive and wacky. Fix by not storing GlyphBrush in Canvas at all.
let dejavu: &[u8] = include_bytes!("assets/DejaVuSans.ttf");
@ -173,6 +180,7 @@ impl<'a> LoadingScreen<'a> {
lines: VecDeque::new(),
max_capacity: (0.8 * initial_height / line_height) as usize,
last_drawn: None,
title,
}
}
@ -185,7 +193,7 @@ impl<'a> LoadingScreen<'a> {
}
self.last_drawn = Some(Instant::now());
let mut txt = Text::prompt("Loading...");
let mut txt = Text::prompt(&self.title);
for l in &self.lines {
txt.add_line(l.to_string());
}

View File

@ -10,8 +10,10 @@ abstutil = { path = "../abstutil" }
geom = { path = "../geom" }
gtfs = { path = "../gtfs" }
nbez = "0.1.0"
num_cpus = "1.10.0"
ordered-float = "1.0.1"
petgraph = { version = "0.4.13", features = ["serde-1"] }
pretty_assertions = "0.6.1"
scoped_threadpool = "0.1.9"
serde = "1.0.89"
serde_derive = "1.0.89"

View File

@ -697,4 +697,31 @@ impl Map {
side2[idx]
}
}
// Parallelizes pathfind()
pub fn calculate_paths(
&self,
requests: Vec<PathRequest>,
timer: &mut Timer,
) -> Vec<(PathRequest, Option<Path>)> {
scoped_threadpool::Pool::new(num_cpus::get() as u32).scoped(|scope| {
let (tx, rx) = std::sync::mpsc::channel();
let mut results: Vec<(PathRequest, Option<Path>)> = Vec::new();
for (idx, req) in requests.into_iter().enumerate() {
results.push((req.clone(), None));
let tx = tx.clone();
scope.execute(move || {
tx.send((idx, self.pathfind(req))).unwrap();
});
}
drop(tx);
timer.start_iter("calculate paths", results.len());
for (idx, path) in rx.iter() {
timer.next();
results[idx].1 = path;
}
results
})
}
}

View File

@ -11,12 +11,10 @@ geom = { path = "../geom" }
histogram = "0.6.9"
map_model = { path = "../map_model" }
more-asserts = "0.2.1"
num_cpus = "1.10.0"
petgraph = "0.4.13"
pretty_assertions = "0.6.1"
rand = { version = "0.6.5", features = ["serde1"] }
rand_xorshift = "0.1.1"
scoped_threadpool = "0.1.9"
serde = "1.0.89"
serde_derive = "1.0.89"
structopt = "0.2.15"

View File

@ -5,7 +5,7 @@ use crate::{
};
use abstutil::Timer;
use geom::{Duration, Speed};
use map_model::{BusRouteID, BusStopID, Map, Path, PathRequest, Position};
use map_model::{BusRouteID, BusStopID, Map, PathRequest, Position};
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeSet;
@ -155,8 +155,7 @@ impl TripSpawner {
timer: &mut Timer,
retry_if_no_room: bool,
) {
let paths = calculate_paths(
map,
let paths = map.calculate_paths(
self.trips
.iter()
.map(|(_, _, _, spec)| spec.get_pathfinding_request(map, parking))
@ -378,29 +377,3 @@ impl TripSpec {
}
}
}
fn calculate_paths(
map: &Map,
requests: Vec<PathRequest>,
timer: &mut Timer,
) -> Vec<(PathRequest, Option<Path>)> {
scoped_threadpool::Pool::new(num_cpus::get() as u32).scoped(|scope| {
let (tx, rx) = std::sync::mpsc::channel();
let mut results: Vec<(PathRequest, Option<Path>)> = Vec::new();
for (idx, req) in requests.into_iter().enumerate() {
results.push((req.clone(), None));
let tx = tx.clone();
scope.execute(move || {
tx.send((idx, map.pathfind(req))).unwrap();
});
}
drop(tx);
timer.start_iter("calculate paths", results.len());
for (idx, path) in rx.iter() {
timer.next();
results[idx].1 = path;
}
results
})
}