Expose ThreadPool_UnfairSemaphoreSpinLimit config
authorLukasz Tomczyk <lutom@microsoft.com>
Mon, 18 Jul 2016 16:28:12 +0000 (09:28 -0700)
committerLukasz Tomczyk <lutom@microsoft.com>
Tue, 2 Aug 2016 23:14:43 +0000 (16:14 -0700)
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 #5928

src/inc/clrconfigvalues.h
src/vm/win32threadpool.cpp
src/vm/win32threadpool.h

index 1cb9ede..d6a362e 100644 (file)
@@ -955,6 +955,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_ThreadPool_ForceMaxWorkerThreads, W("ThreadPoo
 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, "")
index 35c2b64..95fa5c0 100644 (file)
@@ -359,7 +359,8 @@ BOOL ThreadpoolMgr::Initialize()
         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);
index 943c358..ba800e5 100644 (file)
@@ -146,7 +146,8 @@ class ThreadpoolMgr
         } 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];
@@ -183,7 +184,8 @@ class ThreadpoolMgr
 
     public:
 
-        UnfairSemaphore(int maxCount)
+        UnfairSemaphore(int maxCount, int spinLimitPerProcessor)
+            : m_spinLimitPerProcessor(spinLimitPerProcessor)
         {
             CONTRACTL
             {
@@ -277,7 +279,6 @@ class ThreadpoolMgr
             // Now we're a spinner.  
             //
             int numSpins = 0;
-            const int spinLimitPerProcessor = 50;
             while (true)
             {
                 Counts currentCounts, newCounts;
@@ -295,7 +296,7 @@ class ThreadpoolMgr
                 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--;