summaryrefslogtreecommitdiff
path: root/src/vm/tieredcompilation.cpp
diff options
context:
space:
mode:
authorKoundinya Veluri <kouvel@users.noreply.github.com>2018-01-25 12:01:32 -0800
committerGitHub <noreply@github.com>2018-01-25 12:01:32 -0800
commit209415618ca5d1a5d1d9e39ca78d643d0935534e (patch)
treed83c946783390afbb52e3e0f968018c38dfd2560 /src/vm/tieredcompilation.cpp
parente9985126acb0f1efd7c780faac4e66bc798b73c0 (diff)
downloadcoreclr-209415618ca5d1a5d1d9e39ca78d643d0935534e.tar.gz
coreclr-209415618ca5d1a5d1d9e39ca78d643d0935534e.tar.bz2
coreclr-209415618ca5d1a5d1d9e39ca78d643d0935534e.zip
Enable tiered jitting for R2R methods (#15967)
Enable tiered jitting for R2R methods - Included R2R methods and generics over value types in CoreLib for tiered jitting. Tier 0 for R2R methods is the precompiled code if available, and tier 1 is selectively scheduled based on call counting. - Added a delay before starting to count calls for tier 1 promotion. The delay is a short duration after frequent tier 0 jitting stops (current heuristic for identifying startup). - Startup time and steady-state performance have improved on JitBench. There is a regression shortly following startup due to call counting and tier 1 jitting, for a short duration before steady-state performance stabilizes. - Added two new config values, one for configuring the call count threshold for promoting to tier 1, and another for specifying the delay from the last tier 0 JIT invocation before starting to count calls
Diffstat (limited to 'src/vm/tieredcompilation.cpp')
-rw-r--r--src/vm/tieredcompilation.cpp179
1 files changed, 169 insertions, 10 deletions
diff --git a/src/vm/tieredcompilation.cpp b/src/vm/tieredcompilation.cpp
index 48c6670fb2..f89f4f2e6b 100644
--- a/src/vm/tieredcompilation.cpp
+++ b/src/vm/tieredcompilation.cpp
@@ -81,11 +81,16 @@
TieredCompilationManager::TieredCompilationManager() :
m_isAppDomainShuttingDown(FALSE),
m_countOptimizationThreadsRunning(0),
- m_callCountOptimizationThreshhold(30),
- m_optimizationQuantumMs(50)
+ m_callCountOptimizationThreshhold(1),
+ m_optimizationQuantumMs(50),
+ m_methodsPendingCountingForTier1(nullptr),
+ m_tier1CountingDelayTimerHandle(nullptr),
+ m_wasTier0JitInvokedSinceCountingDelayReset(false)
{
LIMITED_METHOD_CONTRACT;
m_lock.Init(LOCK_TYPE_DEFAULT);
+
+ // On Unix, we can reach here before EEConfig is initialized, so defer config-based initialization to Init()
}
// Called at AppDomain Init
@@ -102,29 +107,115 @@ void TieredCompilationManager::Init(ADID appDomainId)
SpinLockHolder holder(&m_lock);
m_domainId = appDomainId;
+ m_callCountOptimizationThreshhold = g_pConfig->TieredCompilation_Tier1CallCountThreshold();
m_asyncWorkDoneEvent.CreateManualEventNoThrow(TRUE);
}
+void TieredCompilationManager::InitiateTier1CountingDelay()
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(g_pConfig->TieredCompilation());
+ _ASSERTE(m_methodsPendingCountingForTier1 == nullptr);
+ _ASSERTE(m_tier1CountingDelayTimerHandle == nullptr);
+
+ DWORD delayMs = g_pConfig->TieredCompilation_Tier1CallCountingDelayMs();
+ if (delayMs == 0)
+ {
+ return;
+ }
+
+ m_tier1CountingDelayLock.Init(LOCK_TYPE_DEFAULT);
+
+ NewHolder<SArray<MethodDesc*>> methodsPendingCountingHolder = new(nothrow) SArray<MethodDesc*>();
+ if (methodsPendingCountingHolder == nullptr)
+ {
+ return;
+ }
+
+ NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new(nothrow) ThreadpoolMgr::TimerInfoContext();
+ if (timerContextHolder == nullptr)
+ {
+ return;
+ }
+
+ timerContextHolder->AppDomainId = m_domainId;
+ timerContextHolder->TimerId = 0;
+ if (!ThreadpoolMgr::CreateTimerQueueTimer(
+ &m_tier1CountingDelayTimerHandle,
+ Tier1DelayTimerCallback,
+ timerContextHolder,
+ delayMs,
+ (DWORD)-1 /* Period, non-repeating */,
+ 0 /* flags */))
+ {
+ _ASSERTE(m_tier1CountingDelayTimerHandle == nullptr);
+ return;
+ }
+
+ m_methodsPendingCountingForTier1 = methodsPendingCountingHolder.Extract();
+ timerContextHolder.SuppressRelease(); // the timer context is automatically deleted by the timer infrastructure
+}
+
+void TieredCompilationManager::OnTier0JitInvoked()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_methodsPendingCountingForTier1 != nullptr)
+ {
+ m_wasTier0JitInvokedSinceCountingDelayReset = true;
+ }
+}
+
// Called each time code in this AppDomain has been run. This is our sole entrypoint to begin
// tiered compilation for now. Returns TRUE if no more notifications are necessary, but
// more notifications may come anyways.
//
// currentCallCount is pre-incremented, that is to say the value is 1 on first call for a given
// method.
-BOOL TieredCompilationManager::OnMethodCalled(MethodDesc* pMethodDesc, DWORD currentCallCount)
+void TieredCompilationManager::OnMethodCalled(
+ MethodDesc* pMethodDesc,
+ DWORD currentCallCount,
+ BOOL* shouldStopCountingCallsRef,
+ BOOL* wasPromotedToTier1Ref)
{
- STANDARD_VM_CONTRACT;
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(pMethodDesc->IsEligibleForTieredCompilation());
+ _ASSERTE(shouldStopCountingCallsRef != nullptr);
+ _ASSERTE(wasPromotedToTier1Ref != nullptr);
+
+ *shouldStopCountingCallsRef =
+ m_methodsPendingCountingForTier1 != nullptr || currentCallCount >= m_callCountOptimizationThreshhold;
+ *wasPromotedToTier1Ref = currentCallCount >= m_callCountOptimizationThreshhold;
+
+ if (currentCallCount == m_callCountOptimizationThreshhold)
+ {
+ AsyncPromoteMethodToTier1(pMethodDesc);
+ }
+}
+
+void TieredCompilationManager::OnMethodCallCountingStoppedWithoutTier1Promotion(MethodDesc* pMethodDesc)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(pMethodDesc != nullptr);
+ _ASSERTE(pMethodDesc->IsEligibleForTieredCompilation());
- if (currentCallCount < m_callCountOptimizationThreshhold)
+ if (g_pConfig->TieredCompilation_Tier1CallCountingDelayMs() == 0)
{
- return FALSE; // continue notifications for this method
+ return;
}
- else if (currentCallCount > m_callCountOptimizationThreshhold)
+
{
- return TRUE; // stop notifications for this method
+ SpinLockHolder holder(&m_tier1CountingDelayLock);
+ if (m_methodsPendingCountingForTier1 != nullptr)
+ {
+ // Record the method to resume counting later (see Tier1DelayTimerCallback)
+ m_methodsPendingCountingForTier1->Append(pMethodDesc);
+ return;
+ }
}
- AsyncPromoteMethodToTier1(pMethodDesc);
- return TRUE;
+
+ // Rare race condition with the timer callback
+ ResumeCountingCalls(pMethodDesc);
}
void TieredCompilationManager::AsyncPromoteMethodToTier1(MethodDesc* pMethodDesc)
@@ -258,6 +349,74 @@ void TieredCompilationManager::Shutdown(BOOL fBlockUntilAsyncWorkIsComplete)
}
}
+VOID WINAPI TieredCompilationManager::Tier1DelayTimerCallback(PVOID parameter, BOOLEAN timerFired)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(timerFired);
+
+ GCX_COOP();
+ ThreadpoolMgr::TimerInfoContext* timerContext = (ThreadpoolMgr::TimerInfoContext*)parameter;
+ ManagedThreadBase::ThreadPool(timerContext->AppDomainId, Tier1DelayTimerCallbackInAppDomain, nullptr);
+}
+
+void TieredCompilationManager::Tier1DelayTimerCallbackInAppDomain(LPVOID parameter)
+{
+ WRAPPER_NO_CONTRACT;
+ GetAppDomain()->GetTieredCompilationManager()->Tier1DelayTimerCallbackWorker();
+}
+
+void TieredCompilationManager::Tier1DelayTimerCallbackWorker()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Reschedule the timer if a tier 0 JIT has been invoked since the timer was started to further delay call counting
+ if (m_wasTier0JitInvokedSinceCountingDelayReset)
+ {
+ m_wasTier0JitInvokedSinceCountingDelayReset = false;
+
+ _ASSERTE(m_tier1CountingDelayTimerHandle != nullptr);
+ if (ThreadpoolMgr::ChangeTimerQueueTimer(
+ m_tier1CountingDelayTimerHandle,
+ g_pConfig->TieredCompilation_Tier1CallCountingDelayMs(),
+ (DWORD)-1 /* Period, non-repeating */))
+ {
+ return;
+ }
+ }
+
+ // Exchange the list of methods pending counting for tier 1
+ SArray<MethodDesc*>* methodsPendingCountingForTier1;
+ {
+ SpinLockHolder holder(&m_tier1CountingDelayLock);
+ methodsPendingCountingForTier1 = m_methodsPendingCountingForTier1;
+ _ASSERTE(methodsPendingCountingForTier1 != nullptr);
+ m_methodsPendingCountingForTier1 = nullptr;
+ }
+
+ // Install call counters
+ MethodDesc** methods = methodsPendingCountingForTier1->GetElements();
+ COUNT_T methodCount = methodsPendingCountingForTier1->GetCount();
+ for (COUNT_T i = 0; i < methodCount; ++i)
+ {
+ ResumeCountingCalls(methods[i]);
+ }
+ delete methodsPendingCountingForTier1;
+
+ // Delete the timer
+ _ASSERTE(m_tier1CountingDelayTimerHandle != nullptr);
+ ThreadpoolMgr::DeleteTimerQueueTimer(m_tier1CountingDelayTimerHandle, nullptr);
+ m_tier1CountingDelayTimerHandle = nullptr;
+}
+
+void TieredCompilationManager::ResumeCountingCalls(MethodDesc* pMethodDesc)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(pMethodDesc != nullptr);
+ _ASSERTE(pMethodDesc->IsVersionableWithPrecode());
+
+ pMethodDesc->GetPrecode()->ResetTargetInterlocked();
+}
+
// This is the initial entrypoint for the background thread, called by
// the threadpool.
DWORD WINAPI TieredCompilationManager::StaticOptimizeMethodsCallback(void *args)