mirror of https://github.com/CGAL/cgal
Merge remote-tracking branch 'mine/Spatial_searching-Parallelize_kd_tree_build-GF' into Spatial_searching-Parallelize_kd_tree_build-GF
This commit is contained in:
commit
95b9f05a28
|
|
@ -161,9 +161,9 @@ private:
|
|||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
if (lower_grid == nullptr)
|
||||
neighborhood = new Neighborhood (input, point_map);
|
||||
neighborhood = new Neighborhood (input, point_map, ConcurrencyTag());
|
||||
else
|
||||
neighborhood = new Neighborhood (input, point_map, voxel_size);
|
||||
neighborhood = new Neighborhood (input, point_map, voxel_size, ConcurrencyTag());
|
||||
t.stop();
|
||||
|
||||
if (lower_grid == nullptr)
|
||||
|
|
|
|||
|
|
@ -173,12 +173,32 @@ public:
|
|||
/*!
|
||||
\brief Constructs a neighborhood object based on the input range.
|
||||
|
||||
\tparam ConcurrencyTag enables sequential versus parallel
|
||||
algorithm. Possible values are `Sequential_tag`, `Parallel_tag`,
|
||||
and `Parallel_if_available_tag`. If no tag is provided,
|
||||
`Parallel_if_available_tag` is used.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
*/
|
||||
template <typename ConcurrencyTag>
|
||||
Point_set_neighborhood (const PointRange& input,
|
||||
PointMap point_map)
|
||||
PointMap point_map,
|
||||
const ConcurrencyTag&)
|
||||
: m_tree (nullptr)
|
||||
{
|
||||
init<ConcurrencyTag> (input, point_map);
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Point_set_neighborhood (const PointRange& input, PointMap point_map)
|
||||
: m_tree (nullptr)
|
||||
{
|
||||
init<Parallel_if_available_tag> (input, point_map);
|
||||
}
|
||||
|
||||
template <typename ConcurrencyTag>
|
||||
void init (const PointRange& input, PointMap point_map)
|
||||
{
|
||||
My_point_property_map pmap (&input, point_map);
|
||||
m_tree = new Tree (boost::counting_iterator<boost::uint32_t> (0),
|
||||
|
|
@ -186,8 +206,9 @@ public:
|
|||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_distance = Distance (pmap);
|
||||
m_tree->build();
|
||||
m_tree->template build<ConcurrencyTag>();
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Constructs a simplified neighborhood object based on the input range.
|
||||
|
|
@ -197,14 +218,36 @@ public:
|
|||
present in one cell, only the point closest to the centroid of
|
||||
this subset is used.
|
||||
|
||||
\tparam ConcurrencyTag enables sequential versus parallel
|
||||
algorithm. Possible values are `Sequential_tag`, `Parallel_tag`,
|
||||
and `Parallel_if_available_tag`. If no tag is provided,
|
||||
`Parallel_if_available_tag` is used.
|
||||
|
||||
\param input input range.
|
||||
\param point_map property map to access the input points.
|
||||
\param voxel_size size of the cells of the 3D grid used for simplification.
|
||||
*/
|
||||
template <typename ConcurrencyTag>
|
||||
Point_set_neighborhood (const PointRange& input,
|
||||
PointMap point_map,
|
||||
float voxel_size,
|
||||
const ConcurrencyTag&)
|
||||
: m_tree (nullptr)
|
||||
{
|
||||
init<ConcurrencyTag> (input, point_map, voxel_size);
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Point_set_neighborhood (const PointRange& input,
|
||||
PointMap point_map,
|
||||
float voxel_size)
|
||||
: m_tree (nullptr)
|
||||
{
|
||||
init<Parallel_if_available_tag> (input, point_map, voxel_size);
|
||||
}
|
||||
|
||||
template <typename ConcurrencyTag>
|
||||
void init (const PointRange& input, PointMap point_map, float voxel_size)
|
||||
{
|
||||
// First, simplify
|
||||
std::vector<boost::uint32_t> indices;
|
||||
|
|
@ -215,8 +258,9 @@ public:
|
|||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_distance = Distance (pmap);
|
||||
m_tree->build();
|
||||
m_tree->template build<ConcurrencyTag>();
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/// @}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ in a dynamically allocated array (e.g., `Epick_d` with dynamic
|
|||
dimension) — we says "to a lesser extent" because the points
|
||||
are re-created by the kd-tree in a cache-friendly order after its construction,
|
||||
so the coordinates are more likely to be stored in a near-optimal order on the
|
||||
heap. When EnablePointsCache` is set to `Tag_true`, the points
|
||||
heap. When `EnablePointsCache` is set to `Tag_true`, the points
|
||||
coordinates will be cached in an optimal way. This will
|
||||
increase memory consumption but provide better search performance.
|
||||
See also the `GeneralDistance` and `FuzzyQueryItem` concepts for
|
||||
|
|
@ -115,7 +115,17 @@ at the first call to a query or removal member function. You can call
|
|||
`build()` explicitly to ensure that the next call to
|
||||
query functions will not trigger the reconstruction of the
|
||||
data structure.
|
||||
|
||||
\tparam ConcurrencyTag enables sequential versus parallel
|
||||
algorithm. Possible values are `Sequential_tag`, `Parallel_tag`, and
|
||||
`Parallel_if_available_tag`. This template parameter is optional:
|
||||
calling `build()` without specifying the concurrency tag will result
|
||||
in `Sequential_tag` being used. If `build()` is not called by the user
|
||||
but called implicitly at the first call to a query or removal member
|
||||
function, `Sequential_tag` is also used.
|
||||
|
||||
*/
|
||||
template <typename ConcurrencyTag>
|
||||
void build();
|
||||
|
||||
/*!
|
||||
|
|
@ -147,14 +157,14 @@ template <class InputIterator> void insert(InputIterator first, InputIterator be
|
|||
/*!
|
||||
Removes the point `p` from the `k-d` tree. It uses `equal_to_p` to identify
|
||||
the point after locating it, which can matter in particular when 2 points are
|
||||
in the same place. `Identify_point` is a unary functor that takes a `Point_d`
|
||||
in the same place. `IdentifyPoint` is a unary functor that takes a `Point_d`
|
||||
and returns a `bool`. This is a limited and naive implementation that does not
|
||||
rebalance the tree. On the other hand, the tree remains valid and ready for
|
||||
queries. If the internal data structure is not already built, for instance
|
||||
because the last operation was an insertion, it first calls `build()`.
|
||||
*/
|
||||
template<class Identify_point>
|
||||
void remove(Point_d p, Identify_point equal_to_p);
|
||||
template<class IdentifyPoint>
|
||||
void remove(Point_d p, IdentifyPoint identify_point);
|
||||
|
||||
/*!
|
||||
Removes point `p`, calling the 2-argument function `remove()` with a functor
|
||||
|
|
|
|||
|
|
@ -407,6 +407,23 @@ higher dimensions.
|
|||
|
||||
\cgalExample{Spatial_searching/splitter_worst_cases.cpp}
|
||||
|
||||
\subsection Spatial_searchingExampleParallel Example for Parallel Neighbor Search
|
||||
|
||||
In order to speed-up the construction of the `kd` tree, the child
|
||||
branches of each internal node can be computed in parallel, by calling
|
||||
`Kd_tree::build<CGAL::Parallel_tag>()`. On a quad-core processor, the
|
||||
parallel construction is experimentally 2 to 3 times faster than the
|
||||
sequential version, depending on the point cloud. The parallel version
|
||||
requires the executable to be linked against the <a href="https://www.threadingbuildingblocks.org">Intel TBB library</a>.
|
||||
|
||||
One query on the `kd` tree is purely sequential, but several queries
|
||||
can be done in parallel.
|
||||
|
||||
The following example shows how to build the `kd` tree in parallel and
|
||||
how to perform parallel queries:
|
||||
|
||||
\cgalExample{Spatial_searching/parallel_kdtree.cpp}
|
||||
|
||||
\section Performance Performance
|
||||
|
||||
\subsection OrthogonalPerfomance Performance of the Orthogonal Search
|
||||
|
|
@ -415,21 +432,33 @@ We took the gargoyle data set (Surface) from aim\@shape, and generated the same
|
|||
We then consider three scenarios as data/queries.
|
||||
The data set contains 800K points. For each query point we compute the K=10,20,30 closest points, with the default splitter and for the bucket size 10 (default) and 20.
|
||||
|
||||
The results were produced with the release 4.6 of \cgal, on an Intel i7 2.7 Ghz
|
||||
laptop with 16 GB RAM, compiled with Visual C++ 2012 with the /O2 option.
|
||||
|
||||
The values are the average of ten tests each.
|
||||
The results were produced with the release 5.0 of \cgal, on an Intel i7 2.3 Ghz
|
||||
laptop with 16 GB RAM, compiled with CLang++ 6 with the O3 option.
|
||||
|
||||
The values are the average of ten tests each. We show timings in seconds for both the building of the tree and the queries.
|
||||
|
||||
<center>
|
||||
k | bucket size | Surface/Surface | Surface/Random | Random/Random
|
||||
--|------------:|-----------------:|---------------:|----------------:
|
||||
10| 10 | 0.89 | 11.48 | 2.63
|
||||
10| 20 | 0.89 | 9.80 | 2.25
|
||||
20| 10 | 1.60 | 13.41 | 4.06
|
||||
20| 20 | 1.59 | 11.62 | 3.46
|
||||
30| 10 | 2.35 | 15.52 | 5.42
|
||||
30| 20 | 2.33 | 13.50 | 4.61
|
||||
k | bucket size | Surface Build | Random Build | Surface/Surface | Surface/Random | Random/Random
|
||||
--|------------:|--------------:|-------------:|-----------------:|---------------:|----------------:
|
||||
10| 10 | 0.17 | 0.31 | 1.13 | 15.35 | 3.40
|
||||
10| 20 | 0.14 | 0.28 | 1.09 | 12.28 | 3.00
|
||||
20| 10 | (see above) | (see above) | 1.88 | 18.25 | 5.39
|
||||
20| 20 | (see above) | (see above) | 1.81 | 14.99 | 4.51
|
||||
30| 10 | (see above) | (see above) | 2.87 | 22.62 | 7.07
|
||||
30| 20 | (see above) | (see above) | 2.66 | 18.39 | 5.68
|
||||
</center>
|
||||
|
||||
The same experiment is done using the parallel version of the tree building algorithm, and performing the queries in parallel too:
|
||||
|
||||
<center>
|
||||
k | bucket size | Surface Build | Random Build | Surface/Surface | Surface/Random | Random/Random
|
||||
--|------------:|--------------:|-------------:|-----------------:|---------------:|----------------:
|
||||
10| 10 | 0.07 | 0.12 | 0.24 | 3.52 | 0.66
|
||||
10| 20 | 0.06 | 0.12 | 0.22 | 2.87 | 0.57
|
||||
20| 10 | (see above) | (see above) | 0.41 | 4.28 | 1.02
|
||||
20| 20 | (see above) | (see above) | 0.38 | 3.43 | 0.88
|
||||
30| 10 | (see above) | (see above) | 0.58 | 4.90 | 1.44
|
||||
30| 20 | (see above) | (see above) | 0.60 | 4.28 | 1.28
|
||||
</center>
|
||||
|
||||
\cgalFigureBegin{Spatial_searchingfigbenchmark,gargoyle.png}
|
||||
|
|
@ -520,9 +549,12 @@ additional requirements when using such a cache.
|
|||
|
||||
\section Spatial_searchingImplementationHistory Implementation History
|
||||
|
||||
The initial implementation of this package was done by Hans Tangelder and
|
||||
Andreas Fabri. It was optimized in speed and memory consumption by Markus
|
||||
Overtheil during an internship at GeometryFactory in 2014.
|
||||
The initial implementation of this package was done by Hans Tangelder
|
||||
and Andreas Fabri. It was optimized in speed and memory consumption by
|
||||
Markus Overtheil during an internship at GeometryFactory in 2014. The
|
||||
`EnablePointsCache` feature was introduced by Clément Jamin in 2019.
|
||||
The parallel `kd` tree build function was introduced by Simon Giraudot
|
||||
in 2020.
|
||||
|
||||
*/
|
||||
} /* namespace CGAL */
|
||||
|
|
|
|||
|
|
@ -18,4 +18,5 @@
|
|||
\example Spatial_searching/weighted_Minkowski_distance.cpp
|
||||
\example Spatial_searching/splitter_worst_cases.cpp
|
||||
\example Spatial_searching/searching_sphere_orthogonally.cpp
|
||||
\example Spatial_searching/parallel_kdtree.cpp
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -76,3 +76,12 @@ else()
|
|||
message(STATUS "will not be compiled as they use CGAL::Epick_d which requires the Eigen library.")
|
||||
|
||||
endif()
|
||||
|
||||
find_package( TBB QUIET )
|
||||
if(TBB_FOUND)
|
||||
create_single_source_cgal_program( "parallel_kdtree.cpp" )
|
||||
cgal_target_use_TBB(parallel_kdtree)
|
||||
else()
|
||||
message(STATUS "parallel_kdtree.cpp requires TBB and will not be compiled")
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/point_generators_3.h>
|
||||
#include <CGAL/Orthogonal_k_neighbor_search.h>
|
||||
#include <CGAL/Search_traits_3.h>
|
||||
|
||||
#include <tbb/blocked_range.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
using Kernel = CGAL::Simple_cartesian<double>;
|
||||
using Point_3 = Kernel::Point_3;
|
||||
|
||||
using Traits = CGAL::Search_traits_3<Kernel>;
|
||||
using Neighbor_search = CGAL::Orthogonal_k_neighbor_search<Traits>;
|
||||
using Tree = Neighbor_search::Tree;
|
||||
using Point_with_distance = Neighbor_search::Point_with_transformed_distance;
|
||||
|
||||
using Generator = CGAL::Random_points_in_sphere_3<Point_3>;
|
||||
|
||||
int main()
|
||||
{
|
||||
const unsigned int N = 1000;
|
||||
const unsigned int k = 6;
|
||||
|
||||
// Generate N points in a sphere
|
||||
std::vector<Point_3> points;
|
||||
points.reserve (N);
|
||||
Generator generator;
|
||||
for (unsigned int i = 0; i < N; ++ i)
|
||||
points.push_back (*(generator++));
|
||||
|
||||
// Build tree in parallel
|
||||
Tree tree(points.begin(), points.end());
|
||||
tree.build<CGAL::Parallel_tag>();
|
||||
|
||||
// Query tree in parallel
|
||||
std::vector<std::vector<Point_3> > neighbors (points.size());
|
||||
tbb::parallel_for (tbb::blocked_range<std::size_t> (0, points.size()),
|
||||
[&](const tbb::blocked_range<std::size_t>& r)
|
||||
{
|
||||
for (std::size_t s = r.begin(); s != r.end(); ++ s)
|
||||
{
|
||||
// Neighbor search can be instantiated from
|
||||
// several threads at the same time
|
||||
Neighbor_search search (tree, points[s], k);
|
||||
neighbors[s].reserve(k);
|
||||
|
||||
// neighbor search returns a set of pair of
|
||||
// point and distance <Point_3,FT>, here we
|
||||
// keep the points only
|
||||
for (const Point_with_distance& pwd : search)
|
||||
neighbors[s].push_back (pwd.first);
|
||||
}
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
#include <CGAL/Splitters.h>
|
||||
#include <CGAL/internal/Get_dimension_tag.h>
|
||||
|
||||
#include <deque>
|
||||
#include <boost/container/deque.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
|
|
@ -35,6 +34,30 @@
|
|||
#include <CGAL/mutex.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
For building the KD Tree in parallel, TBB is needed. If TBB is
|
||||
linked, the internal structures `deque` will be replaced by
|
||||
`tbb::concurrent_vector`, even if the KD Tree is built in sequential
|
||||
mode (this is to avoid changing the type of the KD Tree when
|
||||
changing the concurrency mode of `build()`).
|
||||
|
||||
Experimentally, using the `tbb::concurrent_vector` in sequential
|
||||
mode does not trigger any loss of performance, so from a user's
|
||||
point of view, it should be transparent.
|
||||
|
||||
However, in case one wants to compile the KD Tree *without using TBB
|
||||
structure even though CGAL is linked with TBB*, the macro
|
||||
`CGAL_DISABLE_TBB_STRUCTURE_IN_KD_TREE` can be defined. In that
|
||||
case, even if TBB is linked, the standard `deque` will be used
|
||||
internally. Note that of course, in that case, parallel build will
|
||||
be disabled.
|
||||
*/
|
||||
#if defined(CGAL_LINKED_WITH_TBB) && !defined(CGAL_DISABLE_TBB_STRUCTURE_IN_KD_TREE)
|
||||
# include <tbb/parallel_invoke.h>
|
||||
# include <tbb/concurrent_vector.h>
|
||||
# define CGAL_TBB_STRUCTURE_IN_KD_TREE
|
||||
#endif
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
//template <class SearchTraits, class Splitter_=Median_of_rectangle<SearchTraits>, class UseExtendedNode = Tag_true >
|
||||
|
|
@ -77,14 +100,13 @@ public:
|
|||
typedef EnablePointsCache Enable_points_cache;
|
||||
|
||||
private:
|
||||
|
||||
SearchTraits traits_;
|
||||
Splitter split;
|
||||
|
||||
|
||||
// wokaround for https://svn.boost.org/trac/boost/ticket/9332
|
||||
#if (_MSC_VER == 1800) && (BOOST_VERSION == 105500)
|
||||
std::deque<Internal_node> internal_nodes;
|
||||
std::deque<Leaf_node> leaf_nodes;
|
||||
#if defined(CGAL_TBB_STRUCTURE_IN_KD_TREE)
|
||||
tbb::concurrent_vector<Internal_node> internal_nodes;
|
||||
tbb::concurrent_vector<Leaf_node> leaf_nodes;
|
||||
#else
|
||||
boost::container::deque<Internal_node> internal_nodes;
|
||||
boost::container::deque<Leaf_node> leaf_nodes;
|
||||
|
|
@ -119,7 +141,6 @@ private:
|
|||
: traits_(tree.traits_),built_(tree.built_),dim_(-1)
|
||||
{};
|
||||
|
||||
|
||||
// Instead of the recursive construction of the tree in the class Kd_tree_node
|
||||
// we do this in the tree class. The advantage is that we then can optimize
|
||||
// the allocation of the nodes.
|
||||
|
|
@ -128,50 +149,69 @@ private:
|
|||
Node_handle
|
||||
create_leaf_node(Point_container& c)
|
||||
{
|
||||
Leaf_node node(true , static_cast<unsigned int>(c.size()));
|
||||
Leaf_node node(static_cast<unsigned int>(c.size()));
|
||||
std::ptrdiff_t tmp = c.begin() - data.begin();
|
||||
node.data = pts.begin() + tmp;
|
||||
|
||||
leaf_nodes.push_back(node);
|
||||
Leaf_node_handle nh = &leaf_nodes.back();
|
||||
|
||||
|
||||
return nh;
|
||||
#ifdef CGAL_TBB_STRUCTURE_IN_KD_TREE
|
||||
return &*(leaf_nodes.push_back(node));
|
||||
#else
|
||||
leaf_nodes.emplace_back (node);
|
||||
return &(leaf_nodes.back());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// The internal node
|
||||
|
||||
Node_handle
|
||||
create_internal_node(Point_container& c, const Tag_true&)
|
||||
Node_handle new_internal_node()
|
||||
{
|
||||
return create_internal_node_use_extension(c);
|
||||
#ifdef CGAL_TBB_STRUCTURE_IN_KD_TREE
|
||||
return &*(internal_nodes.push_back(Internal_node()));
|
||||
#else
|
||||
internal_nodes.emplace_back ();
|
||||
return &(internal_nodes.back());
|
||||
#endif
|
||||
}
|
||||
|
||||
Node_handle
|
||||
create_internal_node(Point_container& c, const Tag_false&)
|
||||
{
|
||||
return create_internal_node(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: Similiar to the leaf_init function above, a part of the code should be
|
||||
// moved to a the class Kd_tree_node.
|
||||
// It is not proper yet, but the goal was to see if there is
|
||||
// a potential performance gain through the Compact_container
|
||||
Node_handle
|
||||
create_internal_node_use_extension(Point_container& c)
|
||||
template <typename ConcurrencyTag>
|
||||
void
|
||||
create_internal_node(Node_handle n, Point_container& c, const ConcurrencyTag& tag)
|
||||
{
|
||||
Internal_node node(false);
|
||||
internal_nodes.push_back(node);
|
||||
Internal_node_handle nh = &internal_nodes.back();
|
||||
Internal_node_handle nh = static_cast<Internal_node_handle>(n);
|
||||
CGAL_assertion (nh != nullptr);
|
||||
|
||||
Separator sep;
|
||||
Point_container c_low(c.dimension(),traits_);
|
||||
split(sep, c, c_low);
|
||||
nh->set_separator(sep);
|
||||
|
||||
handle_extended_node (nh, c, c_low, UseExtendedNode());
|
||||
|
||||
if (try_parallel_internal_node_creation (nh, c, c_low, tag))
|
||||
return;
|
||||
|
||||
if (c_low.size() > split.bucket_size())
|
||||
{
|
||||
nh->lower_ch = new_internal_node();
|
||||
create_internal_node (nh->lower_ch, c_low, tag);
|
||||
}
|
||||
else
|
||||
nh->lower_ch = create_leaf_node(c_low);
|
||||
|
||||
if (c.size() > split.bucket_size())
|
||||
{
|
||||
nh->upper_ch = new_internal_node();
|
||||
create_internal_node (nh->upper_ch, c, tag);
|
||||
}
|
||||
else
|
||||
nh->upper_ch = create_leaf_node(c);
|
||||
}
|
||||
|
||||
void handle_extended_node (Internal_node_handle nh, Point_container& c, Point_container& c_low, const Tag_true&)
|
||||
{
|
||||
int cd = nh->cutting_dimension();
|
||||
if(!c_low.empty()){
|
||||
nh->lower_low_val = c_low.tight_bounding_box().min_coord(cd);
|
||||
|
|
@ -192,56 +232,45 @@ private:
|
|||
|
||||
CGAL_assertion(nh->cutting_value() >= nh->lower_low_val);
|
||||
CGAL_assertion(nh->cutting_value() <= nh->upper_high_val);
|
||||
|
||||
if (c_low.size() > split.bucket_size()){
|
||||
nh->lower_ch = create_internal_node_use_extension(c_low);
|
||||
}else{
|
||||
nh->lower_ch = create_leaf_node(c_low);
|
||||
}
|
||||
if (c.size() > split.bucket_size()){
|
||||
nh->upper_ch = create_internal_node_use_extension(c);
|
||||
}else{
|
||||
nh->upper_ch = create_leaf_node(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return nh;
|
||||
}
|
||||
|
||||
inline void handle_extended_node (Internal_node_handle, Point_container&, Point_container&, const Tag_false&) { }
|
||||
|
||||
// Note also that I duplicated the code to get rid if the if's for
|
||||
// the boolean use_extension which was constant over the construction
|
||||
Node_handle
|
||||
create_internal_node(Point_container& c)
|
||||
inline bool try_parallel_internal_node_creation (Internal_node_handle, Point_container&,
|
||||
Point_container&, const Sequential_tag&)
|
||||
{
|
||||
Internal_node node(false);
|
||||
internal_nodes.push_back(node);
|
||||
Internal_node_handle nh = &internal_nodes.back();
|
||||
Separator sep;
|
||||
|
||||
Point_container c_low(c.dimension(),traits_);
|
||||
split(sep, c, c_low);
|
||||
nh->set_separator(sep);
|
||||
|
||||
if (c_low.size() > split.bucket_size()){
|
||||
nh->lower_ch = create_internal_node(c_low);
|
||||
}else{
|
||||
nh->lower_ch = create_leaf_node(c_low);
|
||||
}
|
||||
if (c.size() > split.bucket_size()){
|
||||
nh->upper_ch = create_internal_node(c);
|
||||
}else{
|
||||
nh->upper_ch = create_leaf_node(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return nh;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CGAL_TBB_STRUCTURE_IN_KD_TREE
|
||||
|
||||
inline bool try_parallel_internal_node_creation (Internal_node_handle nh, Point_container& c,
|
||||
Point_container& c_low, const Parallel_tag& tag)
|
||||
{
|
||||
/*
|
||||
The two child branches are computed in parallel if and only if:
|
||||
|
||||
* both branches lead to internal nodes (if at least one branch
|
||||
is a leaf, it's useless)
|
||||
|
||||
* the current number of points is sufficiently high to be worth
|
||||
the cost of launching new threads. Experimentally, using 10
|
||||
times the bucket size as a limit gives the best timings.
|
||||
*/
|
||||
if (c_low.size() > split.bucket_size() && c.size() > split.bucket_size()
|
||||
&& (c_low.size() + c.size() > 10 * split.bucket_size()))
|
||||
{
|
||||
nh->lower_ch = new_internal_node();
|
||||
nh->upper_ch = new_internal_node();
|
||||
tbb::parallel_invoke (std::bind (&Self::create_internal_node<Parallel_tag>, this, nh->lower_ch, std::ref(c_low), std::cref(tag)),
|
||||
std::bind (&Self::create_internal_node<Parallel_tag>, this, nh->upper_ch, std::ref(c), std::cref(tag)));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -261,6 +290,32 @@ public:
|
|||
return pts.empty();
|
||||
}
|
||||
|
||||
void build()
|
||||
{
|
||||
build<Sequential_tag>();
|
||||
}
|
||||
|
||||
/*
|
||||
Note about parallel `build()`. Several different strategies have
|
||||
been tried, among which:
|
||||
|
||||
* keeping the `deque` and using mutex structures to secure the
|
||||
insertions in them
|
||||
* using free stand-alone pointers generated with `new` instead of
|
||||
pushing elements in a container
|
||||
* using a global `tbb::task_group` to handle the internal node
|
||||
computations
|
||||
* using one `tbb::task_group` per internal node to handle the
|
||||
internal node computations
|
||||
|
||||
Experimentally, the options giving the best timings is the one
|
||||
kept, namely:
|
||||
|
||||
* nodes are stored in `tbb::concurrent_vector` structures
|
||||
* the parallel computations are launched using
|
||||
`tbb::parallel_invoke`
|
||||
*/
|
||||
template <typename ConcurrencyTag>
|
||||
void
|
||||
build()
|
||||
{
|
||||
|
|
@ -277,12 +332,19 @@ public:
|
|||
for(unsigned int i = 0; i < pts.size(); i++){
|
||||
data.push_back(&pts[i]);
|
||||
}
|
||||
|
||||
#ifndef CGAL_TBB_STRUCTURE_IN_KD_TREE
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#endif
|
||||
|
||||
Point_container c(dim_, data.begin(), data.end(),traits_);
|
||||
bbox = new Kd_tree_rectangle<FT,D>(c.bounding_box());
|
||||
if (c.size() <= split.bucket_size()){
|
||||
tree_root = create_leaf_node(c);
|
||||
}else {
|
||||
tree_root = create_internal_node(c, UseExtendedNode());
|
||||
tree_root = new_internal_node();
|
||||
create_internal_node (tree_root, c, ConcurrencyTag());
|
||||
}
|
||||
|
||||
//Reorder vector for spatial locality
|
||||
|
|
|
|||
|
|
@ -51,15 +51,12 @@ namespace CGAL {
|
|||
typedef typename Kdt::iterator iterator;
|
||||
typedef typename Kdt::D D;
|
||||
|
||||
bool leaf;
|
||||
|
||||
public :
|
||||
Kd_tree_node(bool leaf_)
|
||||
:leaf(leaf_){}
|
||||
Kd_tree_node() { }
|
||||
|
||||
bool is_leaf() const{
|
||||
return leaf;
|
||||
}
|
||||
virtual ~Kd_tree_node() { }
|
||||
|
||||
virtual bool is_leaf() const = 0;
|
||||
|
||||
std::size_t
|
||||
num_items() const
|
||||
|
|
@ -97,8 +94,8 @@ namespace CGAL {
|
|||
Internal_node_const_handle node =
|
||||
static_cast<Internal_node_const_handle>(this);
|
||||
return
|
||||
(std::max)( node->lower()->depth(current_max_depth + 1),
|
||||
node->upper()->depth(current_max_depth + 1));
|
||||
(std::max)( node->lower()->depth(current_max_depth + 1),
|
||||
node->upper()->depth(current_max_depth + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,17 +109,17 @@ namespace CGAL {
|
|||
OutputIterator
|
||||
tree_items(OutputIterator it) const {
|
||||
if (is_leaf()) {
|
||||
Leaf_node_const_handle node =
|
||||
Leaf_node_const_handle node =
|
||||
static_cast<Leaf_node_const_handle>(this);
|
||||
if (node->size()>0)
|
||||
for (iterator i=node->begin(); i != node->end(); i++)
|
||||
{*it=*i; ++it;}
|
||||
}
|
||||
if (node->size()>0)
|
||||
for (iterator i=node->begin(); i != node->end(); i++)
|
||||
{*it=*i; ++it;}
|
||||
}
|
||||
else {
|
||||
Internal_node_const_handle node =
|
||||
Internal_node_const_handle node =
|
||||
static_cast<Internal_node_const_handle>(this);
|
||||
it=node->lower()->tree_items(it);
|
||||
it=node->upper()->tree_items(it);
|
||||
it=node->lower()->tree_items(it);
|
||||
it=node->upper()->tree_items(it);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
|
@ -206,20 +203,20 @@ namespace CGAL {
|
|||
else {
|
||||
Internal_node_const_handle node =
|
||||
static_cast<Internal_node_const_handle>(this);
|
||||
// after splitting b denotes the lower part of b
|
||||
Kd_tree_rectangle<FT,D> b_upper(b);
|
||||
node->split_bbox(b, b_upper);
|
||||
// after splitting b denotes the lower part of b
|
||||
Kd_tree_rectangle<FT,D> b_upper(b);
|
||||
node->split_bbox(b, b_upper);
|
||||
|
||||
if (q.outer_range_contains(b))
|
||||
it=node->lower()->tree_items(it);
|
||||
else
|
||||
if (q.inner_range_intersects(b))
|
||||
it=node->lower()->search(it,q,b,tree_points_begin,cache_begin,dim);
|
||||
if (q.outer_range_contains(b_upper))
|
||||
it=node->upper()->tree_items(it);
|
||||
else
|
||||
if (q.inner_range_intersects(b_upper))
|
||||
it=node->upper()->search(it,q,b_upper,tree_points_begin,cache_begin,dim);
|
||||
if (q.outer_range_contains(b))
|
||||
it=node->lower()->tree_items(it);
|
||||
else
|
||||
if (q.inner_range_intersects(b))
|
||||
it=node->lower()->search(it,q,b,tree_points_begin,cache_begin,dim);
|
||||
if (q.outer_range_contains(b_upper))
|
||||
it=node->upper()->tree_items(it);
|
||||
else
|
||||
if (q.inner_range_intersects(b_upper))
|
||||
it=node->upper()->search(it,q,b_upper,tree_points_begin,cache_begin,dim);
|
||||
};
|
||||
return it;
|
||||
}
|
||||
|
|
@ -398,13 +395,13 @@ namespace CGAL {
|
|||
Kd_tree_leaf_node()
|
||||
{}
|
||||
|
||||
Kd_tree_leaf_node(bool leaf_ )
|
||||
: Base(leaf_)
|
||||
Kd_tree_leaf_node(unsigned int n_ )
|
||||
: n(n_)
|
||||
{}
|
||||
|
||||
Kd_tree_leaf_node(bool leaf_,unsigned int n_ )
|
||||
: Base(leaf_), n(n_)
|
||||
{}
|
||||
virtual ~Kd_tree_leaf_node() { }
|
||||
|
||||
virtual bool is_leaf() const { return true; }
|
||||
|
||||
// members for all nodes
|
||||
|
||||
|
|
@ -475,12 +472,15 @@ namespace CGAL {
|
|||
|
||||
// default constructor
|
||||
Kd_tree_internal_node()
|
||||
: cut_dim(-1), cut_val(0)
|
||||
, lower_ch (nullptr), upper_ch (nullptr)
|
||||
, upper_low_val(0), upper_high_val(0)
|
||||
, lower_low_val(0), lower_high_val(0)
|
||||
{}
|
||||
|
||||
Kd_tree_internal_node(bool leaf_)
|
||||
: Base(leaf_)
|
||||
{}
|
||||
virtual ~Kd_tree_internal_node() { }
|
||||
|
||||
virtual bool is_leaf() const { return false; }
|
||||
|
||||
// members for internal node and extended internal node
|
||||
|
||||
|
|
@ -621,9 +621,7 @@ namespace CGAL {
|
|||
Kd_tree_internal_node()
|
||||
{}
|
||||
|
||||
Kd_tree_internal_node(bool leaf_)
|
||||
: Base(leaf_)
|
||||
{}
|
||||
virtual bool is_leaf() const { return false; }
|
||||
|
||||
|
||||
// members for internal node and extended internal node
|
||||
|
|
|
|||
Loading…
Reference in New Issue