diff options
author | Koundinya Veluri <kouvel@users.noreply.github.com> | 2018-01-25 12:01:32 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-25 12:01:32 -0800 |
commit | 209415618ca5d1a5d1d9e39ca78d643d0935534e (patch) | |
tree | d83c946783390afbb52e3e0f968018c38dfd2560 /src/vm/tieredcompilation.cpp | |
parent | e9985126acb0f1efd7c780faac4e66bc798b73c0 (diff) | |
download | coreclr-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.cpp | 179 |
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) |