diff --git a/CHANGELOG.md b/CHANGELOG.md index 02f5aba..bdabad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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): diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index 792e9e1..89097e7 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -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 = 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 = 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); diff --git a/src/lib.rs b/src/lib.rs index 96cfedc..c215abd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,14 +46,13 @@ where } } -fn encode(current: f64, previous: f64, factor: i32) -> Result { +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(); while coordinate >= 0x20 { let from_char = char::from_u32(((0x20 | (coordinate & 0x1f)) + 63) as u32) .ok_or("Couldn't convert character")?; @@ -62,7 +61,7 @@ fn encode(current: f64, previous: f64, factor: i32) -> Result { } let from_char = char::from_u32((coordinate + 63) as u32).ok_or("Couldn't convert character")?; output.push(from_char); - Ok(output) + Ok(()) } /// Encodes a Google Encoded Polyline. @@ -83,7 +82,7 @@ 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() { @@ -91,8 +90,8 @@ where .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)