Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify Time Tracking in all the Algorithms #2278

Merged
merged 53 commits into from
Jun 1, 2020
Merged

Conversation

kevinkreiser
Copy link
Member

@kevinkreiser kevinkreiser commented Mar 18, 2020

Issue

This PR seeks to unify the way we track time during any given algorithms expansion in the graph. At the moment we use a bunch of the same patterns in all the different algorithms, and it would be great to encapsulate that into a single set of functionality that all the different code paths can re-use.

The eventual goal of this is to do bidirectional a* with time dependence but that functionality will be a completely separate PR. this is just the first steps

Tasklist

  • create and use struct in all algorithms
  • add struct unit tests
  • add integration (route) unit tests
  • Update the changelog

@kevinkreiser kevinkreiser changed the title Timedep bidir astar Unify Time Tracking in all the Algorithms Mar 18, 2020
@kevinkreiser kevinkreiser marked this pull request as ready for review March 24, 2020 15:58
t.tm_hour < 0 || t.tm_hour > 23 || t.tm_min < 0 || t.tm_min > 59) {
t.tm_year = 0;
// If parsing failed zero 0 the struct
if (ss.fail()) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gknisely you can more easily test for failure by checking the failbit on the stream


// we weren't able to use this string as a date and you'd like to know about it
if (can_throw && in.fail())
throw std::invalid_argument("Date string is invalid: " + date);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gknisely if the caller says you can throw then we throw when they pass an invalid date format. the default is to NOT throw which is how the code was previously working. currently only the new code ive written for time tracking asks this to throw. i dont like it but i dont want to go through the exorcise right now of making all the places that call this catch

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinkreiser That works, but can we create an issue for this with examples of the bad input so that we can update and create tests. Thank you.

@@ -851,5 +851,10 @@ AABB2<PointLL> GraphReader::GetMinimumBoundingBox(const AABB2<PointLL>& bb) {
return min_bb;
}

int GraphReader::GetTimezone(const baldr::GraphId& node, const GraphTile*& tile) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was a helper funciton in the timedependent algorithms but since all of them need to use it i figured we can move it to the graphreader where we have other helpers

@@ -2,26 +2,27 @@ file(GLOB headers ${VALHALLA_SOURCE_DIR}/valhalla/thor/*.h)

set(sources
astar.cc
attributes_controller.cc
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i went to insert the new source file and saw this wasnt alphabetially sorted, so i did that

if (closest_ni != nullptr && origin.has_date_time() && origin.date_time() == "current") {
origin.set_date_time(
DateTime::iso_date_time(DateTime::get_tz_db().from_index(closest_ni->timezone())));
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i gutted all the time setting stuff from all the algorithms becuse the new struct handles all that for you

@@ -43,7 +43,6 @@ BidirectionalAStar::BidirectionalAStar() : PathAlgorithm() {

// Destructor
BidirectionalAStar::~BidirectionalAStar() {
Clear();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was pointless, but the class isnt final so i left the destructor

@@ -113,7 +112,8 @@ bool BidirectionalAStar::ExpandForward(GraphReader& graphreader,
const GraphId& node,
BDEdgeLabel& pred,
const uint32_t pred_idx,
const bool from_transition) {
const bool from_transition,
const TimeInfo& time_info) {
Copy link
Member Author

@kevinkreiser kevinkreiser Mar 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the algorithms pass this struct around to compute time offsets at every expansion. instead of having the guts of that computation in the algorithm, its all in the TimeInfo struct nicely encapsulated

@@ -228,7 +234,8 @@ inline bool BidirectionalAStar::ExpandForwardInner(GraphReader& graphreader,
return true; // This is an edge we _could_ have expanded, so return true
}

const uint64_t localtime = 0; // Bidirectional is not yet time-aware
// TODO: actually use time_info
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're not actually to the point yet where bidirectional makes time aware expansion choices. coming soon

@coxchapman coxchapman added this to the Sprint 11 - Left Turn milestone May 26, 2020
@kevinkreiser
Copy link
Member Author

Ok I've brought this branch back up to speed in both senses of the word. I've made use of the caching of timezones and I've gotten master merged back into it. I've added extensive testing of the new little struct and done benchmarking using a file of 13797 large and small routes all over the world. I used the run_with_server.py script with an mmap tar of the planet. Both the server and the script were set to concurrency=24.

Master completed the routes in:

real	0m38.098s
user	0m28.145s
sys	0m2.287s

Where as this branch completed the routes in:

real	0m38.107s
user	0m28.569s
sys	0m2.309s

A difference of just 9 milliseconds which is well within the margin of error over the course of almost 14k route requests.

const date::time_zone* origin_tz,
const date::time_zone* dest_tz,
std::unordered_map<const date::time_zone*, std::vector<date::sys_info>>* cache = nullptr);
using tz_sys_info_cache_t = std::unordered_map<const date::time_zone*, std::vector<date::sys_info>>;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i couldnt stand looking at this detail so i type aliased it away

* @return the initialized TimeInfo
*/
static TimeInfo
make(valhalla::Location& location,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of the algorithms now start off by calling this function. it sets all the time information that we need to track over the course of the expansion, see the members above.

* @param next_tz_index the timezone index at the new location
* @return a new TimeInfo object reflecting the offset
*/
inline TimeInfo forward(float seconds_offset, int next_tz_index) const {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method and its sister method below are in charge of updating the timeinfo object to reflect the passage of time as expansion proceeds. so after you traverse an edge you provide the amount of time that passed during that traversal to this function so it can update its members to reflect that time change.

namespace valhalla {
namespace baldr {
// A structure for tracking time information as the route progresses
struct TimeInfo {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the main point of this struct is to provide a one stop shop for 2 things:

  1. convenient encapsulation that allows any routing algorithm (graph expansion) to track time
  2. parameter pack that we can, in the future, send directly to costing to do the right thign with respect to time-based speed look up (second of week for historical speed and seconds from now for real time speed)

const gurka::ways ways = {{"AB", {{"highway", "trunk"}}}};
const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100);
auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_time_tracking_make",
{{"mjlonir.timezone", "/path/to/timezone.sqlite"}});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mjlonir? :)

}
}
}
std::vector<double> expected = {0, 17.1429, 34.2857, 176.789, 220.289, 244.289, 268.289};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the big step between 34s to 176s? Is it because of a huge turncost when turning south?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i want to say its because its right hand driving an you make a left onto a lower class road from the ramp but... what i really did here was run this exact test on master and captured the output. this was basically a regression test

sw += tz_diff;
}

