Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit 7f42eeb

Browse files
authored
Adds pairwise_point_polygon_distance benchmark (#1131)
This PR separates the `pairwise_point_polygon_distance` benchmark portion of PR #1002. While that PR is only left for nvtx3 experiments. # Original PR description: This PR adds pairwise point polygon distance benchmark. Depends on #998 Point-polygon distance performance can be affected by many factors, because the geometry is complex in nature. I benchmarked these questions: 1. How does the algorithm scales with simple multipolygons? 2. How does it scales with complex multipolygons? ## How does the algorithm scales with simple multipolygons? The benchmark uses the most simple multipolygon, 3 sides per polygon, 0 hole and 1 polygon per multipolygon. Float32 | Num multipolygon | Throughput (#multipolygons / s) | | --- | --- | | 1 | 28060.32971 | | 100 | 2552687.469 | | 10000 | 186044781 | | 1000000 | 1047783101 | | 100000000 | 929537385.2 | Float64 | Num multipolygon | Throughput (#multipolygons / s) | | --- | --- | | 1 | 28296.94817 | | 100 | 2491541.218 | | 10000 | 179379919.5 | | 1000000 | 854678939.9 | | 100000000 | 783364410.7 | ![image](https://user-images.githubusercontent.com/13521008/226502300-c3273d80-5f9f-4d53-b961-a24e64216e9b.png) The chart shows that with simple polygons and simple multipoint (1 point per multipoint), the algorithm scales pretty nicely. Throughput is maxed out at near 1M pairs. ## How does the algorithm scales with complex multipolygons? The benchmark uses a complex multipolygon, 100 edges per ring, 10 holes per polygon and 3 polygons per multipolygon. float32 Num multipolygon | Throughput (#multipolygons / s) -- | -- 1000 | 158713.2377 10000 | 345694.2642 100000 | 382849.058 float64 Num multipolygon | Throughput (#multipolygons / s) -- | -- 1000 | 148727.1246 10000 | 353141.9758 100000 | 386007.3016 ![image](https://user-images.githubusercontent.com/13521008/226502732-0d116db7-6257-4dec-b170-c42b30df9cea.png) The algorithm reaches max throughput at near 10K pairs. About 100X lower than the simple multipolygon example. Authors: - Michael Wang (https://github.com/isVoid) - Mark Harris (https://github.com/harrism) Approvers: - Mark Harris (https://github.com/harrism) URL: #1131
1 parent c6ecbc0 commit 7f42eeb

16 files changed

+269
-42
lines changed

cpp/benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ ConfigureBench(HAUSDORFF_BENCH
8080
distance/hausdorff_benchmark.cpp)
8181

8282
ConfigureNVBench(DISTANCES_BENCH
83+
distance/pairwise_point_polygon_distance.cu
8384
distance/pairwise_linestring_distance.cu)
8485

8586
ConfigureNVBench(QUADTREE_ON_POINTS_BENCH
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright (c) 2023, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <benchmarks/fixture/rmm_pool_raii.hpp>
18+
#include <nvbench/nvbench.cuh>
19+
20+
#include <cuspatial_test/geometry_generator.cuh>
21+
22+
#include <cuspatial/distance.cuh>
23+
#include <cuspatial/geometry/vec_2d.hpp>
24+
25+
#include <rmm/cuda_stream_view.hpp>
26+
#include <rmm/exec_policy.hpp>
27+
28+
using namespace cuspatial;
29+
using namespace cuspatial::test;
30+
31+
template <typename T>
32+
void pairwise_point_polygon_distance_benchmark(nvbench::state& state, nvbench::type_list<T>)
33+
{
34+
// TODO: to be replaced by nvbench fixture once it's ready
35+
cuspatial::rmm_pool_raii rmm_pool;
36+
rmm::cuda_stream_view stream{rmm::cuda_stream_default};
37+
38+
auto const num_pairs{static_cast<std::size_t>(state.get_int64("num_pairs"))};
39+
40+
auto const num_polygons_per_multipolygon{
41+
static_cast<std::size_t>(state.get_int64("num_polygons_per_multipolygon"))};
42+
auto const num_holes_per_polygon{
43+
static_cast<std::size_t>(state.get_int64("num_holes_per_polygon"))};
44+
auto const num_edges_per_ring{static_cast<std::size_t>(state.get_int64("num_edges_per_ring"))};
45+
46+
auto const num_points_per_multipoint{
47+
static_cast<std::size_t>(state.get_int64("num_points_per_multipoint"))};
48+
49+
auto mpoly_generator_param = multipolygon_generator_parameter<T>{
50+
num_pairs, num_polygons_per_multipolygon, num_holes_per_polygon, num_edges_per_ring};
51+
52+
auto mpoint_generator_param = multipoint_generator_parameter<T>{
53+
num_pairs, num_points_per_multipoint, vec_2d<T>{-1, -1}, vec_2d<T>{0, 0}};
54+
55+
auto multipolygons = generate_multipolygon_array<T>(mpoly_generator_param, stream);
56+
auto multipoints = generate_multipoint_array<T>(mpoint_generator_param, stream);
57+
58+
auto distances = rmm::device_vector<T>(num_pairs);
59+
auto out_it = distances.begin();
60+
61+
auto mpoly_view = multipolygons.range();
62+
auto mpoint_view = multipoints.range();
63+
64+
state.add_element_count(num_pairs, "NumPairs");
65+
state.add_element_count(mpoly_generator_param.num_polygons(), "NumPolygons");
66+
state.add_element_count(mpoly_generator_param.num_rings(), "NumRings");
67+
state.add_element_count(mpoly_generator_param.num_coords(), "NumPoints (in mpoly)");
68+
state.add_element_count(static_cast<std::size_t>(mpoly_generator_param.num_coords() *
69+
mpoly_generator_param.num_rings() *
70+
mpoly_generator_param.num_polygons()),
71+
"Multipolygon Complexity");
72+
state.add_element_count(mpoint_generator_param.num_points(), "NumPoints (in multipoints)");
73+
74+
state.add_global_memory_reads<T>(
75+
mpoly_generator_param.num_coords() + mpoint_generator_param.num_points(),
76+
"CoordinatesReadSize");
77+
state.add_global_memory_reads<std::size_t>(
78+
(mpoly_generator_param.num_rings() + 1) + (mpoly_generator_param.num_polygons() + 1) +
79+
(mpoly_generator_param.num_multipolygons + 1) + (mpoint_generator_param.num_multipoints + 1),
80+
"OffsetsDataSize");
81+
82+
state.add_global_memory_writes<T>(num_pairs);
83+
84+
state.exec(nvbench::exec_tag::sync,
85+
[&mpoly_view, &mpoint_view, &out_it, &stream](nvbench::launch& launch) {
86+
pairwise_point_polygon_distance(mpoint_view, mpoly_view, out_it, stream);
87+
});
88+
}
89+
90+
using floating_point_types = nvbench::type_list<float, double>;
91+
92+
// Benchmark scalability with simple multipolygon (3 sides, 0 hole, 1 poly)
93+
NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark,
94+
NVBENCH_TYPE_AXES(floating_point_types))
95+
.set_type_axes_names({"CoordsType"})
96+
.add_int64_axis("num_pairs", {1, 1'00, 10'000, 1'000'000, 100'000'000})
97+
.add_int64_axis("num_polygons_per_multipolygon", {1})
98+
.add_int64_axis("num_holes_per_polygon", {0})
99+
.add_int64_axis("num_edges_per_ring", {3})
100+
.add_int64_axis("num_points_per_multipoint", {1})
101+
.set_name("point_polygon_distance_benchmark_simple_polygon");
102+
103+
// Benchmark scalability with complex multipolygon (100 sides, 10 holes, 3 polys)
104+
NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark,
105+
NVBENCH_TYPE_AXES(floating_point_types))
106+
.set_type_axes_names({"CoordsType"})
107+
.add_int64_axis("num_pairs", {1'000, 10'000, 100'000, 1'000'000})
108+
.add_int64_axis("num_polygons_per_multipolygon", {2})
109+
.add_int64_axis("num_holes_per_polygon", {3})
110+
.add_int64_axis("num_edges_per_ring", {50})
111+
.add_int64_axis("num_points_per_multipoint", {1})
112+
.set_name("point_polygon_distance_benchmark_complex_polygon");
113+
114+
// // Benchmark impact of rings (100K pairs, 1 polygon, 3 sides)
115+
NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark,
116+
NVBENCH_TYPE_AXES(floating_point_types))
117+
.set_type_axes_names({"CoordsType"})
118+
.add_int64_axis("num_pairs", {10'000})
119+
.add_int64_axis("num_polygons_per_multipolygon", {1})
120+
.add_int64_axis("num_holes_per_polygon", {0, 10, 100, 1000})
121+
.add_int64_axis("num_edges_per_ring", {3})
122+
.add_int64_axis("num_points_per_multipoint", {1})
123+
.set_name("point_polygon_distance_benchmark_ring_numbers");
124+
125+
// Benchmark impact of rings (1M pairs, 1 polygon, 0 holes, 3 sides)
126+
NVBENCH_BENCH_TYPES(pairwise_point_polygon_distance_benchmark,
127+
NVBENCH_TYPE_AXES(floating_point_types))
128+
.set_type_axes_names({"CoordsType"})
129+
.add_int64_axis("num_pairs", {100})
130+
.add_int64_axis("num_polygons_per_multipolygon", {1})
131+
.add_int64_axis("num_holes_per_polygon", {0})
132+
.add_int64_axis("num_edges_per_ring", {3})
133+
.add_int64_axis("num_points_per_multipoint", {50, 5'00, 5'000, 50'000, 500'000})
134+
.set_name("point_polygon_distance_benchmark_points_in_multipoint");

cpp/include/cuspatial/detail/nvtx/ranges.hpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, NVIDIA CORPORATION.
2+
* Copyright (c) 2020-2023, NVIDIA CORPORATION.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,17 +20,12 @@
2020

2121
namespace cuspatial {
2222
/**
23-
* @brief Tag type for libcudf's NVTX domain.
23+
* @brief Tag type for libcuspatial's NVTX domain.
2424
*/
2525
struct libcuspatial_domain {
26-
static constexpr char const* name{"libcuspatial"}; ///< Name of the libcudf domain
26+
static constexpr char const* name{"libcuspatial"}; ///< Name of the libcuspatial domain
2727
};
2828

29-
/**
30-
* @brief Alias for an NVTX range in the libcudf domain.
31-
*/
32-
using thread_range = ::nvtx3::domain_thread_range<libcudf_domain>;
33-
3429
} // namespace cuspatial
3530

3631
/**

cpp/include/cuspatial_test/geometry_generator.cuh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#pragma once
1818

19+
#include <cuspatial_test/random.cuh>
1920
#include <cuspatial_test/vector_factories.cuh>
2021

2122
#include <cuspatial/cuda_utils.hpp>
@@ -251,5 +252,60 @@ auto generate_multipolygon_array(multipolygon_generator_parameter<T> params,
251252
std::move(coordinates));
252253
}
253254

255+
/**
256+
* @brief Struct to store the parameters of the multipoint aray
257+
*
258+
* @tparam T Type of the coordinates
259+
*/
260+
template <typename T>
261+
struct multipoint_generator_parameter {
262+
using element_t = T;
263+
264+
std::size_t num_multipoints;
265+
std::size_t num_points_per_multipoints;
266+
vec_2d<T> lower_left;
267+
vec_2d<T> upper_right;
268+
269+
CUSPATIAL_HOST_DEVICE std::size_t num_points()
270+
{
271+
return num_multipoints * num_points_per_multipoints;
272+
}
273+
};
274+
275+
/**
276+
* @brief Helper to generate random multipoints within a range
277+
*
278+
* @tparam T The floating point type for the coordinates
279+
* @param params Parameters to specify for the multipoints
280+
* @param stream The CUDA stream to use for device memory operations and kernel launches
281+
* @return a cuspatial::test::multipoint_array object
282+
*/
283+
template <typename T>
284+
auto generate_multipoint_array(multipoint_generator_parameter<T> params,
285+
rmm::cuda_stream_view stream)
286+
{
287+
rmm::device_uvector<vec_2d<T>> coordinates(params.num_points(), stream);
288+
rmm::device_uvector<std::size_t> offsets(params.num_multipoints + 1, stream);
289+
290+
thrust::sequence(rmm::exec_policy(stream),
291+
offsets.begin(),
292+
offsets.end(),
293+
std::size_t{0},
294+
params.num_points_per_multipoints);
295+
296+
auto engine_x = deterministic_engine(params.num_points());
297+
auto engine_y = deterministic_engine(2 * params.num_points());
298+
299+
auto x_dist = make_uniform_dist(params.lower_left.x, params.upper_right.x);
300+
auto y_dist = make_uniform_dist(params.lower_left.y, params.upper_right.y);
301+
302+
auto point_gen =
303+
point_generator(params.lower_left, params.upper_right, engine_x, engine_y, x_dist, y_dist);
304+
305+
thrust::tabulate(rmm::exec_policy(stream), coordinates.begin(), coordinates.end(), point_gen);
306+
307+
return make_multipoint_array(std::move(offsets), std::move(coordinates));
308+
}
309+
254310
} // namespace test
255311
} // namespace cuspatial

cpp/include/cuspatial_test/random.cuh

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,21 @@ struct value_generator {
154154
template <typename T, typename Generator>
155155
struct point_generator {
156156
using Cart2D = cuspatial::vec_2d<T>;
157-
value_generator<T, Generator> vgen;
158-
159-
point_generator(T lower_bound, T upper_bound, thrust::minstd_rand& engine, Generator gen)
160-
: vgen(lower_bound, upper_bound, engine, gen)
157+
value_generator<T, Generator> vgenx;
158+
value_generator<T, Generator> vgeny;
159+
160+
point_generator(vec_2d<T> lower_left,
161+
vec_2d<T> upper_right,
162+
thrust::minstd_rand& engine_x,
163+
thrust::minstd_rand& engine_y,
164+
Generator gen_x,
165+
Generator gen_y)
166+
: vgenx(lower_left.x, upper_right.x, engine_x, gen_x),
167+
vgeny(lower_left.y, upper_right.y, engine_y, gen_y)
161168
{
162169
}
163170

164-
__device__ Cart2D operator()(size_t n) { return {vgen(n), vgen(n)}; }
171+
__device__ Cart2D operator()(size_t n) { return {vgenx(n), vgeny(n)}; }
165172
};
166173

167174
/**

cpp/include/cuspatial_test/vector_factories.cuh

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,22 @@ auto make_multilinestring_array(std::initializer_list<std::size_t> geometry_inl,
309309
template <typename GeometryArray, typename CoordinateArray>
310310
class multipoint_array {
311311
public:
312-
multipoint_array(GeometryArray geometry_offsets_array, CoordinateArray coordinate_array)
312+
using geometry_t = typename GeometryArray::value_type;
313+
using coord_t = typename CoordinateArray::value_type;
314+
315+
multipoint_array(thrust::device_vector<geometry_t> geometry_offsets_array,
316+
thrust::device_vector<coord_t> coordinate_array)
313317
: _geometry_offsets(geometry_offsets_array), _coordinates(coordinate_array)
314318
{
315319
}
316320

321+
multipoint_array(rmm::device_uvector<geometry_t>&& geometry_offsets_array,
322+
rmm::device_uvector<coord_t>&& coordinate_array)
323+
: _geometry_offsets(std::move(geometry_offsets_array)),
324+
_coordinates(std::move(coordinate_array))
325+
{
326+
}
327+
317328
/// Return the number of multipoints
318329
auto size() { return _geometry_offsets.size() - 1; }
319330

@@ -337,27 +348,33 @@ class multipoint_array {
337348
* coordinates
338349
*/
339350
template <typename GeometryRange, typename CoordRange>
340-
auto make_multipoints_array(GeometryRange geometry_inl, CoordRange coordinates_inl)
351+
auto make_multipoint_array(GeometryRange geometry_inl, CoordRange coordinates_inl)
341352
{
342-
return multipoint_array{make_device_vector(geometry_inl), make_device_vector(coordinates_inl)};
353+
using IndexType = typename GeometryRange::value_type;
354+
using CoordType = typename CoordRange::value_type;
355+
using DeviceIndexVector = thrust::device_vector<IndexType>;
356+
using DeviceCoordVector = thrust::device_vector<CoordType>;
357+
358+
return multipoint_array<DeviceIndexVector, DeviceCoordVector>{
359+
make_device_vector(geometry_inl), make_device_vector(coordinates_inl)};
343360
}
344361

345362
/**
346363
* @brief Factory method to construct multipoint array from initializer list of multipoints.
347364
*
348365
* Example: Construct an array of 2 multipoints, each with 2, 0, 1 points:
349366
* using P = vec_2d<float>;
350-
* make_multipoints_array({{P{0.0, 1.0}, P{2.0, 0.0}}, {}, {P{3.0, 4.0}}});
367+
* make_multipoint_array({{P{0.0, 1.0}, P{2.0, 0.0}}, {}, {P{3.0, 4.0}}});
351368
*
352369
* Example: Construct an empty multilinestring array:
353-
* make_multipoints_array<float>({}); // Explicit parameter required to deduce type.
370+
* make_multipoint_array<float>({}); // Explicit parameter required to deduce type.
354371
*
355372
* @tparam T Type of coordinate
356373
* @param inl List of multipoints
357374
* @return multipoints_array object
358375
*/
359376
template <typename T>
360-
auto make_multipoints_array(std::initializer_list<std::initializer_list<vec_2d<T>>> inl)
377+
auto make_multipoint_array(std::initializer_list<std::initializer_list<vec_2d<T>>> inl)
361378
{
362379
std::vector<std::size_t> offsets{0};
363380
std::transform(inl.begin(), inl.end(), std::back_inserter(offsets), [](auto multipoint) {
@@ -371,8 +388,20 @@ auto make_multipoints_array(std::initializer_list<std::initializer_list<vec_2d<T
371388
return init;
372389
});
373390

374-
return multipoint_array{rmm::device_vector<std::size_t>(offsets),
375-
rmm::device_vector<vec_2d<T>>(coordinates)};
391+
return multipoint_array<rmm::device_vector<std::size_t>, rmm::device_vector<vec_2d<T>>>{
392+
rmm::device_vector<std::size_t>(offsets), rmm::device_vector<vec_2d<T>>(coordinates)};
393+
}
394+
395+
/**
396+
* @brief Factory method to construct multipoint array by moving the offsets and coordinates from
397+
* `rmm::device_uvector`.
398+
*/
399+
template <typename IndexType, typename T>
400+
auto make_multipoint_array(rmm::device_uvector<IndexType> geometry_offsets,
401+
rmm::device_uvector<vec_2d<T>> coords)
402+
{
403+
return multipoint_array<rmm::device_uvector<std::size_t>, rmm::device_uvector<vec_2d<T>>>{
404+
std::move(geometry_offsets), std::move(coords)};
376405
}
377406

378407
} // namespace test

cpp/tests/distance/point_distance_test.cu

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
#include <cuspatial_test/vector_equality.hpp>
18-
1917
#include <cuspatial_test/random.cuh>
18+
#include <cuspatial_test/vector_equality.hpp>
2019

2120
#include <cuspatial/distance.cuh>
2221
#include <cuspatial/error.hpp>
@@ -59,7 +58,8 @@ struct PairwisePointDistanceTest : public ::testing::Test {
5958
{
6059
auto engine = cuspatial::test::deterministic_engine(0);
6160
auto uniform = cuspatial::test::make_normal_dist<T>(0.0, 1.0);
62-
auto pgen = cuspatial::test::point_generator(T{0.0}, T{1.0}, engine, uniform);
61+
auto pgen = cuspatial::test::point_generator(
62+
vec_2d<T>{0.0, 0.0}, vec_2d<T>{1.0, 1.0}, engine, engine, uniform, uniform);
6363
rmm::device_vector<vec_2d<T>> points(num_points);
6464
auto counting_iter = thrust::make_counting_iterator(seed);
6565
thrust::transform(

cpp/tests/distance/point_polygon_distance_test.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ struct PairwisePointPolygonDistanceTest : public ::testing::Test {
6161
std::vector<vec_2d<T>> const& multipolygon_coordinates,
6262
std::initializer_list<T> expected)
6363
{
64-
auto d_multipoints = make_multipoints_array(multipoints);
64+
auto d_multipoints = make_multipoint_array(multipoints);
6565
auto d_multipolygons = make_multipolygon_array(
6666
range{multipolygon_geometry_offsets.begin(), multipolygon_geometry_offsets.end()},
6767
range{multipolygon_part_offsets.begin(), multipolygon_part_offsets.end()},

cpp/tests/equality/pairwise_multipoint_equals_count_test.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ struct PairwiseMultipointEqualsCountTest : public BaseFixture {
3232
std::initializer_list<std::initializer_list<vec_2d<T>>> rhs_coordinates,
3333
std::initializer_list<uint32_t> expected)
3434
{
35-
auto larray = make_multipoints_array(lhs_coordinates);
36-
auto rarray = make_multipoints_array(rhs_coordinates);
35+
auto larray = make_multipoint_array(lhs_coordinates);
36+
auto rarray = make_multipoint_array(rhs_coordinates);
3737

3838
auto lhs = larray.range();
3939
auto rhs = rarray.range();

0 commit comments

Comments
 (0)