- 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
+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)
}
//----------------------------------------------------------------------------