Skip to content

Commit

Permalink
Somewhat ameliorate perf regression from validation
Browse files Browse the repository at this point in the history
It's still slower than before the validation checks, but now only 10-20% slower rather than 90-110% slower

$ cargo bench --bench="*" -- --baseline=perf2
   Compiling polyline v0.10.2 (/Users/mkirk/src/georust/polyline)
    Finished `bench` profile [optimized] target(s) in 0.77s
     Running benches/benchmarks.rs (target/release/deps/benchmarks-cc8f3ea04be06866)
encode 10_000 coordinates at precision 1e-5
                        time:   [105.11 µs 105.16 µs 105.23 µs]
                        change: [-0.2235% -0.1070% +0.0226%] (p = 0.09 > 0.05)
                        No change in performance detected.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) high mild
  2 (2.00%) high severe

encode 10_000 coordinates at precision 1e-6
                        time:   [123.98 µs 124.32 µs 124.77 µs]
                        change: [-3.0982% -2.4787% -1.8225%] (p = 0.00 < 0.05)
                        Performance has improved.

decode 10_000 coordinates at precision 1e-5
                        time:   [86.887 µs 87.835 µs 88.768 µs]
                        change: [+10.484% +11.820% +13.207%] (p = 0.00 < 0.05)
                        Performance has regressed.

decode 10_000 coordinates at precision 1e-6
                        time:   [110.52 µs 111.44 µs 112.35 µs]
                        change: [+19.484% +20.773% +22.034%] (p = 0.00 < 0.05)
                        Performance has regressed.
  • Loading branch information
michaelkirk committed May 8, 2024
1 parent 0d827dd commit c57292a
Showing 1 changed file with 21 additions and 31 deletions.
52 changes: 21 additions & 31 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use geo_types::{Coord, LineString};
use std::char;
use std::iter::{Enumerate, Peekable};

const MIN_LONGITUDE: f64 = -180.0;
const MAX_LONGITUDE: f64 = 180.0;
Expand Down Expand Up @@ -109,59 +110,51 @@ where
/// let decoded_polyline = polyline::decode_polyline(&"_p~iF~ps|U_ulLnnqC_mqNvxq`@", 5);
/// ```
pub fn decode_polyline(polyline: &str, precision: u32) -> Result<LineString<f64>, String> {
let mut index = 0;
let mut scaled_lat: i64 = 0;
let mut scaled_lon: i64 = 0;
let mut coordinates = vec![];
let base: i32 = 10;
let factor = i64::from(base.pow(precision));

let chars = polyline.as_bytes();
let mut chars = polyline.as_bytes().iter().copied().enumerate().peekable();

while index < chars.len() {
let (latitude_change, new_index) = trans(chars, index)?;
if new_index >= chars.len() {
break;
}
while let Some((lat_start, _)) = chars.peek().copied() {
let latitude_change = decode_next(&mut chars)?;
scaled_lat += latitude_change;
let lat = scaled_lat as f64 / factor as f64;
if !(MIN_LATITUDE..MAX_LATITUDE).contains(&lat) {
return Err(format!(
"Invalid latitude: {lat} from character range {index}..{new_index}"
));
return Err(format!("Invalid latitude: {lat} at index: {lat_start}"));
}

let (longitude_change, new_new_index) = trans(chars, new_index)?;
let Some((lon_start, _)) = chars.peek().copied() else {
return Err(format!(
"No longitude to go with latitude at index: {lat_start}"
));
};
let longitude_change = decode_next(&mut chars)?;
scaled_lon += longitude_change;
let lon = scaled_lon as f64 / factor as f64;
if !(MIN_LONGITUDE..MAX_LONGITUDE).contains(&lon) {
return Err(format!(
"Invalid longitude: {lon} from character range {new_index}..{new_new_index}"
));
return Err(format!("Invalid longitude: {lon} at index: {lon_start}"));
}

index = new_new_index;
coordinates.push([lon, lat]);
}

Ok(coordinates.into())
}

fn trans(chars: &[u8], mut index: usize) -> Result<(i64, usize), String> {
fn decode_next(
chars: &mut Peekable<Enumerate<impl std::iter::Iterator<Item = u8>>>,
) -> Result<i64, String> {
let mut shift = 0;
let mut result = 0;
let mut byte;
loop {
if index >= chars.len() {
return Err(format!("Invalid polyline missing valid termination"));
}
byte = chars[index] as u64;
while let Some((idx, mut byte)) = chars.next() {
if byte < 63 || (shift > 64 - 5) {
return Err(format!("Cannot decode character at index {index}"));
return Err(format!("Cannot decode character at index {idx}"));
}
byte -= 63;
result |= (byte & 0x1f) << shift;
index += 1;
result |= ((byte & 0x1f) as u64) << shift;
shift += 5;
if byte < 0x20 {
break;
Expand All @@ -173,7 +166,7 @@ fn trans(chars: &[u8], mut index: usize) -> Result<(i64, usize), String> {
} else {
result >> 1
} as i64;
Ok((coordinate_change, index))
Ok(coordinate_change)
}

#[cfg(test)]
Expand Down Expand Up @@ -252,10 +245,7 @@ mod tests {
fn broken_string() {
let s = "_p~iF~ps|U_u🗑lLnnqC_mqNvxq`@";
let err = decode_polyline(s, 5).unwrap_err();
assert_eq!(
err,
"Invalid latitude: 2306360.53104 from character range 10..18"
);
assert_eq!(err, "Invalid latitude: 2306360.53104 at index: 10");
}

#[test]
Expand All @@ -269,7 +259,7 @@ mod tests {
fn another_invalid_string() {
let s = "ugh_ugh";
let err = decode_polyline(s, 5).unwrap_err();
assert_eq!(err, "Invalid polyline missing valid termination");
assert_eq!(err, "Invalid latitude: 49775.95019 at index: 0");
}

#[test]
Expand Down

0 comments on commit c57292a

Please sign in to comment.