Spin-wait in ThreadpoolMgr::UnfairSemaphore::Wait can cause significant
CPU usage, which is unnecessary in some scenarios, where threadpool
receives work in bursts. Use of the above parameter will allow fine tuning
of the threadpool behavior based on the workload expectation.
Fix dotnet/coreclr#5928
Commit migrated from https://github.com/dotnet/coreclr/commit/
0504bf7c2d8003e81b0c5ad6392831f36781470a
RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_DisableStarvationDetection, W("ThreadPool_DisableStarvationDetection"), 0, "Disables the ThreadPool feature that forces new threads to be added when workitems run for too long")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_DebugBreakOnWorkerStarvation, W("ThreadPool_DebugBreakOnWorkerStarvation"), 0, "Breaks into the debugger if the ThreadPool detects work queue starvation")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_EnableWorkerTracking, W("ThreadPool_EnableWorkerTracking"), 0, "Enables extra expensive tracking of how many workers threads are working simultaneously")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_UnfairSemaphoreSpinLimit, W("ThreadPool_UnfairSemaphoreSpinLimit"), 50, "Per processor limit used when calculating spin duration in UnfairSemaphore::Wait")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_Thread_UseAllCpuGroups, W("Thread_UseAllCpuGroups"), 0, "Specifies if to automatically distribute thread across CPU Groups")
CONFIG_DWORD_INFO(INTERNAL_ThreadpoolTickCountAdjustment, W("ThreadpoolTickCountAdjustment"), 0, "")
RetiredCPWakeupEvent->CreateAutoEvent(FALSE);
_ASSERTE(RetiredCPWakeupEvent->IsValid());
- WorkerSemaphore = new UnfairSemaphore(ThreadCounter::MaxPossibleCount);
+ int spinLimitPerProcessor = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_UnfairSemaphoreSpinLimit);
+ WorkerSemaphore = new UnfairSemaphore(ThreadCounter::MaxPossibleCount, spinLimitPerProcessor);
RetiredWorkerSemaphore = new CLRSemaphore();
RetiredWorkerSemaphore->Create(0, ThreadCounter::MaxPossibleCount);
} m_counts;
private:
- CLRSemaphore m_sem; //waiters wait on this
+ const int m_spinLimitPerProcessor; //used when calculating max spin duration
+ CLRSemaphore m_sem; //waiters wait on this
// padding to ensure we get our own cache line
BYTE padding2[64];
public:
- UnfairSemaphore(int maxCount)
+ UnfairSemaphore(int maxCount, int spinLimitPerProcessor)
+ : m_spinLimitPerProcessor(spinLimitPerProcessor)
{
CONTRACTL
{
// Now we're a spinner.
//
int numSpins = 0;
- const int spinLimitPerProcessor = 50;
while (true)
{
Counts currentCounts, newCounts;
else
{
double spinnersPerProcessor = (double)currentCounts.spinners / ThreadpoolMgr::NumberOfProcessors;
- int spinLimit = (int)((spinLimitPerProcessor / spinnersPerProcessor) + 0.5);
+ int spinLimit = (int)((m_spinLimitPerProcessor / spinnersPerProcessor) + 0.5);
if (numSpins >= spinLimit)
{
newCounts.spinners--;