diff --git a/Cargo.toml b/Cargo.toml index 3483be9..cfa998a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,4 +51,7 @@ rayon = ["dep:rayon"] derive = ["dep:rpcl2_derive", "dep:type-layout"] nalgebra = ["dep:nalgebra"] -default = ["rayon", "derive", "nalgebra"] \ No newline at end of file +default = ["derive"] + +[package.metadata.docs.rs] +rustdoc-args = ["--generate-link-to-definition"] diff --git a/README.md b/README.md index 7143c6c..8c4a8d1 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ ## !! Note !! + This library is currently in development for v1.0.0, for the documentation of v0.4.0 on crates.io, visit the [docs](https://docs.rs/ros_pointcloud2/0.4.0/ros_pointcloud2/).

ROS PointCloud2

-

A complete and versatile abstraction of PointCloud2.

+

A PointCloud2 message conversion library.

-To keep the crate a general purpose library for the problem, it uses its own type for the message `PointCloud2Msg`. -ROS1 and ROS2 is supported with feature flags. +ros_pointcloud2 uses its own type for the message `PointCloud2Msg` to keep the library framework agnostic. ROS1 and ROS2 are supported with feature flags. Get started with the example below, check out the other use cases in the `examples` folder or see the [Documentation](https://docs.rs/ros_pointcloud2/1.0.0/ros_pointcloud2/) for a complete guide. ## Quickstart -The following example uses the iterator APIs. For memory bound pipelines, you may also try the `try_from_vec` or `try_into_vec` functions by enabling the `derive` feature. - ```rust use ros_pointcloud2::prelude::*; @@ -27,7 +25,7 @@ let cloud_points = vec![ ]; // Give the Vec or anything that implements `IntoIterator`. -let in_msg = PointCloud2Msg::try_from_iter(cloud_points).unwrap(); +let in_msg = PointCloud2Msg::try_from_vec(cloud_points).unwrap(); // Convert the ROS crate message type, we will use r2r here. // let msg: r2r::sensor_msgs::msg::PointCloud2 = in_msg.into(); @@ -36,7 +34,7 @@ let in_msg = PointCloud2Msg::try_from_iter(cloud_points).unwrap(); // let in_msg: PointCloud2Msg = msg.into(); let new_pcl = in_msg.try_into_iter().unwrap() - .map(|point: PointXYZ| { // Define the type of point here. + .map(|point: PointXYZ| { // Define the info you want to have from the Msg. // Some logic here ... point @@ -84,7 +82,15 @@ Also, indicate the following dependencies to your linker inside the `package.xml builtin_interfaces ``` -Please open an issue or PR if you want to see support for other crates. +Please open an issue or PR if you need other integrations. + +## Performance + +The library offers a speed up when compared to PointCloudLibrary (PCL) conversions but the specific factor depends heavily on the use case and system. +`vec` conversions are on average ~7.5x faster than PCL while the single core iteration `_iter` APIs are around 2x faster. +Parallelization with `_par_iter` showcases a 10.5x speed up compared to an OpenMP accelerated PCL pipeline. + +The full benchmarks are publicly available in this [repository](https://github.com/stelzo/ros_pcl_conv_bench). ## License diff --git a/benches/roundtrip.rs b/benches/roundtrip.rs index cf4945b..afe4473 100644 --- a/benches/roundtrip.rs +++ b/benches/roundtrip.rs @@ -3,32 +3,174 @@ use ros_pointcloud2::prelude::*; use rand::Rng; -pub fn generate_random_pointcloud(num_points: usize, min: f32, max: f32) -> Vec { +pub type PointXYZB = PointXYZINormal; + +pub fn distance_to_origin(point: &PointXYZ) -> f32 { + ((point.x.powi(2)) + (point.y.powi(2)) + (point.z.powi(2))).sqrt() +} + +pub fn dot_product(point1: &PointXYZ, point2: &PointXYZ) -> f32 { + point1.x * point2.x + point1.y * point2.y + point1.z * point2.z +} + +pub fn cross_product(point1: &PointXYZ, point2: &PointXYZ) -> PointXYZ { + PointXYZ { + x: point1.y * point2.z - point1.z * point2.y, + y: point1.z * point2.x - point1.x * point2.z, + z: point1.x * point2.y - point1.y * point2.x, + } +} + +pub fn scalar_multiply(point: &PointXYZ, scalar: f32) -> PointXYZ { + PointXYZ { + x: point.x * scalar, + y: point.y * scalar, + z: point.z * scalar, + } +} + +pub fn magnitude_squared(point: &PointXYZ) -> f32 { + (point.x.powi(2)) + (point.y.powi(2)) + (point.z.powi(2)) +} + +pub fn reflection_through_plane( + point: &PointXYZ, + normal: &PointXYZ, + point_on_plane: &PointXYZ, +) -> PointXYZ { + PointXYZ { + x: point.x + - 2.0 + * ((point.x - point_on_plane.x) * normal.x + + (point.y - point_on_plane.y) * normal.y + + (point.z - point_on_plane.z) * normal.z), + y: point.y + - 2.0 + * ((point.x - point_on_plane.x) * normal.x + + (point.y - point_on_plane.y) * normal.y + + (point.z - point_on_plane.z) * normal.z), + z: point.z + - 2.0 + * ((point.x - point_on_plane.x) * normal.x + + (point.y - point_on_plane.y) * normal.y + + (point.z - point_on_plane.z) * normal.z), + } +} + +pub fn rotation_about_x(point: &PointXYZ, angle: f32) -> PointXYZ { + let c = f32::cos(angle); + let s = f32::sin(angle); + PointXYZ { + x: point.x, + y: point.y * c - point.z * s, + z: point.y * s + point.z * c, + } +} + +pub fn closest_point_on_line( + point: &PointXYZ, + line_point: &PointXYZ, + line_direction: &PointXYZ, +) -> PointXYZ { + PointXYZ { + x: line_point.x + + (line_point.x - point.x) * ((line_point.x - point.x).powi(2)) + / ((line_direction.x * 2.0).powi(2)) + + (line_direction.y * 2.0) * (point.z - line_point.z) + / ((line_direction.z * 2.0).powi(2)), + y: line_point.y + + (line_point.y - point.y) * ((line_point.y - point.y).powi(2)) + / ((line_direction.y * 2.0).powi(2)) + + (line_direction.x * 2.0) * (point.x - line_point.x) + / ((line_direction.x * 2.0).powi(2)), + z: line_point.z + + (line_point.z - point.z) * ((line_point.z - point.z).powi(2)) + / ((line_direction.z * 2.0).powi(2)) + + (line_direction.y * 2.0) * (point.y - line_point.y) + / ((line_direction.y * 2.0).powi(2)), + } +} + +fn minus(point1: &PointXYZ, point2: &PointXYZ) -> PointXYZ { + PointXYZ { + x: point1.x - point2.x, + y: point1.y - point2.y, + z: point1.z - point2.z, + } +} + +pub fn generate_random_pointcloud(num_points: usize, min: f32, max: f32) -> Vec { let mut rng = rand::thread_rng(); let mut pointcloud = Vec::with_capacity(num_points); for _ in 0..num_points { - let point = PointXYZ { + let point = PointXYZB { x: rng.gen_range(min..max), y: rng.gen_range(min..max), z: rng.gen_range(min..max), + ..Default::default() }; pointcloud.push(point); } pointcloud } +pub fn heavy_computing(point: &PointXYZ, iterations: u32) -> f32 { + let mut result = distance_to_origin(point); + for _ in 0..iterations { + result += dot_product( + point, + &PointXYZ { + x: 1.0, + y: 2.0, + z: 3.0, + }, + ); + result += cross_product( + point, + &PointXYZ { + x: 4.0, + y: 5.0, + z: 6.0, + }, + ) + .x; + result = result + (result * 10.0).sqrt(); + let reflected_point = reflection_through_plane( + point, + &PointXYZ { + x: 7.0, + y: 8.0, + z: 9.0, + }, + &PointXYZ { + x: 3.0, + y: 4.0, + z: 5.0, + }, + ); + let rotated_point = rotation_about_x( + &PointXYZ { + x: 10.0, + y: 11.0, + z: 12.0, + }, + std::f32::consts::PI / 2.0, + ); + + result += magnitude_squared(&minus(&reflected_point, &rotated_point)); + } + result +} + #[cfg(feature = "derive")] -fn roundtrip_vec(cloud: Vec) -> bool { +fn roundtrip_vec(cloud: Vec) -> bool { let orig_len = cloud.len(); let internal_msg = PointCloud2Msg::try_from_vec(cloud).unwrap(); - let total = internal_msg - .try_into_iter() - .unwrap() - .collect::>(); + let total: Vec = internal_msg.try_into_vec().unwrap(); orig_len == total.len() } -fn roundtrip(cloud: Vec) -> bool { +fn roundtrip(cloud: Vec) -> bool { let orig_len = cloud.len(); let internal_msg = PointCloud2Msg::try_from_iter(cloud).unwrap(); let total = internal_msg @@ -39,15 +181,13 @@ fn roundtrip(cloud: Vec) -> bool { } #[cfg(feature = "derive")] -fn roundtrip_filter_vec(cloud: Vec) -> bool { +fn roundtrip_filter_vec(cloud: Vec) -> bool { let orig_len = cloud.len(); let internal_msg = PointCloud2Msg::try_from_vec(cloud).unwrap(); let total = internal_msg .try_into_iter() .unwrap() - .filter(|point: &PointXYZ| { - (point.x.powi(2) + point.y.powi(2) + point.z.powi(2)).sqrt() < 1.9 - }) + .filter(|point: &PointXYZ| distance_to_origin(point) < 69.9) .fold(PointXYZ::default(), |acc, point| PointXYZ { x: acc.x + point.x, y: acc.y + point.y, @@ -56,15 +196,13 @@ fn roundtrip_filter_vec(cloud: Vec) -> bool { orig_len == total.x as usize } -fn roundtrip_filter(cloud: Vec) -> bool { +fn roundtrip_filter(cloud: Vec) -> bool { let orig_len = cloud.len(); let internal_msg = PointCloud2Msg::try_from_iter(cloud).unwrap(); let total = internal_msg .try_into_iter() .unwrap() - .filter(|point: &PointXYZ| { - (point.x.powi(2) + point.y.powi(2) + point.z.powi(2)).sqrt() < 1.9 - }) + .filter(|point: &PointXYZ| distance_to_origin(point) < 69.9) .fold(PointXYZ::default(), |acc, point| PointXYZ { x: acc.x + point.x, y: acc.y + point.y, @@ -73,8 +211,52 @@ fn roundtrip_filter(cloud: Vec) -> bool { orig_len == total.x as usize } +fn roundtrip_computing(cloud: Vec) -> bool { + let internal_msg = PointCloud2Msg::try_from_iter(cloud).unwrap(); + let total = internal_msg + .try_into_iter() + .unwrap() + .map(|point: PointXYZ| heavy_computing(&point, 100)) + .sum::(); + total > 0.0 +} + +#[cfg(feature = "rayon")] +fn roundtrip_computing_par(cloud: Vec) -> bool { + let internal_msg = PointCloud2Msg::try_from_iter(cloud).unwrap(); + let total = internal_msg + .try_into_par_iter() + .unwrap() + .map(|point: PointXYZ| heavy_computing(&point, 100)) + .sum::(); + total > 0.0 +} + #[cfg(feature = "rayon")] -fn roundtrip_par(cloud: Vec) -> bool { +fn roundtrip_computing_par_par(cloud: Vec) -> bool { + let internal_msg = PointCloud2Msg::try_from_par_iter(cloud.into_par_iter()).unwrap(); + let total = internal_msg + .try_into_par_iter() + .unwrap() + .map(|point: PointXYZ| heavy_computing(&point, 100)) + .sum::(); + total > 0.0 +} + +#[cfg(feature = "derive")] +fn roundtrip_computing_vec(cloud: Vec) -> bool { + let internal_msg = PointCloud2Msg::try_from_vec(cloud).unwrap(); + let total: f32 = internal_msg + .try_into_vec() + .unwrap() + .into_iter() + .map(|point: PointXYZ| heavy_computing(&point, 100)) + .sum(); + total > 0.0 +} + +#[cfg(feature = "rayon")] +fn roundtrip_par(cloud: Vec) -> bool { let orig_len = cloud.len(); let internal_msg = PointCloud2Msg::try_from_iter(cloud).unwrap(); let total = internal_msg @@ -85,15 +267,40 @@ fn roundtrip_par(cloud: Vec) -> bool { } #[cfg(feature = "rayon")] -fn roundtrip_filter_par(cloud: Vec) -> bool { +fn roundtrip_par_par(cloud: Vec) -> bool { + let orig_len = cloud.len(); + let internal_msg = PointCloud2Msg::try_from_par_iter(cloud.into_par_iter()).unwrap(); + let total = internal_msg + .try_into_par_iter() + .unwrap() + .collect::>(); + orig_len != total.len() +} + +#[cfg(feature = "rayon")] +fn roundtrip_filter_par(cloud: Vec) -> bool { let orig_len: usize = cloud.len(); let internal_msg = PointCloud2Msg::try_from_iter(cloud).unwrap(); let total = internal_msg .try_into_par_iter() .unwrap() - .filter(|point: &PointXYZ| { - (point.x.powi(2) + point.y.powi(2) + point.z.powi(2)).sqrt() < 1.9 - }) + .filter(|point: &PointXYZ| distance_to_origin(point) < 69.9) + .reduce(PointXYZ::default, |acc, point| PointXYZ { + x: acc.x + point.x, + y: acc.y + point.y, + z: acc.z + point.z, + }); + orig_len == total.x as usize +} + +#[cfg(feature = "rayon")] +fn roundtrip_filter_par_par(cloud: Vec) -> bool { + let orig_len: usize = cloud.len(); + let internal_msg = PointCloud2Msg::try_from_par_iter(cloud.into_par_iter()).unwrap(); + let total = internal_msg + .try_into_par_iter() + .unwrap() + .filter(|point: &PointXYZ| distance_to_origin(point) < 69.9) .reduce(PointXYZ::default, |acc, point| PointXYZ { x: acc.x + point.x, y: acc.y + point.y, @@ -103,61 +310,385 @@ fn roundtrip_filter_par(cloud: Vec) -> bool { } fn roundtrip_benchmark(c: &mut Criterion) { + let cloud_points_16k = generate_random_pointcloud(16_000, f32::MIN / 2.0, f32::MAX / 2.0); + let cloud_points_60k = generate_random_pointcloud(60_000, f32::MIN / 2.0, f32::MAX / 2.0); + let cloud_points_120k = generate_random_pointcloud(120_000, f32::MIN / 2.0, f32::MAX / 2.0); let cloud_points_500k = generate_random_pointcloud(500_000, f32::MIN / 2.0, f32::MAX / 2.0); let cloud_points_1_5m = generate_random_pointcloud(1_500_000, f32::MIN / 2.0, f32::MAX / 2.0); - c.bench_function("roundtrip 500k", |b| { + // 16k points (Velodyne with 16 beams) + + // Moving memory + c.bench_function("16k iter", |b| { + b.iter(|| { + black_box(roundtrip(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("16k iter_par", |b| { + b.iter(|| { + black_box(roundtrip_par(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("16k iter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_par_par(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("16k vec", |b| { + b.iter(|| { + black_box(roundtrip_vec(cloud_points_16k.clone())); + }) + }); + + // Simple distance filter + c.bench_function("16k iter_filter", |b| { + b.iter(|| { + roundtrip_filter(black_box(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("16k filter_par", |b| { + b.iter(|| { + roundtrip_filter_par(black_box(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("16k filter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_filter_par_par(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("16k vec_filter", |b| { + b.iter(|| { + roundtrip_filter_vec(black_box(cloud_points_16k.clone())); + }) + }); + + // Heavy computing + c.bench_function("16k iter_compute", |b| { + b.iter(|| { + roundtrip_computing(black_box(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("16k iter_compute_par", |b| { + b.iter(|| { + roundtrip_computing_par(black_box(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("16k iter_compute_par_par", |b| { + b.iter(|| { + roundtrip_computing_par_par(black_box(cloud_points_16k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("16k vec_compute", |b| { + b.iter(|| { + roundtrip_computing_vec(black_box(cloud_points_16k.clone())); + }) + }); + + // 60k points (Ouster with 64 beams) + + // Moving memory + c.bench_function("60k iter", |b| { + b.iter(|| { + black_box(roundtrip(cloud_points_60k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("60k iter_par", |b| { + b.iter(|| { + black_box(roundtrip_par(cloud_points_60k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("60k iter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_par_par(cloud_points_60k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("60k vec", |b| { + b.iter(|| { + black_box(roundtrip_vec(cloud_points_60k.clone())); + }) + }); + + // 120k points (Ouster with 128 beams) + + // Moving memory + c.bench_function("120k iter", |b| { + b.iter(|| { + black_box(roundtrip(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("120k iter_par", |b| { + b.iter(|| { + black_box(roundtrip_par(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("120k iter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_par_par(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("120k vec", |b| { + b.iter(|| { + black_box(roundtrip_vec(cloud_points_120k.clone())); + }) + }); + + // Simple distance filter + c.bench_function("120k iter_filter", |b| { + b.iter(|| { + roundtrip_filter(black_box(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("120k filter_par", |b| { + b.iter(|| { + roundtrip_filter_par(black_box(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("120k filter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_filter_par_par(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("120k vec_filter", |b| { + b.iter(|| { + roundtrip_filter_vec(black_box(cloud_points_120k.clone())); + }) + }); + + // Heavy computing + c.bench_function("120k iter_compute", |b| { + b.iter(|| { + roundtrip_computing(black_box(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("120k iter_compute_par", |b| { + b.iter(|| { + roundtrip_computing_par(black_box(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("120k iter_compute_par_par", |b| { + b.iter(|| { + roundtrip_computing_par_par(black_box(cloud_points_120k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("120k vec_compute", |b| { + b.iter(|| { + roundtrip_computing_vec(black_box(cloud_points_120k.clone())); + }) + }); + + // 500k points (just to show how it scales) + + // Moving memory + c.bench_function("500k iter", |b| { b.iter(|| { black_box(roundtrip(cloud_points_500k.clone())); }) }); #[cfg(feature = "rayon")] - c.bench_function("roundtrip_par 500k", |b| { + c.bench_function("500k iter_par", |b| { b.iter(|| { black_box(roundtrip_par(cloud_points_500k.clone())); }) }); + #[cfg(feature = "rayon")] + c.bench_function("500k iter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_par_par(cloud_points_500k.clone())); + }) + }); + #[cfg(feature = "derive")] - c.bench_function("roundtrip_vec 500k", |b| { + c.bench_function("500k vec", |b| { b.iter(|| { black_box(roundtrip_vec(cloud_points_500k.clone())); }) }); - c.bench_function("roundtrip_filter 500k", |b| { + // Simple distance filter + c.bench_function("500k iter_filter", |b| { b.iter(|| { roundtrip_filter(black_box(cloud_points_500k.clone())); }) }); #[cfg(feature = "rayon")] - c.bench_function("roundtrip_filter_par 500k", |b| { + c.bench_function("500k filter_par", |b| { b.iter(|| { roundtrip_filter_par(black_box(cloud_points_500k.clone())); }) }); - c.bench_function("roundtrip_filter 1.5m", |b| { + #[cfg(feature = "rayon")] + c.bench_function("500k filter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_filter_par_par(cloud_points_500k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("500k vec_filter", |b| { + b.iter(|| { + roundtrip_filter_vec(black_box(cloud_points_500k.clone())); + }) + }); + + // Heavy computing + c.bench_function("500k iter_compute", |b| { + b.iter(|| { + roundtrip_computing(black_box(cloud_points_500k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("500k iter_compute_par", |b| { + b.iter(|| { + roundtrip_computing_par(black_box(cloud_points_500k.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("500k iter_compute_par_par", |b| { + b.iter(|| { + roundtrip_computing_par_par(black_box(cloud_points_500k.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("500k vec_compute", |b| { + b.iter(|| { + roundtrip_computing_vec(black_box(cloud_points_500k.clone())); + }) + }); + + // 1.5m points (scale of small localmaps in SLAM) + + // Moving memory + c.bench_function("1.5m iter", |b| { + b.iter(|| { + black_box(roundtrip(cloud_points_1_5m.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("1.5m iter_par", |b| { + b.iter(|| { + black_box(roundtrip_par(cloud_points_1_5m.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("1.5m iter_par_par", |b| { + b.iter(|| { + black_box(roundtrip_par_par(cloud_points_1_5m.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("1.5m vec", |b| { + b.iter(|| { + black_box(roundtrip_vec(cloud_points_1_5m.clone())); + }) + }); + + // Simple distance filter + c.bench_function("1.5m iter_filter", |b| { b.iter(|| { roundtrip_filter(black_box(cloud_points_1_5m.clone())); }) }); #[cfg(feature = "rayon")] - c.bench_function("roundtrip_filter_par 1.5m", |b| { + c.bench_function("1.5m iter_par_filter", |b| { b.iter(|| { black_box(roundtrip_filter_par(cloud_points_1_5m.clone())); }) }); + #[cfg(feature = "rayon")] + c.bench_function("1.5m iter_par_par_filter", |b| { + b.iter(|| { + black_box(roundtrip_filter_par_par(cloud_points_1_5m.clone())); + }) + }); + #[cfg(feature = "derive")] - c.bench_function("roundtrip_filter_vec 1.5m", |b| { + c.bench_function("1.5m vec_filter", |b| { b.iter(|| { roundtrip_filter_vec(black_box(cloud_points_1_5m.clone())); }) }); + + // Heavy computing + c.bench_function("1.5m iter_compute", |b| { + b.iter(|| { + roundtrip_computing(black_box(cloud_points_1_5m.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("1.5m iter_compute_par", |b| { + b.iter(|| { + roundtrip_computing_par(black_box(cloud_points_1_5m.clone())); + }) + }); + + #[cfg(feature = "rayon")] + c.bench_function("1.5m iter_compute_par_par", |b| { + b.iter(|| { + roundtrip_computing_par_par(black_box(cloud_points_1_5m.clone())); + }) + }); + + #[cfg(feature = "derive")] + c.bench_function("1.5m vec_compute", |b| { + b.iter(|| { + roundtrip_computing_vec(black_box(cloud_points_1_5m.clone())); + }) + }); } criterion_group!(benches, roundtrip_benchmark); diff --git a/examples/custom_enum_field_filter.rs b/examples/custom_enum_field_filter.rs index 264422b..c86e50c 100644 --- a/examples/custom_enum_field_filter.rs +++ b/examples/custom_enum_field_filter.rs @@ -17,7 +17,8 @@ enum Label { Car, } -// Define a custom point with an enum (not supported by PointCloud2) +// Define a custom point with an enum. +// This is normally not supported by PointCloud2 but we will explain the library how to handle it. #[derive(Debug, PartialEq, Clone, Default)] struct CustomPoint { x: f32, @@ -27,7 +28,7 @@ struct CustomPoint { my_custom_label: Label, } -// For our convenience +// Some convenience functions to convert between the enum and u8. impl From