ENH: reduce some internal overhead when splitting octree nodes (#2609)

- don't need separate scratch arrays (avoids possible reallocations
  when split is imbalanced)

ENH: upgrade dynamicIndexedOctree to use DynamicList directly

- with C++11 move semantics don't need lists of autoPtr
  for efficient transfers
This commit is contained in:
Mark Olesen
2022-10-12 20:12:08 +02:00
committed by Andrew Heather
parent b129446221
commit 3d7dc6a870
4 changed files with 123 additions and 172 deletions

View File

@ -41,38 +41,27 @@ Foam::scalar Foam::dynamicIndexedOctree<Type>::perturbTol_ = 10*SMALL;
template<class Type> template<class Type>
void Foam::dynamicIndexedOctree<Type>::divide void Foam::dynamicIndexedOctree<Type>::divide
( (
const autoPtr<DynamicList<label>>& indices, const labelUList& indices,
const treeBoundBox& bb, const treeBoundBox& bb,
contentListList& result FixedList<DynamicList<label>, 8>& dividedIndices
) const ) const
{ {
for (direction octant = 0; octant < 8; octant++) const label presize = (indices.size()/8);
{
result.append
(
autoPtr<DynamicList<label>>
(
new DynamicList<label>(indices().size()/8)
)
);
}
// Precalculate bounding boxes. for (direction octant = 0; octant < 8; ++octant)
FixedList<treeBoundBox, 8> subBbs;
for (direction octant = 0; octant < 8; octant++)
{ {
subBbs[octant] = bb.subBbox(octant); const treeBoundBox subBbs(bb.subBbox(octant));
}
forAll(indices(), i) auto& contains = dividedIndices[octant];
{
label shapeI = indices()[i];
for (direction octant = 0; octant < 8; octant++) contains.clear();
contains.reserve_nocopy(presize);
for (const label index : indices)
{ {
if (shapes_.overlaps(shapeI, subBbs[octant])) if (shapes_.overlaps(index, subBbs))
{ {
result[octant]().append(shapeI); contains.append(index);
} }
} }
} }
@ -84,20 +73,16 @@ Foam::dynamicIndexedOctreeBase::node
Foam::dynamicIndexedOctree<Type>::divide Foam::dynamicIndexedOctree<Type>::divide
( (
const treeBoundBox& bb, const treeBoundBox& bb,
const label contentI, label contentIndex,
const label parentNodeIndex, const label parentNodeIndex,
const label octantToBeDivided const label octantToBeDivided
) )
{ {
const autoPtr<DynamicList<label>>& indices = contents_[contentI];
node nod;
if if
( (
bb.min()[0] >= bb.max()[0] bb.min().x() >= bb.max().x()
|| bb.min()[1] >= bb.max()[1] || bb.min().y() >= bb.max().y()
|| bb.min()[2] >= bb.max()[2] || bb.min().z() >= bb.max().z()
) )
{ {
FatalErrorInFunction FatalErrorInFunction
@ -105,48 +90,42 @@ Foam::dynamicIndexedOctree<Type>::divide
<< abort(FatalError); << abort(FatalError);
} }
// Divide the indices into 8 (possibly empty) subsets.
// Replace current contentIndex with the first (non-empty) subset.
// Append the rest.
const DynamicList<label>& indices = contents_[contentIndex];
FixedList<DynamicList<label>, 8> dividedIndices;
divide(indices, bb, dividedIndices);
node nod;
nod.bb_ = bb; nod.bb_ = bb;
nod.parent_ = -1; nod.parent_ = -1;
contentListList dividedIndices(8); bool replaceNode = true;
divide(indices, bb, dividedIndices);
// Have now divided the indices into 8 (possibly empty) subsets. for (direction octant = 0; octant < 8; ++octant)
// Replace current contentI with the first (non-empty) subset.
// Append the rest.
bool replaced = false;
for (direction octant = 0; octant < dividedIndices.size(); octant++)
{ {
autoPtr<DynamicList<label>>& subIndices = dividedIndices[octant]; auto& subIndices = dividedIndices[octant];
if (subIndices().size()) if (subIndices.size())
{ {
if (!replaced) if (replaceNode)
{ {
contents_[contentI]->transfer(subIndices()); // Replace existing
nod.subNodes_[octant] = contentPlusOctant(contentI, octant); contents_[contentIndex] = std::move(subIndices);
replaceNode = false;
replaced = true;
} }
else else
{ {
// Store at end of contents. // Append to contents
// note dummy append + transfer trick contentIndex = contents_.size();
label sz = contents_.size(); contents_.append(std::move(subIndices));
contents_.append
(
autoPtr<DynamicList<label>>
(
new DynamicList<label>()
)
);
contents_[sz]->transfer(subIndices());
nod.subNodes_[octant] = contentPlusOctant(sz, octant);
} }
nod.subNodes_[octant] = contentPlusOctant(contentIndex, octant);
} }
else else
{ {
@ -160,12 +139,12 @@ Foam::dynamicIndexedOctree<Type>::divide
{ {
nod.parent_ = parentNodeIndex; nod.parent_ = parentNodeIndex;
label sz = nodes_.size(); const label newNodeId = nodes_.size();
nodes_.append(nod); nodes_.append(nod);
nodes_[parentNodeIndex].subNodes_[octantToBeDivided] nodes_[parentNodeIndex].subNodes_[octantToBeDivided]
= nodePlusOctant(sz, octantToBeDivided); = nodePlusOctant(newNodeId, octantToBeDivided);
} }
return nod; return nod;
@ -184,7 +163,7 @@ void Foam::dynamicIndexedOctree<Type>::recursiveSubDivision
{ {
if if
( (
contents_[contentI]->size() > minSize_ contents_[contentI].size() > minSize_
&& nLevels < maxLevels_ && nLevels < maxLevels_
) )
{ {
@ -415,7 +394,7 @@ void Foam::dynamicIndexedOctree<Type>::findNearest
{ {
shapes_.findNearest shapes_.findNearest
( (
*(contents_[getContent(index)]), contents_[getContent(index)],
sample, sample,
nearestDistSqr, nearestDistSqr,
@ -474,7 +453,7 @@ void Foam::dynamicIndexedOctree<Type>::findNearest
{ {
shapes_.findNearest shapes_.findNearest
( (
*(contents_[getContent(index)]), contents_[getContent(index)],
ln, ln,
tightest, tightest,
@ -1265,7 +1244,7 @@ void Foam::dynamicIndexedOctree<Type>::traverseNode
if (isContent(index)) if (isContent(index))
{ {
const labelList& indices = *(contents_[getContent(index)]); const labelList& indices = contents_[getContent(index)];
if (indices.size()) if (indices.size())
{ {
@ -1675,7 +1654,7 @@ void Foam::dynamicIndexedOctree<Type>::findBox
{ {
if (nodeBb.subOverlaps(octant, searchBox)) if (nodeBb.subOverlaps(octant, searchBox))
{ {
const labelList& indices = *(contents_[getContent(index)]); const labelList& indices = contents_[getContent(index)];
forAll(indices, i) forAll(indices, i)
{ {
@ -1721,7 +1700,7 @@ void Foam::dynamicIndexedOctree<Type>::findSphere
{ {
if (nodeBb.subOverlaps(octant, centre, radiusSqr)) if (nodeBb.subOverlaps(octant, centre, radiusSqr))
{ {
const labelList& indices = *(contents_[getContent(index)]); const labelList& indices = contents_[getContent(index)];
forAll(indices, i) forAll(indices, i)
{ {
@ -1934,7 +1913,7 @@ Foam::label Foam::dynamicIndexedOctree<Type>::countElements
} }
else if (isContent(index)) else if (isContent(index))
{ {
nElems += contents_[getContent(index)]->size(); nElems += contents_[getContent(index)].size();
} }
else else
{ {
@ -2049,7 +2028,7 @@ Foam::dynamicIndexedOctree<Type>::dynamicIndexedOctree
maxDuplicity_(maxDuplicity), maxDuplicity_(maxDuplicity),
nodes_(label(shapes.size() / maxLeafRatio_)), nodes_(label(shapes.size() / maxLeafRatio_)),
contents_(label(shapes.size() / maxLeafRatio_)), contents_(label(shapes.size() / maxLeafRatio_)),
nodeTypes_(0) nodeTypes_()
{ {
if (shapes_.size() == 0) if (shapes_.size() == 0)
{ {
@ -2111,7 +2090,7 @@ Foam::pointIndexHit Foam::dynamicIndexedOctree<Type>::findNearest
) const ) const
{ {
label nearestShapeI = -1; label nearestShapeI = -1;
point nearestPoint; point nearestPoint(Zero);
if (nodes_.size()) if (nodes_.size())
{ {
@ -2126,10 +2105,6 @@ Foam::pointIndexHit Foam::dynamicIndexedOctree<Type>::findNearest
nearestPoint nearestPoint
); );
} }
else
{
nearestPoint = Zero;
}
return pointIndexHit(nearestShapeI != -1, nearestPoint, nearestShapeI); return pointIndexHit(nearestShapeI != -1, nearestPoint, nearestShapeI);
} }
@ -2260,7 +2235,7 @@ Foam::label Foam::dynamicIndexedOctree<Type>::findInside
// Need to check for the presence of content, in-case the node is empty // Need to check for the presence of content, in-case the node is empty
if (isContent(contentIndex)) if (isContent(contentIndex))
{ {
const labelList& indices = *(contents_[getContent(contentIndex)]); const labelList& indices = contents_[getContent(contentIndex)];
forAll(indices, elemI) forAll(indices, elemI)
{ {
@ -2292,7 +2267,7 @@ const Foam::labelList& Foam::dynamicIndexedOctree<Type>::findIndices
// Need to check for the presence of content, in-case the node is empty // Need to check for the presence of content, in-case the node is empty
if (isContent(contentIndex)) if (isContent(contentIndex))
{ {
return *(contents_[getContent(contentIndex)]); return contents_[getContent(contentIndex)];
} }
return labelList::null(); return labelList::null();
@ -2402,15 +2377,8 @@ bool Foam::dynamicIndexedOctree<Type>::insert(label startIndex, label endIndex)
if (nodes_.empty()) if (nodes_.empty())
{ {
contents_.append contents_.append(DynamicList<label>(1));
( contents_[0].append(0);
autoPtr<DynamicList<label>>
(
new DynamicList<label>(1)
)
);
contents_[0]->append(0);
// Create topnode. // Create topnode.
node topNode = divide(bb_, 0, -1, 0); node topNode = divide(bb_, 0, -1, 0);
@ -2474,7 +2442,7 @@ bool Foam::dynamicIndexedOctree<Type>::insertIndex
{ {
const label contentI = getContent(subNodeLabel); const label contentI = getContent(subNodeLabel);
contents_[contentI]->append(index); contents_[contentI].append(index);
recursiveSubDivision recursiveSubDivision
( (
@ -2496,12 +2464,9 @@ bool Foam::dynamicIndexedOctree<Type>::insertIndex
{ {
label sz = contents_.size(); label sz = contents_.size();
contents_.append contents_.append(DynamicList<label>(1));
(
autoPtr<DynamicList<label>>(new DynamicList<label>(1))
);
contents_[sz]->append(index); contents_[sz].append(index);
nodes_[nodIndex].subNodes_[octant] nodes_[nodIndex].subNodes_[octant]
= contentPlusOctant(sz, octant); = contentPlusOctant(sz, octant);
@ -2574,7 +2539,7 @@ Foam::label Foam::dynamicIndexedOctree<Type>::removeIndex
if (shapes().overlaps(index, subBb)) if (shapes().overlaps(index, subBb))
{ {
DynamicList<label>& contentList = *(contents_[contentI]); DynamicList<label>& contentList = contents_[contentI];
DynamicList<label> newContent(contentList.size()); DynamicList<label> newContent(contentList.size());
@ -2590,7 +2555,7 @@ Foam::label Foam::dynamicIndexedOctree<Type>::removeIndex
newContent.shrink(); newContent.shrink();
if (newContent.size() == 0) if (newContent.empty())
{ {
// Set to empty. // Set to empty.
nodes_[nodIndex].subNodes_[octant] nodes_[nodIndex].subNodes_[octant]
@ -2600,7 +2565,7 @@ Foam::label Foam::dynamicIndexedOctree<Type>::removeIndex
contentList.transfer(newContent); contentList.transfer(newContent);
} }
totalContents += contents_[contentI]->size(); totalContents += contents_[contentI].size();
} }
else else
{ {
@ -2651,7 +2616,7 @@ void Foam::dynamicIndexedOctree<Type>::print
} }
else if (isContent(index)) else if (isContent(index))
{ {
const labelList& indices = *(contents_[getContent(index)]); const labelList& indices = contents_[getContent(index)];
if (false) //debug) if (false) //debug)
{ {
@ -2689,9 +2654,9 @@ template<class Type>
void Foam::dynamicIndexedOctree<Type>::writeTreeInfo() const void Foam::dynamicIndexedOctree<Type>::writeTreeInfo() const
{ {
label nEntries = 0; label nEntries = 0;
forAll(contents_, i) for (const auto& subContents : contents_)
{ {
nEntries += contents_[i]->size(); nEntries += subContents.size();
} }
Pout<< "indexedOctree::indexedOctree" Pout<< "indexedOctree::indexedOctree"
@ -2726,9 +2691,9 @@ Foam::operator<<(Ostream& os, const dynamicIndexedOctree<Type>& t)
{ {
os << t.bb() << token::SPACE << t.nodes() << endl; os << t.bb() << token::SPACE << t.nodes() << endl;
forAll(t.contents(), cI) for (const auto& subContents : t.contents())
{ {
os << t.contents()[cI]() << endl; os << subContents << endl;
} }
return os; return os;

View File

@ -47,8 +47,6 @@ SourceFiles
namespace Foam namespace Foam
{ {
typedef DynamicList<autoPtr<DynamicList<label>>> contentListList;
// Forward Declarations // Forward Declarations
class Istream; class Istream;
template<class Type> class dynamicIndexedOctree; template<class Type> class dynamicIndexedOctree;
@ -122,7 +120,7 @@ class dynamicIndexedOctree
DynamicList<node> nodes_; DynamicList<node> nodes_;
//- List of all contents (referenced by those nodes that are contents) //- List of all contents (referenced by those nodes that are contents)
contentListList contents_; DynamicList<DynamicList<label>> contents_;
//- Per node per octant whether is fully inside/outside/mixed. //- Per node per octant whether is fully inside/outside/mixed.
mutable PackedList<2> nodeTypes_; mutable PackedList<2> nodeTypes_;
@ -136,9 +134,9 @@ class dynamicIndexedOctree
// according to where they are in relation to mid. // according to where they are in relation to mid.
void divide void divide
( (
const autoPtr<DynamicList<label>>& indices, const labelUList& indices,
const treeBoundBox& bb, const treeBoundBox& bb,
contentListList& result FixedList<DynamicList<label>, 8>& dividedIndices
) const; ) const;
//- Subdivide the contents node at position contentI. //- Subdivide the contents node at position contentI.
@ -146,7 +144,7 @@ class dynamicIndexedOctree
node divide node divide
( (
const treeBoundBox& bb, const treeBoundBox& bb,
const label contentI, label contentIndex,
const label parentNodeIndex, const label parentNodeIndex,
const label octantToBeDivided const label octantToBeDivided
); );
@ -371,7 +369,7 @@ public:
//- List of all contents //- List of all contents
//- (referenced by those nodes that are contents) //- (referenced by those nodes that are contents)
const contentListList& contents() const const DynamicList<DynamicList<label>>& contents() const noexcept
{ {
return contents_; return contents_;
} }

View File

@ -43,41 +43,30 @@ Foam::scalar Foam::indexedOctree<Type>::perturbTol_ = 10*SMALL;
template<class Type> template<class Type>
void Foam::indexedOctree<Type>::divide void Foam::indexedOctree<Type>::divide
( (
const labelList& indices, const labelUList& indices,
const treeBoundBox& bb, const treeBoundBox& bb,
labelListList& result FixedList<labelList, 8>& dividedIndices
) const ) const
{ {
List<DynamicList<label>> subIndices(8); // Scratch array
for (direction octant = 0; octant < subIndices.size(); octant++) DynamicList<label> contains(indices.size());
{
subIndices[octant].setCapacity(indices.size()/8);
}
// Precalculate bounding boxes. for (direction octant = 0; octant < 8; ++octant)
FixedList<treeBoundBox, 8> subBbs;
for (direction octant = 0; octant < subBbs.size(); octant++)
{ {
subBbs[octant] = bb.subBbox(octant); const treeBoundBox subBbs(bb.subBbox(octant));
}
forAll(indices, i) contains.clear();
{
label shapeI = indices[i];
for (direction octant = 0; octant < 8; octant++) for (const label index : indices)
{ {
if (shapes_.overlaps(shapeI, subBbs[octant])) if (shapes_.overlaps(index, subBbs))
{ {
subIndices[octant].append(shapeI); contains.append(index);
}
} }
} }
result.setSize(8); // The sub-divided indices:
for (direction octant = 0; octant < subIndices.size(); octant++) dividedIndices[octant] = contains;
{
result[octant].transfer(subIndices[octant]);
} }
} }
@ -88,18 +77,14 @@ Foam::indexedOctree<Type>::divide
( (
const treeBoundBox& bb, const treeBoundBox& bb,
DynamicList<labelList>& contents, DynamicList<labelList>& contents,
const label contentI label contentIndex
) const ) const
{ {
const labelList& indices = contents[contentI];
node nod;
if if
( (
bb.min()[0] >= bb.max()[0] bb.min().x() >= bb.max().x()
|| bb.min()[1] >= bb.max()[1] || bb.min().y() >= bb.max().y()
|| bb.min()[2] >= bb.max()[2] || bb.min().z() >= bb.max().z()
) )
{ {
FatalErrorInFunction FatalErrorInFunction
@ -107,38 +92,41 @@ Foam::indexedOctree<Type>::divide
<< abort(FatalError); << abort(FatalError);
} }
// Divide the indices into 8 (possibly empty) subsets.
// Replace current contentIndex with the first (non-empty) subset.
// Append the rest.
const labelList& indices = contents[contentIndex];
FixedList<labelList, 8> dividedIndices;
divide(indices, bb, dividedIndices);
node nod;
nod.bb_ = bb; nod.bb_ = bb;
nod.parent_ = -1; nod.parent_ = -1;
labelListList dividedIndices(8); bool replaceNode = true;
divide(indices, bb, dividedIndices);
// Have now divided the indices into 8 (possibly empty) subsets. for (direction octant = 0; octant < 8; ++octant)
// Replace current contentI with the first (non-empty) subset.
// Append the rest.
bool replaced = false;
for (direction octant = 0; octant < dividedIndices.size(); octant++)
{ {
labelList& subIndices = dividedIndices[octant]; auto& subIndices = dividedIndices[octant];
if (subIndices.size()) if (subIndices.size())
{ {
if (!replaced) if (replaceNode)
{ {
contents[contentI].transfer(subIndices); // Replace existing
nod.subNodes_[octant] = contentPlusOctant(contentI, octant); contents[contentIndex] = std::move(subIndices);
replaced = true; replaceNode = false;
} }
else else
{ {
// Store at end of contents. // Append to contents
// note dummy append + transfer trick contentIndex = contents.size();
label sz = contents.size(); contents.append(std::move(subIndices));
contents.append(labelList(0));
contents[sz].transfer(subIndices);
nod.subNodes_[octant] = contentPlusOctant(sz, octant);
} }
nod.subNodes_[octant] = contentPlusOctant(contentIndex, octant);
} }
else else
{ {
@ -2089,9 +2077,9 @@ template<class Type>
Foam::indexedOctree<Type>::indexedOctree(const Type& shapes) Foam::indexedOctree<Type>::indexedOctree(const Type& shapes)
: :
shapes_(shapes), shapes_(shapes),
nodes_(0), nodes_(),
contents_(0), contents_(),
nodeTypes_(0) nodeTypes_()
{} {}
@ -2100,13 +2088,13 @@ Foam::indexedOctree<Type>::indexedOctree
( (
const Type& shapes, const Type& shapes,
const List<node>& nodes, const List<node>& nodes,
const labelListList& contents const List<labelList>& contents
) )
: :
shapes_(shapes), shapes_(shapes),
nodes_(nodes), nodes_(nodes),
contents_(contents), contents_(contents),
nodeTypes_(0) nodeTypes_()
{} {}
@ -2121,9 +2109,9 @@ Foam::indexedOctree<Type>::indexedOctree
) )
: :
shapes_(shapes), shapes_(shapes),
nodes_(0), nodes_(),
contents_(0), contents_(),
nodeTypes_(0) nodeTypes_()
{ {
int oldMemSize = 0; int oldMemSize = 0;
if (debug) if (debug)
@ -2299,7 +2287,7 @@ Foam::indexedOctree<Type>::indexedOctree
shapes_(shapes), shapes_(shapes),
nodes_(is), nodes_(is),
contents_(is), contents_(is),
nodeTypes_(0) nodeTypes_()
{} {}

View File

@ -254,7 +254,7 @@ class indexedOctree
List<node> nodes_; List<node> nodes_;
//- List of all contents (referenced by those nodes that are contents) //- List of all contents (referenced by those nodes that are contents)
labelListList contents_; List<labelList> contents_;
//- Per node per octant whether is fully inside/outside/mixed. //- Per node per octant whether is fully inside/outside/mixed.
mutable PackedList<2> nodeTypes_; mutable PackedList<2> nodeTypes_;
@ -268,9 +268,9 @@ class indexedOctree
// according to where they are in relation to mid. // according to where they are in relation to mid.
void divide void divide
( (
const labelList& indices, const labelUList& indices,
const treeBoundBox& bb, const treeBoundBox& bb,
labelListList& result FixedList<labelList, 8>& dividedIndices
) const; ) const;
//- Subdivide the contents node at position contentI. //- Subdivide the contents node at position contentI.
@ -279,7 +279,7 @@ class indexedOctree
( (
const treeBoundBox& bb, const treeBoundBox& bb,
DynamicList<labelList>& contents, DynamicList<labelList>& contents,
const label contentI label contentIndex
) const; ) const;
//- Split any contents node with more than minSize elements. //- Split any contents node with more than minSize elements.
@ -503,7 +503,7 @@ public:
( (
const Type& shapes, const Type& shapes,
const List<node>& nodes, const List<node>& nodes,
const labelListList& contents const List<labelList>& contents
); );
//- Construct from shapes //- Construct from shapes