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

speed up encode #42

Merged
merged 2 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Speed up encode function (now runs in ~72% less time / 3.5x improvement):
* https://github.com/georust/polyline/pull/42

## 0.10.2

* fix decoder crashing with out-of-bounds error (https://github.com/georust/polyline/pull/37):
Expand Down
53 changes: 23 additions & 30 deletions benches/benchmarks.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,66 @@
#[macro_use]
extern crate criterion;
use criterion::Criterion;
use criterion::{Criterion, black_box};
use geo_types::LineString;
use polyline::{decode_polyline, encode_coordinates};
use rand::distributions::Distribution;
use rand::distributions::Uniform;
use rand::thread_rng;
use rand::SeedableRng;
use rand::rngs::StdRng;


#[allow(unused_must_use)]
fn bench_encode(c: &mut Criterion) {
let mut rng = thread_rng();
let mut rng = StdRng::seed_from_u64(42);
// These coordinates cover London, approximately
let between_lon = Uniform::from(-6.379880..1.768960);
let between_lat = Uniform::from(49.871159..55.811741);
let mut v: Vec<[f64; 2]> = vec![];
(0..1000).for_each(|_| v.push([between_lon.sample(&mut rng), between_lat.sample(&mut rng)]));
(0..10_000).for_each(|_| v.push([between_lon.sample(&mut rng), between_lat.sample(&mut rng)]));
let res: LineString<f64> = v.into();
c.bench_function("bench encode: 1000 coordinates", move |b| {

c.bench_function("encode 10_000 coordinates at precision 1e-5", |b| {
b.iter(|| {
encode_coordinates(res.clone(), 5);
black_box(encode_coordinates(res.coords().copied(), 5));
})
});

c.bench_function("encode 10_000 coordinates at precision 1e-6", |b| {
b.iter(|| {
black_box(encode_coordinates(res.coords().copied(), 6));
})
});
}

#[allow(unused_must_use)]
fn bench_decode(c: &mut Criterion) {
// comparable cpp (see e.g. Valhalla) decodes the same number of coords in around 500 µs
let mut rng = thread_rng();
// comparable cpp (see e.g. Valhalla)
let mut rng = StdRng::seed_from_u64(42);
// These coordinates cover London, approximately
let between_lon = Uniform::from(-6.379880..1.768960);
let between_lat = Uniform::from(49.871159..55.811741);
let mut v: Vec<[f64; 2]> = vec![];
(0..21501).for_each(|_| v.push([between_lon.sample(&mut rng), between_lat.sample(&mut rng)]));
(0..10_000).for_each(|_| v.push([between_lon.sample(&mut rng), between_lat.sample(&mut rng)]));
let res: LineString<f64> = v.into();
let encoded = encode_coordinates(res, 6).unwrap();
c.bench_function("bench decode: 21502 coordinates", move |b| {
b.iter(|| {
decode_polyline(&encoded, 6);
})
});
}

#[allow(unused_must_use)]
fn bench_polyline6_decoding(c: &mut Criterion) {
c.bench_function("bench polyline6 decoding", move |b| {
c.bench_function("decode 10_000 coordinates at precision 1e-5", |b| {
b.iter(|| {
decode_polyline("_p~iF~ps|U_ulLnnqC_mqNvxq`@", 6).unwrap();
black_box(decode_polyline(&encoded, 5));
})
});
}

#[allow(unused_must_use)]
fn bench_polyline6_decoding_huge(c: &mut Criterion) {
c.bench_function("bench HUGE polyline6 decoding", move |b| {
c.bench_function("decode 10_000 coordinates at precision 1e-6", |b| {
b.iter(|| {
decode_polyline(
include_str!("../resources/route-geometry-sweden-west-coast.polyline6"),
6,
)
.unwrap();
black_box(decode_polyline(&encoded, 6));
})
});
}


criterion_group!(
benches,
bench_encode,
bench_decode,
bench_polyline6_decoding,
bench_polyline6_decoding_huge
);
criterion_main!(benches);
11 changes: 5 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,13 @@ where
}
}

fn encode(current: f64, previous: f64, factor: i32) -> Result<String, String> {
fn encode(current: f64, previous: f64, factor: i32, output: &mut String) -> Result<(), String> {
let current = scale(current, factor);
let previous = scale(previous, factor);
let mut coordinate = (current - previous) << 1;
if (current - previous) < 0 {
coordinate = !coordinate;
}
let mut output: String = "".to_string();
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 allocation goes away

while coordinate >= 0x20 {
let from_char = char::from_u32(((0x20 | (coordinate & 0x1f)) + 63) as u32)
.ok_or("Couldn't convert character")?;
Expand All @@ -62,7 +61,7 @@ fn encode(current: f64, previous: f64, factor: i32) -> Result<String, String> {
}
let from_char = char::from_u32((coordinate + 63) as u32).ok_or("Couldn't convert character")?;
output.push(from_char);
Copy link
Member Author

@michaelkirk michaelkirk May 1, 2024

Choose a reason for hiding this comment

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

And we now just push directly onto the passed in string. Saving an allocation and a concatenation.

Ok(output)
Ok(())
}

/// Encodes a Google Encoded Polyline.
Expand All @@ -83,16 +82,16 @@ where
let base: i32 = 10;
let factor: i32 = base.pow(precision);

let mut output = "".to_string();
let mut output = String::new();
let mut b = Coord { x: 0.0, y: 0.0 };

for (i, a) in coordinates.into_iter().enumerate() {
check(a.y, (MIN_LATITUDE, MAX_LATITUDE))
.map_err(|e| format!("Latitude error at position {0}: {1}", i, e))?;
check(a.x, (MIN_LONGITUDE, MAX_LONGITUDE))
.map_err(|e| format!("Longitude error at position {0}: {1}", i, e))?;
output = output + &encode(a.y, b.y, factor)?;
output = output + &encode(a.x, b.x, factor)?;
encode(a.y, b.y, factor, &mut output)?;
encode(a.x, b.x, factor, &mut output)?;
b = a;
}
Ok(output)
Expand Down