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:
Seth Hillbrand 2018-10-25 04:20:12 -07:00
parent 256f1a26e6
commit 2d8f1df293
2 changed files with 48 additions and 50 deletions

View File

@ -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

View File

@ -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();