Threading: Minimize thread usage
This minimizes the thread usage, particularly during online actions such as moving items and routing. UpdateRatsnest: Minimized the number of atomics by utilizing future promise. Avoided updating node-less nets. Node-less nets are common in the dynamic ratsnest because the net vector is created from the board nets by value but only populated with a subset. So the dynamic ratsnest calculation always saw every net as dirty and spun up extra threads. searchConnections: ParallelThreadCount is no longer lower bounded. Small item counts are handled inline rather than with a separate thread.
This commit is contained in:
parent
256f1a26e6
commit
2d8f1df293
|
@ -221,40 +221,43 @@ void CN_CONNECTIVITY_ALGO::searchConnections()
|
|||
std::atomic<size_t> nextItem( 0 );
|
||||
std::atomic<size_t> threadsFinished( 0 );
|
||||
|
||||
size_t parallelThreadCount = std::min<size_t>(
|
||||
std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
|
||||
dirtyItems.size() );
|
||||
|
||||
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
|
||||
auto conn_lambda = [&nextItem, &threadsFinished, &dirtyItems]
|
||||
( CN_LIST* aItemList, PROGRESS_REPORTER* aReporter)
|
||||
{
|
||||
std::thread t = std::thread( [&nextItem, &threadsFinished, &dirtyItems, this]()
|
||||
for( size_t i = nextItem++; i < dirtyItems.size(); i = nextItem++ )
|
||||
{
|
||||
for( size_t i = nextItem.fetch_add( 1 );
|
||||
i < dirtyItems.size();
|
||||
i = nextItem.fetch_add( 1 ) )
|
||||
{
|
||||
CN_VISITOR visitor( dirtyItems[i] );
|
||||
m_itemList.FindNearby( dirtyItems[i], visitor );
|
||||
CN_VISITOR visitor( dirtyItems[i] );
|
||||
aItemList->FindNearby( dirtyItems[i], visitor );
|
||||
|
||||
if( m_progressReporter )
|
||||
m_progressReporter->AdvanceProgress();
|
||||
}
|
||||
if( aReporter )
|
||||
aReporter->AdvanceProgress();
|
||||
}
|
||||
|
||||
threadsFinished++;
|
||||
} );
|
||||
threadsFinished++;
|
||||
};
|
||||
|
||||
t.detach();
|
||||
}
|
||||
size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
|
||||
( dirtyItems.size() + 7 ) / 8 );
|
||||
|
||||
// Finalize the connectivity threads
|
||||
while( threadsFinished < parallelThreadCount )
|
||||
if( parallelThreadCount <= 1 )
|
||||
conn_lambda( &m_itemList, m_progressReporter );
|
||||
else
|
||||
{
|
||||
if( m_progressReporter )
|
||||
m_progressReporter->KeepRefreshing();
|
||||
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
|
||||
std::thread( conn_lambda, &m_itemList, m_progressReporter ).detach();
|
||||
|
||||
// This routine is called every click while routing so keep the sleep time minimal
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );
|
||||
// Wait for connectivity threads to finish while updating the UI if set
|
||||
while( threadsFinished < parallelThreadCount )
|
||||
{
|
||||
if( m_progressReporter )
|
||||
m_progressReporter->KeepRefreshing();
|
||||
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_progressReporter )
|
||||
m_progressReporter->KeepRefreshing();
|
||||
}
|
||||
|
||||
#ifdef PROFILE
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <future>
|
||||
|
||||
#include <connectivity/connectivity_data.h>
|
||||
#include <connectivity/connectivity_algo.h>
|
||||
|
@ -100,42 +101,36 @@ void CONNECTIVITY_DATA::updateRatsnest()
|
|||
std::vector<RN_NET*> dirty_nets;
|
||||
|
||||
// Start with net 1 as net 0 is reserved for not-connected
|
||||
// Nets without nodes are also ignored
|
||||
std::copy_if( m_nets.begin() + 1, m_nets.end(), std::back_inserter( dirty_nets ),
|
||||
[] ( RN_NET* aNet ) { return aNet->IsDirty(); } );
|
||||
|
||||
std::atomic<size_t> nextNet( 0 );
|
||||
std::atomic<size_t> threadsFinished( 0 );
|
||||
|
||||
auto update_lambda = [&nextNet, &threadsFinished, &dirty_nets, this]()
|
||||
{
|
||||
for( size_t i = nextNet++; i < dirty_nets.size(); i = nextNet++ )
|
||||
{
|
||||
dirty_nets[i]->Update();
|
||||
}
|
||||
|
||||
threadsFinished++;
|
||||
};
|
||||
[] ( RN_NET* aNet ) { return aNet->IsDirty() && aNet->GetNodeCount() > 0; } );
|
||||
|
||||
// We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
|
||||
size_t parallelThreadCount = std::min<size_t>(
|
||||
std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
|
||||
size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
|
||||
( dirty_nets.size() + 7 ) / 8 );
|
||||
|
||||
// This prevents generating a thread for point while routing as we are only
|
||||
// updating the ratsnest on a single net
|
||||
std::atomic<size_t> nextNet( 0 );
|
||||
std::vector<std::future<size_t>> returns( parallelThreadCount );
|
||||
|
||||
auto update_lambda = [&nextNet, &dirty_nets]() -> size_t
|
||||
{
|
||||
for( size_t i = nextNet++; i < dirty_nets.size(); i = nextNet++ )
|
||||
dirty_nets[i]->Update();
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
if( parallelThreadCount == 1 )
|
||||
update_lambda();
|
||||
else
|
||||
{
|
||||
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
|
||||
{
|
||||
std::thread( update_lambda ).detach();
|
||||
}
|
||||
}
|
||||
returns[ii] = std::async( update_lambda );
|
||||
|
||||
// Finalize the ratsnest threads
|
||||
while( threadsFinished < parallelThreadCount )
|
||||
std::this_thread::yield();
|
||||
// Finalize the ratsnest threads
|
||||
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
|
||||
returns[ii].wait();
|
||||
}
|
||||
|
||||
#ifdef PROFILE
|
||||
rnUpdate.Show();
|
||||
|
|
Loading…
Reference in New Issue