- multithread in BuildTree

This commit is contained in:
Daniele Bariletti
2025-03-31 16:28:49 +02:00
parent 9c662e2799
commit 562f750200
3 changed files with 166 additions and 101 deletions
+18 -5
View File
@@ -315,17 +315,30 @@ SurfBezier::GetCentroid( Point3d& ptCen) const
return m_pSTM->GetCentroid( ptCen) ;
}
mutex mapBernstein ;
//----------------------------------------------------------------------------
bool
SurfBezier::GetBernstein( double dU, int nDegU, DBLVECTOR& vBernU) const
{
INTINT key( nDegU, int( dU * pow(2,24))) ;
if ( m_mBernCache.find( key) == m_mBernCache.end()) {
bool bCalculated = false ;
{
lock_guard<mutex> lock( mapBernstein) ;
bCalculated = m_mBernCache.find( key) != m_mBernCache.end() ;
}
if ( ! bCalculated) {
DBLVECTOR vBern( nDegU + 1) ;
GetAllBernstein( dU, nDegU, vBern) ;
m_mBernCache[key] = vBern ;
{
lock_guard<mutex> lock( mapBernstein) ;
m_mBernCache[key] = vBern ;
}
}
{
lock_guard<mutex> lock( mapBernstein) ;
vBernU = m_mBernCache.at( key) ;
}
vBernU = m_mBernCache[key] ;
return true ;
}
@@ -1620,7 +1633,7 @@ SurfBezier::GetAuxSurf( void) const
if ( m_pSTM != nullptr)
return m_pSTM ;
// eseguo calcolo
m_pSTM = GetApproxSurf( 50 * EPS_SMALL, 100 * EPS_SMALL) ;
m_pSTM = GetApproxSurf( 1000 * EPS_SMALL, 100 * EPS_SMALL) ;
return m_pSTM ;
}
@@ -1959,7 +1972,7 @@ SurfBezier::GetLeaves( vector<tuple<int, Point3d, Point3d>>& vLeaves) const
//Tree.BuildTree( 5 * LIN_TOL_FINE, 1) ; // per debug
}
else {
Tree.BuildTree( 5 * LIN_TOL_FINE, 0.1) ;
Tree.BuildTree( 100 * LIN_TOL_FINE, 0.1) ;
}
vector<Cell> vCells ;
Tree.GetLeaves( vCells) ;
+142 -93
View File
@@ -32,9 +32,6 @@
#include <thread>
#include <atomic>
#include <execution>
#include <queue>
#include <condition_variable>
#include <functional>
#include <future>
using namespace std ;
@@ -109,17 +106,30 @@ Tree::AdjustLoop( PolyLine& pl, POLYLINEVECTOR& vPl, BOOLVECTOR& vbOrientation)
return true ;
}
mutex map3D ;
//----------------------------------------------------------------------------
bool
Tree::GetPoint( double dU, double dV, Point3d& pt) const
{
pair<int64_t, int64_t> key (static_cast<int64_t>(dU * pow(2,15)), static_cast<int64_t>(dV * pow(2,15))) ;
bool bOk = true ;
if ( m_mPt3d.find( key) == m_mPt3d.end()) {
bOk = bOk && m_pSrfBz->GetPoint( dU / SBZ_TREG_COEFF, dV / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt) ;
m_mPt3d[key] = pt ;
bool bCalculated = false ;
{
lock_guard<mutex> lock( map3D) ;
bCalculated = m_mPt3d.find( key) != m_mPt3d.end() ;
}
if ( ! bCalculated) {
bOk = bOk && m_pSrfBz->GetPoint( dU / SBZ_TREG_COEFF, dV / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt) ;
{
lock_guard<mutex> lock( map3D) ;
m_mPt3d[key] = pt ;
}
}
{
lock_guard<mutex> lock( map3D) ;
pt = m_mPt3d[key] ;
}
pt = m_mPt3d[key] ;
return bOk ;
}
@@ -128,8 +138,15 @@ bool
Tree::SavePoint( double dU, double dV, Point3d& pt)
{
pair<int64_t, int64_t> key (static_cast<int64_t>(dU * pow(2,15)), static_cast<int64_t>(dV * pow(2,15))) ;
if ( m_mPt3d.find( key) == m_mPt3d.end())
bool bCalculated = true ;
{
lock_guard<mutex> lock( map3D) ;
bCalculated = m_mPt3d.find( key) != m_mPt3d.end() ;
}
if ( ! bCalculated){
lock_guard<mutex> lock( map3D) ;
m_mPt3d[key] = pt ;
}
return true ;
}
@@ -147,6 +164,7 @@ Tree::SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches, const Point3d& ptMi
m_mChunk.clear() ;
m_vPlApprox.clear() ;
m_vCCLoop2D.clear() ;
CellCounter = 0 ;
m_pSrfBz = pSrfBz ;
m_bSplitPatches = bSplitPatches ;
@@ -646,24 +664,30 @@ Tree::BuildTree_test( double dLinTol, double dSideMin, double dSideMax)
return true ;
}
mutex queueMutex ;
//----------------------------------------------------------------------------
void
Tree::BranchManager( queue<function<void()>>& tasks, condition_variable& cv, bool& done)
Tree::BranchManager( queue<function<void()>>& qTasks, condition_variable& cv, bool& done)
{
while (true) {
function<void()> task ;
{
std::unique_lock<std::mutex> lock(mapMutex);
cv.wait(lock, [&tasks, &done]() { return !tasks.empty() || done; });
std::unique_lock<std::mutex> lock( queueMutex) ;
cv.wait(lock, [&qTasks, &done]() { return ! qTasks.empty() || done; }) ;
if (tasks.empty() && done) {
return;
if ( qTasks.empty() && done) {
return ;
}
task = move(tasks.front());
tasks.pop();
task = std::move( qTasks.front()) ;
qTasks.pop() ;
// notifico il thread principale che non ci sono più task
if ( qTasks.empty() && ! done)
cv.notify_all() ;
}
task(); // Execute the task
task() ; // Execute the task
}
}
@@ -673,85 +697,108 @@ Tree::BuildTree( double dLinTol, double dSideMin, double dSideMax)
{
// lancio in parallelo vari thread in modo da calcolare separatamente i branch da ogni foglia attuale
int nThreadMax = ( thread::hardware_concurrency()) / 2 ;
int nStartingLeaves = m_vnLeaves.size() ;
int nStartingLeaves = m_vnParents.size() ;
condition_variable cv ;
vector<unordered_map<int, Cell>> vBranches( nStartingLeaves) ;
// ad ogni ramo, metto come cella di partenza una foglia
for ( int i = 0 ; i < nStartingLeaves ; ++i) {
Cell cLeaf = m_mTree.at( i) ;
vBranches[i].insert( pair<int,Cell>( i, cLeaf)) ;
INTVECTOR vFirstCells ;
// creo la coda dei task
queue<function<void()>> qTasks ;
bool done = false ;
for (int i = 0; i < nStartingLeaves; ++i) {
// ad ogni ramo, metto come cella di partenza una foglia
Cell cLeaf = m_mTree.at( m_vnParents[i]) ;
vBranches[i].insert( pair<int,Cell>( cLeaf.m_nId, cLeaf)) ;
int nFirst = cLeaf.m_nId ;
vFirstCells.push_back( nFirst) ;
qTasks.emplace([this, i, &vBranches, dLinTol, dSideMin, dSideMax, nFirst]
{Tree::BuildBranch( nFirst, vBranches[i], dLinTol, dSideMin, dSideMax) ;} ) ;
}
// Create worker threads
vector<thread> workers ;
for (int i = 0 ; i < nThreadMax ; ++i)
workers.emplace_back( &Tree::BranchManager, this, std::ref( qTasks), std::ref( cv),std::ref( done)) ;
queue<function<void()>> tasks;
// Add tasks to the queue
// dico ai thread di iniziare a lavorare
cv.notify_all() ;
// Wait for all tasks to be processed
{
std::lock_guard<std::mutex> lock(queueMutex);
for (int i = 0; i < nStartingLeaves; ++i) {
tasks.emplace([this]() {});
}
unique_lock<mutex> lock( queueMutex);
cv.wait( lock, [&qTasks] { return qTasks.empty(); }) ;
}
// Notify threads that tasks are available
cv.notify_all();
// Mark work as done and notify all threads to exit
// Wait for all tasks to be processed
{
std::lock_guard<std::mutex> lock(queueMutex);
done = true;
}
cv.notify_all();
// Wait for all threads to finish
for (auto& t : threads) {
t.join();
unique_lock<mutex> lock( queueMutex) ;
done = true ;
}
cv.notify_all() ;
vector<vector<pair<int, Cell>>> vectorBranches( nStartingLeaves);
// Join all threads
for ( auto& t : workers)
t.join() ;
// Step 1: Convert each map to a vector in parallel
for_each(execution::par, vectorBranches.begin(), vectorBranches.end(),
[&](auto& map) {
size_t index = &map - &vectorBranches[0]; // Get the index of the current map
vectorBranches[index].assign( make_move_iterator(map.begin()),
make_move_iterator(map.end()));
});
// debug
for ( int i = 0 ; i < nStartingLeaves ; ++i)
m_mTree.erase( vFirstCells[i]) ;
for ( int i = 0 ; i < nStartingLeaves ; ++i)
m_mTree.insert( vBranches[i].begin(), vBranches[i].end()) ;
// Step 2: Precompute total size and allocate final vector
size_t totalSize = accumulate(vectorBranches.begin(), vectorBranches.end(), 0,
[](size_t sum, const vector<pair<int, Cell>>& v) {
return sum + v.size();
}) ;
return true ;
// debug
vector<pair<int, Cell>> mergedVector( totalSize) ;
// Step 3: Assign thread-specific ranges and move elements in parallel
vector<size_t> offsets( nStartingLeaves + 1, 0) ;
for (size_t i = 0; i < nStartingLeaves; ++i)
offsets[i + 1] = offsets[i] + vectorBranches[i].size() ;
//vector<vector<pair<int, Cell>>> vectorBranches( nStartingLeaves);
for_each( execution::par, vectorBranches.begin(), vectorBranches.end(),
[&]( auto& vec) {
size_t index = &vec - &vectorBranches[0];
move( vec.begin(), vec.end(), mergedVector.begin() + offsets[index]) ;
});
//// Step 1: Convert each map to a vector in parallel
//for_each(execution::par, vBranches.begin(), vBranches.end(),
// [&](auto& map) {
// size_t index = &map - &vBranches[0]; // Get the index of the current map
// vectorBranches[index].assign( make_move_iterator(map.begin()),
// make_move_iterator(map.end()));
// });
// Step 4: Construct final unordered_map in one bulk step
//unordered_map<int, Cell> finalMap( mergedVector.begin(), mergedVector.end()) ;
Cell cRoot = m_mTree.at( -1) ;
m_mTree.clear() ;
m_mTree.insert( pair<int, Cell>(cRoot.m_nId, cRoot)) ;
m_mTree.insert( mergedVector.begin(), mergedVector.end()) ;
//// Step 2: Precompute total size and allocate final vector
//size_t totalSize = accumulate(vectorBranches.begin(), vectorBranches.end(), 0,
// [](size_t sum, const vector<pair<int, Cell>>& v) {
// return sum + v.size();
// }) ;
//vector<pair<int, Cell>> mergedVector( totalSize) ;
//// Step 3: Assign thread-specific ranges and move elements in parallel
//vector<size_t> offsets( nStartingLeaves + 1, 0) ;
//for (int i = 0; i < nStartingLeaves; ++i)
// offsets[i + 1] = offsets[i] + vectorBranches[i].size() ;
//for_each( execution::par, vectorBranches.begin(), vectorBranches.end(),
// [&]( auto& vec) {
// size_t index = &vec - &vectorBranches[0];
// move( vec.begin(), vec.end(), mergedVector.begin() + offsets[index]) ;
// });
//// Step 4: Construct final unordered_map in one bulk step
////unordered_map<int, Cell> finalMap( mergedVector.begin(), mergedVector.end()) ;
//Cell cRoot = m_mTree.at( -1) ;
//m_mTree.clear() ;
//unordered_map<int,Cell> mContainer( mergedVector.begin(), mergedVector.end()) ;
//m_mTree = std::move( mContainer) ;
//m_mTree.insert( pair<int, Cell>( cRoot.m_nId, cRoot)) ;
//return true ;
}
//----------------------------------------------------------------------------
bool
Tree::BuildBranch( double dLinTol, double dSideMin, double dSideMax, Cell* cBranchRoot, unordered_map<int, Cell>& mBranch)
Tree::BuildBranch( int nFirstCell, unordered_map<int, Cell>& mBranch, double dLinTol, double dSideMin, double dSideMax)
{
// suddivido lo spazio parametrico con divisioni a metà su uno dei due parametri
int nCToSplit = cBranchRoot->m_nId ;
int nBranchRoot = cBranchRoot->m_nId ;
Cell* pcToSplit = cBranchRoot ;
int nCToSplit = nFirstCell ;
Cell* pcToSplit = &mBranch.at(nFirstCell) ;
int nBranchRoot = pcToSplit->m_nParent ;
bool bIsPlanar = m_pSrfBz->IsPlanar() ;
if ( ! m_bBilinear) {
while ( pcToSplit->IsProcessed() == false) {
@@ -1017,7 +1064,7 @@ Tree::BuildBranch( double dLinTol, double dSideMin, double dSideMax, Cell* cBran
// procedo con lo split del Child1
nCToSplit = pcToSplit->m_nChild1 ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
else {
// sono arrivato ad una cella Leaf, quindi salvo la cella
@@ -1025,37 +1072,37 @@ Tree::BuildBranch( double dLinTol, double dSideMin, double dSideMax, Cell* cBran
pcToSplit->SetProcessed() ;
// risalgo i parent finché non trovo il primo Child2 da processare
nCToSplit = pcToSplit->m_nParent ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
if ( nCToSplit == nBranchRoot)
return true ;
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
if ( mBranch[pcToSplit->m_nChild1].IsProcessed() && mBranch[pcToSplit->m_nChild2].IsProcessed())
pcToSplit->SetProcessed() ;
while ( m_mTree[pcToSplit->m_nChild2].IsProcessed()) {
while ( mBranch[pcToSplit->m_nChild2].IsProcessed()) {
if ( pcToSplit->m_nParent != nBranchRoot) {
nCToSplit = pcToSplit->m_nParent ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
else
return true ;
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
if ( mBranch[pcToSplit->m_nChild1].IsProcessed() && mBranch[pcToSplit->m_nChild2].IsProcessed())
pcToSplit->SetProcessed() ;
if ( nCToSplit == -1 && m_mTree[pcToSplit->m_nChild2].IsProcessed())
if ( nCToSplit == -1 && mBranch[pcToSplit->m_nChild2].IsProcessed())
break ;
}
nCToSplit = pcToSplit->m_nChild2 ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
}
else {
nCToSplit = pcToSplit->m_nChild1 ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
}
Balance() ; // da implementare quando dividerò ad un parametro a scelta e non a metà // probabilmente mi servirà salvare nella cella il livello di profondità
}
// bilineare
else {
while ( nCToSplit != -2 && pcToSplit->IsProcessed() == false) {
while ( nCToSplit != nBranchRoot && pcToSplit->IsProcessed() == false) {
if ( pcToSplit->IsLeaf()) {
// vertici della cella
Point3d ptP00, ptP10, ptP11, ptP01 ;
@@ -1126,7 +1173,7 @@ Tree::BuildBranch( double dLinTol, double dSideMin, double dSideMax, Cell* cBran
// procedo con lo split del Child1
nCToSplit = pcToSplit->m_nChild1 ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
else {
// sono arrivato ad una cella Leaf, quindi salvo la cella
@@ -1134,28 +1181,28 @@ Tree::BuildBranch( double dLinTol, double dSideMin, double dSideMax, Cell* cBran
pcToSplit->SetProcessed() ;
// risalgo i parent finché non trovo il primo Child2 da processare
nCToSplit = pcToSplit->m_nParent ;
pcToSplit = &m_mTree[nCToSplit] ;
if ( nCToSplit == -2)
pcToSplit = &mBranch[nCToSplit] ;
if ( nCToSplit == nBranchRoot)
return true ;
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
if ( mBranch[pcToSplit->m_nChild1].IsProcessed() && mBranch[pcToSplit->m_nChild2].IsProcessed())
pcToSplit->SetProcessed() ;
while ( m_mTree[pcToSplit->m_nChild2].IsProcessed()) {
if ( pcToSplit->m_nParent != -2) {
while ( mBranch[pcToSplit->m_nChild2].IsProcessed()) {
if ( pcToSplit->m_nParent != nBranchRoot) {
nCToSplit = pcToSplit->m_nParent ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
if ( mBranch[pcToSplit->m_nChild1].IsProcessed() && mBranch[pcToSplit->m_nChild2].IsProcessed())
pcToSplit->SetProcessed() ;
if ( nCToSplit == -1 && m_mTree[pcToSplit->m_nChild2].IsProcessed())
if ( nCToSplit == -1 && mBranch[pcToSplit->m_nChild2].IsProcessed())
break ;
}
nCToSplit = pcToSplit->m_nChild2 ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
}
else {
nCToSplit = pcToSplit->m_nChild1 ;
pcToSplit = &m_mTree[nCToSplit] ;
pcToSplit = &mBranch[nCToSplit] ;
}
}
}
@@ -1173,6 +1220,8 @@ Tree::Balance()
// al momento il problema viene bypassato in fase di generazione dei poligoni, considerando per ogni cella, oltre ai propri vertici
// i vertici dei vicini che giacciono sui suoi lati
// in realtà non è vero: perchè non basta avere più triangoli nella singola cella, può servire proprio spezzare la cella e avere più celle che rappresentano
// facce diverse (quindi con pendenze diverse)
}
//----------------------------------------------------------------------------
+6 -3
View File
@@ -21,6 +21,9 @@
#include "/EgtDev/Include/EGkChainCurves.h"
#include <map>
#include <utility>
#include <queue>
#include <functional>
#include <condition_variable>
struct PairHashInt64 {
size_t operator()(const pair<int64_t, int64_t>& key) const {
@@ -249,16 +252,16 @@ class Tree
Tree( const Point3d ptBl, const Point3d ptTr) ; // creatore da usare solo nel caso in cui si voglia aggiungere tagli ad un'unica cella e del risultato ottenere il contorno
bool SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches = true, const Point3d& ptMin = ORIG, const Point3d& ptMax = ORIG) ;
bool GetIndependentTrees( BIPNTVECTOR& vTrees) ; // calcolo la suddivisione della superficie solo sulle singole bbox dei loop di trim ( unendo quelli vicini)
void BranchManager( queue<std::function<void()>>& tasks, condition_variable& cv, bool& done) ;
void BranchManager( queue<function<void()>>& tasks, condition_variable& cv, bool& done) ;
bool BuildTree( double dLinTol = LIN_TOL_STD, double dSideMin = 1, double dSideMax = INFINITO) ;
bool BuildBranch( double dLinTol = LIN_TOL_STD, double dSideMin = 1, double dSideMax = INFINITO, Cell* cBranchRoot, unordered_map<int, Cell>& mBranch) ; // dSideMax è il massimo per la dimensione maggiore di un triangolo della trimesh
bool BuildBranch( int nFirstCell, unordered_map<int, Cell>& mBranch, double dLinTol = LIN_TOL_STD, double dSideMin = 1, double dSideMax = INFINITO) ; // dSideMax è il massimo per la dimensione maggiore di un triangolo della trimesh
// dSideMin è lunghezza minima del lato di una cella nello spazio reale
bool BuildTree_test( double dLinTol = LIN_TOL_STD, double dSideMin = 1, double dSideMax = INFINITO) ;
bool GetPolygons( POLYLINEMATRIX& vvPolygons, POLYLINEMATRIX& vvPolygons3d, vector<ICRVCOMPOPOVECTOR>& vCCEdges3D, ICRVCOMPOPOVECTOR& vCCLoops) ;
bool GetPolygonsBasic( POLYLINEVECTOR& vPolygons, POLYLINEVECTOR& vPolygonsCorrected, POLYLINEVECTOR& vPolygons3d) ; // restituisce il poligono corrispondente ad ogni cella foglia dell'albero
// ad ogni poligono sono stati aggiunti tutti i vertici dei vicini posizionati sui suoi lati
// ad alcuni poligoni potrebbero venire tolti dei punti per evitare errori dovuti ad eventuali poli sui bordi del parametrico
bool GetLeaves ( std::vector<Cell>& vLeaves) const ; // restituisce gli indici delle foglie nell'albero
bool GetLeaves ( vector<Cell>& vLeaves) const ; // restituisce gli indici delle foglie nell'albero
bool GetEdges3D ( vector<ICRVCOMPOPOVECTOR>& mCCEdge, POLYLINEVECTOR& vPolygons) ; // restituisce gli edge 3D come polyline
bool GetSplitLoops( ICRVCOMPOPOVECTOR& vCCLoopSplit) const // funzione che restituisce i loop splitatti ai confini delle celle
{ for ( int i = 0 ; i < int( m_vCCLoop2D.size()); ++i) vCCLoopSplit.emplace_back( m_vCCLoop2D[i]->Clone()) ; return true ; };