From 622a24941d4708a45f4194676bac83646c640628 Mon Sep 17 00:00:00 2001 From: rtkartista Date: Wed, 6 May 2026 12:17:43 -0700 Subject: [PATCH 1/2] benchmarks: add KdTree FLANN vs nanoflann benchmark suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3860 (partial — search module benchmark) Adds benchmarks/search/bench_kdtree.cpp covering tree construction, kNN query, and radius search for pcl::search::KdTree (FLANN) vs pcl::search::KdTreeNanoflann (added in 1.15.1 / #6250). Results on Legion 5 / i7-10750H / Ubuntu 24.04 / GCC 13 / Release: - Build 500k pts: 134 ms (FLANN) - kNN k=1: 0.875 us, k=20: 2.34 us (FLANN) - Radius r=5m: 13,049 us (FLANN) Benchmark skips silently if google-benchmark is not installed. Accepts optional PCD path as argv[1], falls back to synthetic cloud. --- benchmarks/CMakeLists.txt | 4 + benchmarks/search/bench_kdtree.cpp | 202 +++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 benchmarks/search/bench_kdtree.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 1934d21e243..c08671b801a 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -39,3 +39,7 @@ PCL_ADD_BENCHMARK(sample_consensus_sac_model_cylinder FILES sample_consensus/sac PCL_ADD_BENCHMARK(search_radius_search FILES search/radius_search.cpp LINK_WITH pcl_io pcl_search pcl_filters ARGUMENTS "${PCL_SOURCE_DIR}/test/table_scene_mug_stereo_textured.pcd") + +PCL_ADD_BENCHMARK(search_kdtree_flann_vs_nanoflann FILES search/bench_kdtree.cpp + LINK_WITH pcl_io pcl_search pcl_kdtree + ARGUMENTS "${PCL_SOURCE_DIR}/test/table_scene_mug_stereo_textured.pcd") \ No newline at end of file diff --git a/benchmarks/search/bench_kdtree.cpp b/benchmarks/search/bench_kdtree.cpp new file mode 100644 index 00000000000..64f28d5d08e --- /dev/null +++ b/benchmarks/search/bench_kdtree.cpp @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Benchmark: pcl::search::KdTree (FLANN) vs pcl::search::KdTreeNanoflann +// Covers: tree construction, kNN query, radius search +// Motivation: PCL 1.15.1 added nanoflann support (#6250); this benchmark +// provides a regression guard and documents expected speedups. +// +// Usage: +// ./search_kdtree_flann_vs_nanoflann # synthetic 100k pts +// ./search_kdtree_flann_vs_nanoflann cloud.pcd # real PCD file + +#include +#include +#include +#include +#include +#include + +#ifdef PCL_HAVE_NANOFLANN +#include +#endif + +#include +#include + +// --------------------------------------------------------------------------- +// Global cloud — loaded once, shared across all benchmarks +// --------------------------------------------------------------------------- + +static pcl::PointCloud::Ptr g_cloud; + +static pcl::PointCloud::Ptr +makeCloud(int N, float range = 100.f, unsigned seed = 42) +{ + auto cloud = std::make_shared>(); + cloud->reserve(N); + std::mt19937 rng(seed); + std::uniform_real_distribution dist(-range, range); + for (int i = 0; i < N; ++i) + cloud->emplace_back(dist(rng), dist(rng), dist(rng)); + cloud->width = N; + cloud->height = 1; + cloud->is_dense = true; + return cloud; +} + +// --------------------------------------------------------------------------- +// FLANN KdTree — construction +// --------------------------------------------------------------------------- + +static void BM_FlannKdTree_Build(benchmark::State& state) +{ + auto cloud = makeCloud(static_cast(state.range(0))); + for (auto _ : state) { + pcl::search::KdTree tree; + tree.setInputCloud(cloud); + benchmark::DoNotOptimize(tree); + } + state.SetItemsProcessed(state.iterations() * state.range(0)); + state.SetLabel("FLANN build"); +} +BENCHMARK(BM_FlannKdTree_Build) + ->Arg(10000)->Arg(100000)->Arg(500000) + ->Unit(benchmark::kMillisecond); + +// --------------------------------------------------------------------------- +// FLANN KdTree — kNN query +// --------------------------------------------------------------------------- + +static void BM_FlannKdTree_kNN(benchmark::State& state) +{ + const int k = static_cast(state.range(0)); + + pcl::search::KdTree tree; + tree.setInputCloud(g_cloud); + + pcl::PointXYZ query(0.f, 0.f, 0.f); + std::vector indices(k); + std::vector dists(k); + + for (auto _ : state) + benchmark::DoNotOptimize(tree.nearestKSearch(query, k, indices, dists)); + + state.SetItemsProcessed(state.iterations()); + state.SetLabel("FLANN kNN"); +} +BENCHMARK(BM_FlannKdTree_kNN) + ->Arg(1)->Arg(5)->Arg(20)->Arg(50) + ->Unit(benchmark::kMicrosecond); + +// --------------------------------------------------------------------------- +// FLANN KdTree — radius search +// --------------------------------------------------------------------------- + +static void BM_FlannKdTree_Radius(benchmark::State& state) +{ + const float r = static_cast(state.range(0)); + + pcl::search::KdTree tree; + tree.setInputCloud(g_cloud); + + pcl::PointXYZ query(0.f, 0.f, 0.f); + std::vector indices; + std::vector dists; + + for (auto _ : state) + benchmark::DoNotOptimize(tree.radiusSearch(query, r, indices, dists)); + + state.SetItemsProcessed(state.iterations()); + state.SetLabel("FLANN radius"); +} +BENCHMARK(BM_FlannKdTree_Radius) + ->Arg(5)->Arg(10)->Arg(20) + ->Unit(benchmark::kMicrosecond); + +// --------------------------------------------------------------------------- +// Nanoflann KdTree — same benchmarks +// --------------------------------------------------------------------------- + +#ifdef PCL_HAVE_NANOFLANN + +static void BM_NanoflannKdTree_Build(benchmark::State& state) +{ + auto cloud = makeCloud(static_cast(state.range(0))); + for (auto _ : state) { + pcl::search::KdTreeNanoflann tree; + tree.setInputCloud(cloud); + benchmark::DoNotOptimize(tree); + } + state.SetItemsProcessed(state.iterations() * state.range(0)); + state.SetLabel("nanoflann build"); +} +BENCHMARK(BM_NanoflannKdTree_Build) + ->Arg(10000)->Arg(100000)->Arg(500000) + ->Unit(benchmark::kMillisecond); + +static void BM_NanoflannKdTree_kNN(benchmark::State& state) +{ + const int k = static_cast(state.range(0)); + + pcl::search::KdTreeNanoflann tree; + tree.setInputCloud(g_cloud); + + pcl::PointXYZ query(0.f, 0.f, 0.f); + std::vector indices(k); + std::vector dists(k); + + for (auto _ : state) + benchmark::DoNotOptimize(tree.nearestKSearch(query, k, indices, dists)); + + state.SetItemsProcessed(state.iterations()); + state.SetLabel("nanoflann kNN"); +} +BENCHMARK(BM_NanoflannKdTree_kNN) + ->Arg(1)->Arg(5)->Arg(20)->Arg(50) + ->Unit(benchmark::kMicrosecond); + +static void BM_NanoflannKdTree_Radius(benchmark::State& state) +{ + const float r = static_cast(state.range(0)); + + pcl::search::KdTreeNanoflann tree; + tree.setInputCloud(g_cloud); + + pcl::PointXYZ query(0.f, 0.f, 0.f); + std::vector indices; + std::vector dists; + + for (auto _ : state) + benchmark::DoNotOptimize(tree.radiusSearch(query, r, indices, dists)); + + state.SetItemsProcessed(state.iterations()); + state.SetLabel("nanoflann radius"); +} +BENCHMARK(BM_NanoflannKdTree_Radius) + ->Arg(5)->Arg(10)->Arg(20) + ->Unit(benchmark::kMicrosecond); + +#endif // PCL_HAVE_NANOFLANN + +// --------------------------------------------------------------------------- +// main — load cloud from argv[1] or fall back to synthetic 100k points +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + if (argc > 1) { + g_cloud = std::make_shared>(); + if (pcl::io::loadPCDFile(argv[1], *g_cloud) < 0) { + std::cerr << "Failed to load " << argv[1] << " — using synthetic cloud\n"; + g_cloud = makeCloud(100000); + } else { + std::cout << "Loaded " << g_cloud->size() << " pts from " << argv[1] << "\n"; + } + } else { + g_cloud = makeCloud(100000); + } + + ::benchmark::Initialize(&argc, argv); + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + return 0; +} \ No newline at end of file From 28ad2cdda892203d05f385513a3a51ed866b6435 Mon Sep 17 00:00:00 2001 From: rtkartista Date: Wed, 6 May 2026 14:11:56 -0700 Subject: [PATCH 2/2] Fix preprocessor directive for NanoFlann support --- benchmarks/search/bench_kdtree.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/search/bench_kdtree.cpp b/benchmarks/search/bench_kdtree.cpp index 64f28d5d08e..328ec97baa3 100644 --- a/benchmarks/search/bench_kdtree.cpp +++ b/benchmarks/search/bench_kdtree.cpp @@ -15,7 +15,7 @@ #include #include -#ifdef PCL_HAVE_NANOFLANN +#ifdef PCL_HAS_NANOFLANN #include #endif @@ -116,7 +116,7 @@ BENCHMARK(BM_FlannKdTree_Radius) // Nanoflann KdTree — same benchmarks // --------------------------------------------------------------------------- -#ifdef PCL_HAVE_NANOFLANN +#ifdef PCL_HAS_NANOFLANN static void BM_NanoflannKdTree_Build(benchmark::State& state) { @@ -175,7 +175,7 @@ BENCHMARK(BM_NanoflannKdTree_Radius) ->Arg(5)->Arg(10)->Arg(20) ->Unit(benchmark::kMicrosecond); -#endif // PCL_HAVE_NANOFLANN +#endif // PCL_HAS_NANOFLANN // --------------------------------------------------------------------------- // main — load cloud from argv[1] or fall back to synthetic 100k points @@ -199,4 +199,4 @@ int main(int argc, char** argv) ::benchmark::RunSpecifiedBenchmarks(); ::benchmark::Shutdown(); return 0; -} \ No newline at end of file +}