Decorator for any KDTree factory which provides support for multi thread KDTree building.
More...
|
| MultiThreadKDTreeFactory (shared_ptr< SimpleKDTreeFactory > const kdtf, shared_ptr< SimpleKDTreeGeometricStrategy > const gs, size_t const numJobs=2, size_t const geomJobs=2) |
| MultiThreadKDTreeFactory default constructor. More...
|
|
KDTreeFactory * | clone () const override |
|
void | _clone (KDTreeFactory *kdtf) const override |
| Assign attributes from MultiThreadKDTreeFactory to its clone.
|
|
KDTreeNodeRoot * | makeFromPrimitivesUnsafe (vector< Primitive * > &primitives, bool const computeStats=false, bool const reportStats=false) override |
| Build a KDTree which type depends on current KDTree factory (MultiThreadKDTreeFactory::kdtf) on a multi thread basis. More...
|
|
virtual shared_ptr< SimpleKDTreeFactory > | getKdtf () const |
| Obtain the SimpleKDTreeFactory used to build tree nodes. More...
|
|
virtual size_t | getPoolSize () const |
| Obtain the pool size of the thread pool (num jobs) More...
|
|
virtual size_t | getNumJobs () const |
| Obtain the number of threads for node-level parallelization. More...
|
|
virtual size_t | getGeomJobs () const |
| Obtain the number of threads for geometry-level parallelization. More...
|
|
virtual shared_ptr< SimpleKDTreeGeometricStrategy > | getGS () const |
| Obtain the geometric strategy. More...
|
|
| SimpleKDTreeFactory () |
| SimpleKDTreeFactory default constructor. More...
|
|
KDTreeFactory * | clone () const override |
|
KDTreeNodeRoot * | makeFromPrimitivesUnsafe (vector< Primitive * > &primitives, bool const computeStats=false, bool const reportStats=false) override |
| Build a simple KDTree from given primitives. More...
|
|
| KDTreeFactory () |
| K dimensional tree factory default constructor.
|
|
virtual KDTreeNodeRoot * | makeFromPrimitives (vector< Primitive * > const &primitives, bool const computeStats=false, bool const reportStats=false) |
| Safe wrapper from makeFromPrimitivesUnsafe which handles a copy to make from primitives by default. This function behavior might be overridden by any derived/child class. It is expected that any implementation of makeFromPrimitives provides a way to implement the makeFromPrimitivesUnsafe method without modifying vector of input primitives. Notice this does not mean primitives themselves cannot be modified, that depends on the type of KDTreeFactory. It only means that the vector itself will not be modified, for instance due to sorting purposes. More...
|
|
virtual bool | isBuildingLightNodes () |
| Check if KDTreeFactory is building light nodes. More...
|
|
virtual void | setBuildingLightNodes (bool const buildLightNodes) |
| Set KDTreeFactory so it build light nodes (true) or not (false) More...
|
|
virtual void | setChild (LightKDTreeNode *&child, KDTreeNode *node) |
| Set child to given node if and only if node is not null. It must be used to assign children nodes in a thread-safe way. More...
|
|
|
KDTreeNode * | buildRecursive (KDTreeNode *parent, bool const left, vector< Primitive * > &primitives, int const depth, int const index) override |
| Recursively build a KDTree for given primitives using given KDTreeFactory (kdtf). The building of upper nodes is delegated to the MultiThreadKDTreeFactory::buildRecursiveGeometryLevel method, while the building of middle and lower nodes is assumed by the MultiThreadKDTreeFactory::buildRecursiveNodeLevel method. More...
|
|
KDTreeNode * | buildRecursiveGeometryLevel (KDTreeNode *parent, bool const left, vector< Primitive * > &primitives, int const depth, int const index) |
| Recursively build a KDTree for given primitives using given KDTreeFactory (kdtf) in a geometry-level parallelization context. The geometry-level parallelization implies distributing threads among splits as uniform as possible, while satisfying the constraint that any split must have at least one associated thread. It is the way to go for building the upper levels of the KDTree. For the sake of understanding, let \(P_i\left[a, b\right]\) note the \(i\)-th split associated to threads from \(a\)-th (inclusive) to \(b\)-th (inclusive). Thus, if \(d\) is said to be the tree depth and \(k\) is the number of threads, then it is possible to modellize the behavior of the geometry level parallel building process as follows: More...
|
|
void | buildChildrenGeometryLevel (KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > const &primitives, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives, int const auxiliarThreads) |
| Provide an alternative implementation for the building of left and right children nodes. More...
|
|
KDTreeNode * | buildRecursiveNodeLevel (KDTreeNode *parent, bool const left, vector< Primitive * > &primitives, int const depth, int const index) |
| Recursively build a KDTree for given primitives using given KDTreeFactory (kdtf) in a node-level parallelization context. The node-level parallelization implies a one thread per node distribution. While it can be used to build the entire KDTree, at the upper levels it leads to idle threads. This problem is easy to see at the first node (root node), because there is only one node and thus only one thread can be working on it. The same would apply for the second node if the number of threads is \(>2\), because there would be only two working threads while the others will remain idle. However, this is inefficient and can be solved by delegating upper nodes to a geometry-level parallelization strategy instead of a node-level one. More...
|
|
void | computeKDTreeStats (KDTreeNodeRoot *root) const override |
| Call the compute KDTree stats method of decorated KDTree factory. More...
|
|
void | reportKDTreeStats (KDTreeNodeRoot *root, vector< Primitive * > const &primitives) const override |
| Call the report KDTree stats method of decorated KDTree factory. More...
|
|
virtual void | prepareToMake () |
| Prepare the MultiThreadKDTreeFactory so it is ready to start making a new KDTree. More...
|
|
virtual void | increaseFinishedGeomJobsCount (size_t const amount) |
| Increase count of finished geometry-level jobs in a thread safe way. More...
|
|
virtual KDTreeNode * | buildRecursiveRecipe (KDTreeNode *parent, bool const left, vector< Primitive * > &primitives, int const depth, int const index, std::function< void(KDTreeNode *node, KDTreeNode *parent, bool const left, vector< Primitive * > const &primitives)> f_computeNodeBoundaries, std::function< void(KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > &primitives, int const depth)> f_defineSplit, std::function< void(vector< Primitive * > const &primitives, int const splitAxis, double const splitPos, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives)> f_populateSplits, std::function< void(KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > const &primitives, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives)> f_buildChildrenNodes) |
| The recipe of the recursive building algorithm. It is meant to be used by the SimpleKDTreeFactory::buildRecursive but also by any alternative implementation which shares the same recipe (global logic) but changes the way some parts are computed. For instance, it is used by the MultiThreadKDTreeFactory to handle geometry-level parallelization. More...
|
|
virtual void | defineSplit (KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > &primitives, int const depth) const |
| Define the split axis and position for current node. More...
|
|
virtual void | populateSplits (vector< Primitive * > const &primitives, int const splitAxis, double const splitPos, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives) const |
| Populate list of primitives for left and right splits from given primitives of node being splitted. More...
|
|
virtual void | buildChildrenNodes (KDTreeNode *node, KDTreeNode *parent, vector< Primitive * > const &primitives, int const depth, int const index, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives) |
| Build children nodes for given node. If no children nodes must be built, then the node is configured as a leaf node. More...
|
|
virtual void | computeNodeBoundaries (KDTreeNode *node, KDTreeNode *parent, bool const left, vector< Primitive * > const &primitives) const |
| Compute min and max position and surface area of bounding cuboid for given node. More...
|
|
virtual void | onPopulateSplitsDigestPrimitive (Primitive *p, int const splitAxis, double const splitPos, vector< Primitive * > &leftPrimitives, vector< Primitive * > &rightPrimitives) const |
| Function to assist SimpleKDTreeFactory::populateSplits by providing the logic of digesting a primitive. More...
|
|
virtual void | computeMinMaxSAHForChild (KDTreeNode *node, KDTreeNode *parent, bool const left, vector< Primitive * > const &primitives) const |
| Function to assist SimpleKDTreeFactory::computeNodeBoundaries when computing surface area heuristic and minimum and maximum positions for child nodes. More...
|
|
virtual void | onRootBoundariesDigestPrimitive (Primitive *primitive, double &ax, double &ay, double &az, double &bx, double &by, double &bz) const |
| Function to assist SimpleKDTreeFactory::computeNodeBoundaries by providing the logic of digesting a primitive. More...
|
|
virtual void | onComputeNodeBoundariesCalcSAH (KDTreeNode *node, double const ax, double const ay, double const az, double const bx, double const by, double const bz) const |
| Function to assist SimpleKDTreeFactory::computeNodeBoundaries when computing the SAH for a node. More...
|
|
virtual bool | checkNodeMustSplit (vector< Primitive * > const &primitives, vector< Primitive * > const &leftPrimitives, vector< Primitive * > const &rightPrimitives) const |
| Check wheter the node must be splitted (true) or not (false) depending on its total primitives and the ones that would be assigned to left and right children. More...
|
|
virtual void | makeLeaf (KDTreeNode *node, vector< Primitive * > const &primitives) const |
| Make given node a leaf one. More...
|
|
virtual void | lighten (KDTreeNodeRoot *root) |
| Rebuild all children of given root KDTree node as LightKDTeeeNode nodes. More...
|
|
virtual LightKDTreeNode * | _lighten (KDTreeNode *node) |
| Assist KDTreeFactory::lighten function by handling the lighten of a given non-root node. More...
|
|
|
shared_ptr< SimpleKDTreeFactory > | kdtf |
| The SimpleKDTreeFactory or derived to be used to build tree nodes.
|
|
shared_ptr< SimpleKDTreeGeometricStrategy > | gs |
| The SimpleKDTreeGeometricStrategy or derived to be used to handle geometry-level parallelization during multi-thread KDTree building.
|
|
KDTreeFactoryThreadPool | tpNode |
| The thread pool to handle concurrency during recursive KDTree building at node-level.
|
|
size_t | minTaskPrimitives |
| The minimum number of primitives on a given split so a new task is started to handle them.
|
|
int | maxGeometryDepth |
| The maximum geometry depth level \(d^*\) as explained in the MultiThreadKDTreeFactory::buildRecursiveGeometryLevel It is updated accordingly always that MultiThreadKDTreeFactory::makeFromPrimitivesUnsafe is called. More...
|
|
size_t | numJobs |
| The maximum number of jobs (threads/workers) that this factory is allowed to use.
|
|
size_t | geomJobs |
| The number of jobs (threads/workers) that this factory must use when building upper KDTree nodes (geometry-level parallelization)
|
|
shared_ptr< SharedTaskSequencer > | masters |
| All masters threads (except main thread) are handled by this shared task sequencer. More...
|
|
size_t | finishedGeomJobs |
| How many geometry-level jobs have fully finished during current KDT building. More...
|
|
boost::mutex | finishedGeomJobsMutex |
| Mutex to handle concurrent access to counter of finished geometry-level jobs. More...
|
|
bool | notUsed |
| True if the factory has not been used before, false otherwise.
|
|
std::function< KDTreeNode *(KDTreeNode *, bool const, vector< Primitive * > &, int const, int const)> | _buildRecursive |
| The member function as attribute used to recursively build KDTree nodes. By default it will be assigned to the buildRecursive member function but it might be overridden by other implementations. For instance, to wrap the buildRecursive behavior to handle parallel building of KDTrees. More...
|
|
size_t | minSplitPrimitives |
| How many primitives are required for a node to be splitted. More...
|
|
bool | buildLightNodes = true |
| When it is true, the KDTreeFactory is expected to build light nodes. It is, built KDTree must have a KDTreeRootNode which children are all LightKDTreeNode. When it is false, KDTreeFactory is allowed to build KDTree with KDTreeNode children, which might require more memory. More...
|
|
LightKDTreeNodeBlockAllocator | lkdtnBlockAllocator |
| The block allocator to speed-up lighten of KDTree by reducing allocation calls when instantiating multiple LightKDTreeNode. More...
|
|
Decorator for any KDTree factory which provides support for multi thread KDTree building.
- Author
- Alberto M. Esmoris Pena
- Version
- 1.0
When combining geometry-level and node-level parallel building strategies, both are assumed to support the same max number of threads. To better understand this, let \(\Phi(t)\) be the number of currently available threads for the geometry-level strategy at time \(t\) while \(\Psi(t)\) denotes the number of currently available threads for the node-level thread pool at time \(t\). Now, if the maximum number of threads is \(k\), at the beginning it would be \(\Phi(t)=k\) and \(\Psi(t)=0\). Once all geometry-level splits have been done, then \(\Phi(t)=0\) and \(\Psi(t)=k\). At the last depth for geometry-level strategy, always that a split is finished involved threads will go from geometry-level mode to node-level thread pool. At any time, threads for geometry-level and node-level are related by following expresion \(\Psi(t) = k - \Phi(t)\).
KDTreeNode * MultiThreadKDTreeFactory::buildRecursiveGeometryLevel |
( |
KDTreeNode * |
parent, |
|
|
bool const |
left, |
|
|
vector< Primitive * > & |
primitives, |
|
|
int const |
depth, |
|
|
int const |
index |
|
) |
| |
|
protected |
Recursively build a KDTree for given primitives using given KDTreeFactory (kdtf) in a geometry-level parallelization context. The geometry-level parallelization implies distributing threads among splits as uniform as possible, while satisfying the constraint that any split must have at least one associated thread. It is the way to go for building the upper levels of the KDTree. For the sake of understanding, let \(P_i\left[a, b\right]\) note the \(i\)-th split associated to threads from \(a\)-th (inclusive) to \(b\)-th (inclusive). Thus, if \(d\) is said to be the tree depth and \(k\) is the number of threads, then it is possible to modellize the behavior of the geometry level parallel building process as follows:
\[ \left\{\begin{array}{ccc} d = 0 &:& \left\{ P_0[a_0, b_0] \right\} \\ d = 1 &:& \left\{ P_0\left[a_0, b_0\right], P_1\left[a_1, b_1\right] \right\} \\ d = 2 &:& \left\{ P_0\left[a_0, b_0\right], P_1\left[a_1, b_1\right], P_2\left[a_2, b_2\right], P_3\left[a_3, b_3\right] \right\} \\ \vdots & \vdots & \vdots \end{array}\right. \]
It is worth to mention that \(a_{i+1} = b_{i}+1\). Now, for generalization purposes let \(\alpha=\left\lfloor{\frac{k}{2^d}}\right\rfloor\) and \(\beta \equiv k \mod 2^d\). In consequence, at any depth level \(d\) it is possible to define \(\forall i,\; P_i = [a_i, b_i]\) where:
\[ a_i = \left\{\begin{array}{lll} i\alpha &,& i < 2^d - \beta \\ i(\alpha+1) - 2^d + \beta &,& i \geq 2^d - \beta \end{array}\right. \]
\[ b_i = \left\{\begin{array}{lll} \alpha(i+1)-1 &,& i < 2^d - \beta \\ i(\alpha+1)-2^d+\beta+\alpha &,& i \geq 2^d - \beta \end{array}\right. \]
It can be seen that the maximum (also assumed as expected here) number of splits at a given depth is given by \(2^d\). Therefore, geometry level building process is guaranteed to be applicable at least while \(k \geq 2^d\) is satisfied. Moreover, any split can be understood as a set of primitives \(P_i = \left\{ p_j \in \mathbb{R}^{n \times 3} : j \in [\phi \geq 1, \psi \leq m] \right\}\), so the total number of primitives is \(m\) (which is the cardinality of \(P_0\) at \(d=0\)) and each primitive is itself a set of \(n\) points in \(\mathbb{R}^3\). Of course, this is a simplification since different types of primitives are supported by Helios. However, it is not necessary to get into that level of detail to understand this algorithm. Just notice that \(P_i\) at \(d = x\) is distinct that \(P_i\) at \(d = y\) as long as \(x \neq y\) is satisfied. It is because once a node is splitted, it is understood as destroyed in this context so it is simply replaced by its children at next depth level.
Finally, the last depth at which geometry level parallelization applies can be deduced from the fact that the number of threads for a parallel algorithm is going to satisfy \(k>1\). For then it follows that \(\log_2{k} \geq 0\). Notice this would stand even for the sequential case because \(\log_2{1} = 0\). In consequence, it is known that \(k \geq 2^d \iff \log_2{k} \geq d\), because the logarithm will not change the sign. But, from aforementioned inequation, the last depth label can be defined as \(d^* = \left\lfloor{\log_2{k}}\right\rfloor\)
- Returns
- Built KDTree node
- See also
- MultiThreadKDTreeFactory::buildRecursive
-
MultiThreadKDTreeFactory::buildRecursiveNodeLevel
-
MultiThreadKDTreeFactory::maxGeometryDepth