// wrap the week second if it went past the end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the seconds_of_week go past the beginning? I.e, become <0?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not in this function, but it could in the function below which checks the other direction

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@purew pointed out that tz_diff is signed. so actually hes right! technically it should all be signed and get wrapped in either direction in both functions. this is important in the forward direction when you drive west over a timezone but are routing near midnight on sunday

uint64_t seconds_from_now : 43;

// the sign bit for seconds_from_now
uint64_t negative_seconds_from_now : 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be named something to signify it's a boolean, like is_negative_... or has_negative_...?

purew
purew previously approved these changes May 29, 2020
Copy link
Contributor

@purew purew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Great with all the unification! 🍰

Just had a few comments

purew
purew previously approved these changes May 29, 2020
Copy link
Contributor

@purew purew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GR8!

@kevinkreiser
Copy link
Member Author

So I've now also tested timedep routes and the main difference is that now that we are using the timezone cache, running the 14k routes from above finishes in real 3m12.889s where as the old code finished in real 16m22.108s. this is still a far cry from bidirectionals 40 seconds but its a nice improvement. There were slight differences in timing though. about half of them had a difference of 1 second which i dont care about at all. About 1% also had slight deviations in the path chosen but had arrival (depart_at) times that worked out to be the same (minute resolution) which i also chaulk up to having that 1 second differences (they were usually long routes with a tiny deviation from the previous codes path). We could try to reconcile the 1 second difference but I dont think its needed.

@kevinkreiser kevinkreiser merged commit 6f6e29e into master Jun 1, 2020
@purew purew deleted the timedep_bidir_astar branch June 1, 2020 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants