From 07de0690dbe9d036c345d93f7f57825ee33b9ea0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 23 Jun 2022 13:57:04 +0200 Subject: [PATCH 01/53] R2R -> InstrumentedTier0 for hot R2R methods --- src/coreclr/inc/clrconfigvalues.h | 12 ++++- src/coreclr/inc/corjitflags.h | 2 +- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.hpp | 5 +++ src/coreclr/jit/fgbasic.cpp | 6 +++ src/coreclr/jit/importer.cpp | 6 +++ src/coreclr/jit/jitee.h | 2 +- src/coreclr/vm/callcounting.cpp | 65 ++++++++++++++++++++++++---- src/coreclr/vm/callcounting.h | 7 ++- src/coreclr/vm/codeversion.cpp | 11 ++++- src/coreclr/vm/codeversion.h | 2 + src/coreclr/vm/eeconfig.cpp | 8 ++++ src/coreclr/vm/interpreter.cpp | 2 +- src/coreclr/vm/prestub.cpp | 8 ++-- src/coreclr/vm/tieredcompilation.cpp | 46 ++++++++++++++++++-- 15 files changed, 159 insertions(+), 25 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 7832490029ce37..55ad2de895e125 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -572,11 +572,16 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_HillClimbing_GainExponent, #define TC_CallCountThreshold (2) #define TC_CallCountingDelayMs (1) #define TC_DelaySingleProcMultiplier (2) +#define TC_DelayPgoDivider (1) #else // !_DEBUG #define TC_BackgroundWorkerTimeoutMs (4000) -#define TC_CallCountThreshold (30) +#define TC_CallCountThreshold (30) // TODO: Find better defaults for PGO #define TC_CallCountingDelayMs (100) #define TC_DelaySingleProcMultiplier (10) +#define TC_DelayPgoDivider (3) // in PGO mode we compile way more methods and don't want to stuck in tier0 + // waiting for a Delay tick to find out it's busy (see IsTieringDelayActive()) + // and wait again and again + #endif // _DEBUG RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredCompilation, W("TieredCompilation"), 1, "Enables tiered compilation") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_QuickJit, W("TC_QuickJit"), 1, "For methods that would be jitted, enable using quick JIT when appropriate.") @@ -590,6 +595,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_BackgroundWorkerTimeoutMs, W("TC_Background RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountThreshold, W("TC_CallCountThreshold"), TC_CallCountThreshold, "Number of times a method must be called in tier 0 after which it is promoted to the next tier.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountingDelayMs, W("TC_CallCountingDelayMs"), TC_CallCountingDelayMs, "A perpetual delay in milliseconds that is applied call counting in tier 0 and jitting at higher tiers, while there is startup-like activity.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DelaySingleProcMultiplier, W("TC_DelaySingleProcMultiplier"), TC_DelaySingleProcMultiplier, "Multiplier for TC_CallCountingDelayMs that is applied on a single-processor machine or when the process is affinitized to a single processor.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_DelayPgoDivider, W("TC_DelayPgoDivider"), TC_DelayPgoDivider, "PGO-specific Divider for TC_CallCountingDelayMs, needed because we jit more methods") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_UseCallCountingStubs, W("TC_UseCallCountingStubs"), 1, "Uses call counting stubs for faster call counting.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteCallCountingStubsAfter"), 0, "Deletes call counting stubs after this many have completed. Zero to disable deleting.") @@ -617,7 +623,9 @@ CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of e RETAIL_CONFIG_STRING_INFO(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") -RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") + +// Enable for CI tests +RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 1, "Instrument Tier0 code and make counts available to Tier1") #endif /// diff --git a/src/coreclr/inc/corjitflags.h b/src/coreclr/inc/corjitflags.h index 71e7af410eb94c..7d7eef15e94273 100644 --- a/src/coreclr/inc/corjitflags.h +++ b/src/coreclr/inc/corjitflags.h @@ -97,7 +97,7 @@ class CORJIT_FLAGS CORJIT_FLAG_UNUSED16 = 43, #endif // !defined(TARGET_ARM) - CORJIT_FLAG_UNUSED17 = 44, + CORJIT_FLAG_PROMOTED = 44, // Method was promoted at least once and is likely hot CORJIT_FLAG_UNUSED18 = 45, CORJIT_FLAG_UNUSED19 = 46, CORJIT_FLAG_UNUSED20 = 47, diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 0ac148774aaf68..cb0a6e376d9c86 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -6668,7 +6668,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, } } - if (reason != nullptr) + if ((reason != nullptr) && !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) { fgSwitchToOptimized(reason); } diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 294151904e0a0c..80366176ec7d58 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4759,6 +4759,11 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason) { whyNot = "OSR can't handle reverse pinvoke"; } + else if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) + { + // This method will likely make it to tier1 on its own soon + whyNot = "OSR is not needed in an already promoted tier"; + } #else whyNot = "OSR feature not defined in build"; #endif diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 541bdc27e7c962..f421ef85233a58 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -3568,6 +3568,12 @@ void Compiler::fgCheckForLoopsInHandlers() return; } + if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) + { + // This method will likely make it to tier1 on its own soon + return; + } + if (info.compXcptnsCount == 0) { return; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 3c01e665ddc327..3b0cee6a7042ad 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -12388,6 +12388,12 @@ void Compiler::impImportBlockCode(BasicBlock* block) bool enablePatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.TC_OnStackReplacement() > 0); + if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) + { + // This method will likely make it to tier1 on its own soon + enablePatchpoints = false; + } + #ifdef DEBUG // Optionally suppress patchpoints by method hash diff --git a/src/coreclr/jit/jitee.h b/src/coreclr/jit/jitee.h index 96c94ef0af4954..a47f698d06ad64 100644 --- a/src/coreclr/jit/jitee.h +++ b/src/coreclr/jit/jitee.h @@ -87,7 +87,7 @@ class JitFlags JIT_FLAG_UNUSED16 = 43, #endif // !defined(TARGET_ARM) - JIT_FLAG_UNUSED17 = 44, + JIT_FLAG_PROMOTED = 44, // Method was promoted at least once and is likely hot JIT_FLAG_UNUSED18 = 45, JIT_FLAG_UNUSED19 = 46, JIT_FLAG_UNUSED20 = 47, diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index 344f68b17b5cad..bc991c3e09bd97 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -147,13 +147,18 @@ FORCEINLINE void CallCountingManager::CallCountingInfo::SetStage(Stage stage) --s_activeCallCountingStubCount; break; + case Stage::PendingReset: + _ASSERT(m_stage >= Stage::PendingCompletion); + --s_activeCallCountingStubCount; + break; + case Stage::StubMayBeActive: _ASSERTE(m_callCountingStub != nullptr); FALLTHROUGH; case Stage::PendingCompletion: - _ASSERTE(m_stage == Stage::StubIsNotActive || m_stage == Stage::StubMayBeActive); - if (m_stage == Stage::StubIsNotActive && m_callCountingStub != nullptr) + _ASSERTE(m_stage == Stage::StubIsNotActive || m_stage == Stage::StubMayBeActive || m_stage == Stage::PendingReset); + if ((m_stage == Stage::StubIsNotActive || m_stage == Stage::PendingReset) && m_callCountingStub != nullptr) { ++s_activeCallCountingStubCount; } @@ -178,6 +183,13 @@ FORCEINLINE void CallCountingManager::CallCountingInfo::SetStage(Stage stage) m_stage = stage; } + +void CallCountingManager::CallCountingInfo::ReuseForNewVersion(NativeCodeVersion version) +{ + WRAPPER_NO_CONTRACT; + m_codeVersion = version; + SetStage(Stage::PendingReset); +} #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -539,6 +551,28 @@ void CallCountingManager::DisableCallCounting(NativeCodeVersion codeVersion) callCountingInfoHolder.SuppressRelease(); } +void CallCountingManager::ReuseStubForNewVersion(NativeCodeVersion oldVersion, NativeCodeVersion newVersion) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + CodeVersionManager::LockHolder codeVersioningLockHolder; + CallCountingInfo* callCountingInfo = m_callCountingInfoByCodeVersionHash.Lookup(oldVersion); + + // No problem if it's null, it's just an optimization to allocate less call counting stubs + if (callCountingInfo != nullptr) + { + m_callCountingInfoByCodeVersionHash.Remove(oldVersion); + callCountingInfo->ReuseForNewVersion(newVersion); + m_callCountingInfoByCodeVersionHash.Add(callCountingInfo); + } +} + // Returns true if the code entry point was updated to reflect the active code version, false otherwise. In normal paths, the // code entry point is not updated only when the use of call counting stubs is disabled, as in that case returning to the // prestub is necessary for further call counting. On exception, the code entry point may or may not have been updated and it's @@ -574,7 +608,7 @@ bool CallCountingManager::SetCodeEntryPoint( // For a default code version that is not tier 0, call counting will have been disabled by this time (checked // below). Avoid the redundant and not-insignificant expense of GetOptimizationTier() on a default code version. !activeCodeVersion.IsDefaultVersion() && - activeCodeVersion.GetOptimizationTier() != NativeCodeVersion::OptimizationTier0 + !activeCodeVersion.IsUnoptimizedTier() ) || !g_pConfig->TieredCompilation_CallCounting()) { @@ -602,7 +636,7 @@ bool CallCountingManager::SetCodeEntryPoint( return true; } - _ASSERTE(activeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + _ASSERTE(activeCodeVersion.IsUnoptimizedTier()); // If the tiering delay is active, postpone further work if (GetAppDomain() @@ -618,6 +652,15 @@ bool CallCountingManager::SetCodeEntryPoint( do { + if (callCountingStage == CallCountingInfo::Stage::PendingReset) + { + // Consider using a different threshold here + CallCount callCountThreshold = g_pConfig->TieredCompilation_CallCountThreshold(); + _ASSERTE(callCountThreshold != 0); + *callCountingInfo->GetRemainingCallCountCell() = callCountThreshold; + break; + } + if (!wasMethodCalled) { break; @@ -649,7 +692,7 @@ bool CallCountingManager::SetCodeEntryPoint( } else { - _ASSERTE(activeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + _ASSERTE(activeCodeVersion.IsUnoptimizedTier()); // If the tiering delay is active, postpone further work if (GetAppDomain() @@ -659,7 +702,7 @@ bool CallCountingManager::SetCodeEntryPoint( return true; } - CallCount callCountThreshold = (CallCount)g_pConfig->TieredCompilation_CallCountThreshold(); + CallCount callCountThreshold = g_pConfig->TieredCompilation_CallCountThreshold(); _ASSERTE(callCountThreshold != 0); NewHolder callCountingInfoHolder = new CallCountingInfo(activeCodeVersion, callCountThreshold); @@ -780,7 +823,7 @@ PCODE CallCountingManager::OnCallCountThresholdReached(TransitionBlock *transiti // used going forward under appropriate locking to synchronize further with deletion. GCX_PREEMP_THREAD_EXISTS(CURRENT_THREAD); - _ASSERTE(codeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + _ASSERTE(codeVersion.IsUnoptimizedTier()); codeEntryPoint = codeVersion.GetNativeCode(); do @@ -797,7 +840,7 @@ PCODE CallCountingManager::OnCallCountThresholdReached(TransitionBlock *transiti } CallCountingInfo::Stage callCountingStage = callCountingInfo->GetStage(); - if (callCountingStage >= CallCountingInfo::Stage::PendingCompletion) + if (callCountingStage >= CallCountingInfo::Stage::PendingReset) { break; } @@ -930,7 +973,11 @@ void CallCountingManager::CompleteCallCounting() methodDesc->ResetCodeEntryPoint(); } while (false); - callCountingInfo->SetStage(CallCountingInfo::Stage::Complete); + // stub was about to be completed but we decided to re-use it + if (callCountingInfo->GetStage() != CallCountingInfo::Stage::PendingReset) + { + callCountingInfo->SetStage(CallCountingInfo::Stage::Complete); + } } EX_CATCH { diff --git a/src/coreclr/vm/callcounting.h b/src/coreclr/vm/callcounting.h index 3d25e1c2826267..e2864d85988015 100644 --- a/src/coreclr/vm/callcounting.h +++ b/src/coreclr/vm/callcounting.h @@ -182,6 +182,9 @@ class CallCountingManager // Stub may be called, don't know if it's actually active (changes to code versions, etc.) StubMayBeActive, + // Stub is being considered to re-use + PendingReset, + // Stub may be active, call counting complete, not yet promoted PendingCompletion, @@ -193,7 +196,7 @@ class CallCountingManager }; private: - const NativeCodeVersion m_codeVersion; + NativeCodeVersion m_codeVersion; const CallCountingStub *m_callCountingStub; CallCount m_remainingCallCount; Stage m_stage; @@ -224,6 +227,7 @@ class CallCountingManager #ifndef DACCESS_COMPILE public: void SetStage(Stage stage); + void ReuseForNewVersion(NativeCodeVersion version); #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -355,6 +359,7 @@ class CallCountingManager #ifndef DACCESS_COMPILE public: void DisableCallCounting(NativeCodeVersion codeVersion); + void ReuseStubForNewVersion(NativeCodeVersion oldVersion, NativeCodeVersion newVersion); public: static bool SetCodeEntryPoint( diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp index bd7fce5d9d7c4d..490c1a99bd6f0b 100644 --- a/src/coreclr/vm/codeversion.cpp +++ b/src/coreclr/vm/codeversion.cpp @@ -333,6 +333,13 @@ NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() con } } +bool NativeCodeVersion::IsUnoptimizedTier() const +{ + LIMITED_METHOD_DAC_CONTRACT; + OptimizationTier tier = GetOptimizationTier(); + return tier == OptimizationTier0 || tier == OptimizationTier0Instrumented; +} + #ifndef DACCESS_COMPILE void NativeCodeVersion::SetOptimizationTier(OptimizationTier tier) { @@ -808,7 +815,7 @@ bool ILCodeVersion::HasAnyOptimizedNativeCodeVersion(NativeCodeVersion tier0Nati _ASSERTE(!tier0NativeCodeVersion.IsNull()); _ASSERTE(tier0NativeCodeVersion.GetILCodeVersion() == *this); _ASSERTE(tier0NativeCodeVersion.GetMethodDesc()->IsEligibleForTieredCompilation()); - _ASSERTE(tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + _ASSERTE(tier0NativeCodeVersion.IsUnoptimizedTier()); NativeCodeVersionCollection nativeCodeVersions = GetNativeCodeVersions(tier0NativeCodeVersion.GetMethodDesc()); for (auto itEnd = nativeCodeVersions.End(), it = nativeCodeVersions.Begin(); it != itEnd; ++it) @@ -1710,7 +1717,7 @@ PCODE CodeVersionManager::PublishVersionableCodeIfNecessary( _ASSERTE(!config->ShouldCountCalls() || pMethodDesc->IsEligibleForTieredCompilation()); _ASSERTE( !config->ShouldCountCalls() || - activeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + activeVersion.IsUnoptimizedTier()); if (config->ShouldCountCalls()) // the generated code was at a tier that is call-counted { // This is the first call to a call-counted code version of the method diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index bacc5305d9bb6c..e4905ed834557a 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -74,12 +74,14 @@ class NativeCodeVersion enum OptimizationTier { OptimizationTier0, + OptimizationTier0Instrumented, OptimizationTier1, OptimizationTier1OSR, OptimizationTierOptimized, // may do less optimizations than tier 1 }; #ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; + bool IsUnoptimizedTier() const; #ifndef DACCESS_COMPILE void SetOptimizationTier(OptimizationTier tier); #endif diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 3b4ad3176e6dac..b77d6836eaa2aa 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -740,6 +740,14 @@ HRESULT EEConfig::sync() } } + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TieredPGO) != 0) + { + DWORD divider = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_DelayPgoDivider); + _ASSERT(divider != 0); + tieredCompilation_CallCountingDelayMs /= divider; + tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); + } + if (fTieredCompilation_CallCounting) { fTieredCompilation_UseCallCountingStubs = diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 0200c71a4887b2..894afc968b438b 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -1802,7 +1802,7 @@ void Interpreter::JitMethodIfAppropriate(InterpreterMethodInfo* interpMethInfo, CodeVersionManager::LockHolder _lockHolder; NativeCodeVersion activeCodeVersion = md->GetCodeVersionManager()->GetActiveILCodeVersion(md).GetActiveNativeCodeVersion(md); ILCodeVersion ilCodeVersion = activeCodeVersion.GetILCodeVersion(); - if (activeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && + if (activeCodeVersion.IsUnoptimizedTier() && !ilCodeVersion.HasAnyOptimizedNativeCodeVersion(activeCodeVersion)) { tieredCompilationManager->AsyncPromoteToTier1(activeCodeVersion, &scheduleTieringBackgroundWork); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 05aa741f10a24f..d4b6561134c3a3 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -364,9 +364,9 @@ PCODE MethodDesc::PrepareILBasedCode(PrepareCodeConfig* pConfig) if (codeVersion.IsDefaultVersion()) { pConfig->GetMethodDesc()->GetLoaderAllocator()->GetCallCountingManager()->DisableCallCounting(codeVersion); - _ASSERTE(codeVersion.GetOptimizationTier() != NativeCodeVersion::OptimizationTier0); + _ASSERTE(!codeVersion.IsUnoptimizedTier()); } - else if (codeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0) + else if (codeVersion.IsUnoptimizedTier()) { codeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); } @@ -465,7 +465,7 @@ PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig, bool shouldTier #ifdef FEATURE_TIERED_COMPILATION if (shouldCountCalls) { - _ASSERTE(pConfig->GetCodeVersion().GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + _ASSERTE(pConfig->GetCodeVersion().IsUnoptimizedTier()); pConfig->SetShouldCountCalls(); } #endif @@ -1217,6 +1217,7 @@ PrepareCodeConfig::JitOptimizationTier PrepareCodeConfig::GetJitOptimizationTier switch (config->GetCodeVersion().GetOptimizationTier()) { case NativeCodeVersion::OptimizationTier0: + case NativeCodeVersion::OptimizationTier0Instrumented: return JitOptimizationTier::QuickJitted; case NativeCodeVersion::OptimizationTier1: @@ -1299,6 +1300,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() NativeCodeVersion::OptimizationTier previousOptimizationTier = GetCodeVersion().GetOptimizationTier(); _ASSERTE( previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || + previousOptimizationTier == NativeCodeVersion::OptimizationTier0Instrumented || previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); #endif // _DEBUG diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 4e4da913e86641..4b157fa55f7550 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -251,7 +251,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread()); _ASSERTE(!tier0NativeCodeVersion.IsNull()); - _ASSERTE(tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + _ASSERTE(tier0NativeCodeVersion.IsUnoptimizedTier()); _ASSERTE(createTieringBackgroundWorkerRef != nullptr); NativeCodeVersion t1NativeCodeVersion; @@ -263,14 +263,39 @@ void TieredCompilationManager::AsyncPromoteToTier1( // occur between now and when jitting completes. If the IL does change in that // interval the new code entry won't be activated. MethodDesc *pMethodDesc = tier0NativeCodeVersion.GetMethodDesc(); + + NativeCodeVersion::OptimizationTier nextTier = NativeCodeVersion::OptimizationTier1; + + // Compile to Tier0 with instrumentation if we promote from R2R and PGO is enabled. + // If that R2R has a static profile we might consider skipping it if we can rely on it being accurate + // e.g. if the current R2R image is Composite (in the default mode we might lose cross-module + // likely classes in the current implementation) + // Also, this Tier0 likely doesn't need any patchpoints since it's already survived a promotion and will + // likely survive it once again + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TieredPGO) != 0 && + pMethodDesc->IsEligibleForTieredCompilation() && + tier0NativeCodeVersion.IsDefaultVersion() && + tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && + ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + { + _ASSERT(!pMethodDesc->RequestedAggressiveOptimization()); + nextTier = NativeCodeVersion::OptimizationTier0Instrumented; + } + ILCodeVersion ilCodeVersion = tier0NativeCodeVersion.GetILCodeVersion(); _ASSERTE(!ilCodeVersion.HasAnyOptimizedNativeCodeVersion(tier0NativeCodeVersion)); - hr = ilCodeVersion.AddNativeCodeVersion(pMethodDesc, NativeCodeVersion::OptimizationTier1, &t1NativeCodeVersion); + hr = ilCodeVersion.AddNativeCodeVersion(pMethodDesc, nextTier, &t1NativeCodeVersion); if (FAILED(hr)) { ThrowHR(hr); } + // Try to re-use previous callcounting stub for instrumented tier0 + if (nextTier == NativeCodeVersion::OptimizationTier0Instrumented) + { + pMethodDesc->GetLoaderAllocator()->GetCallCountingManager()->ReuseStubForNewVersion(tier0NativeCodeVersion, t1NativeCodeVersion); + } + // Insert the method into the optimization queue and trigger a thread to service // the queue if needed. SListElem* pMethodListItem = new SListElem(t1NativeCodeVersion); @@ -993,7 +1018,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) _ASSERTE(config != nullptr); _ASSERTE( !config->WasTieringDisabledBeforeJitting() || - config->GetCodeVersion().GetOptimizationTier() != NativeCodeVersion::OptimizationTier0); + !config->GetCodeVersion().IsUnoptimizedTier()); CORJIT_FLAGS flags; @@ -1018,7 +1043,11 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) { if (g_pConfig->TieredCompilation_QuickJit()) { - _ASSERTE(nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + if ((nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0Instrumented)) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROMOTED); + } + _ASSERTE(nativeCodeVersion.IsUnoptimizedTier()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); return flags; } @@ -1029,6 +1058,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) { newOptimizationTier = NativeCodeVersion::OptimizationTier1; flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROMOTED); } methodDesc->GetLoaderAllocator()->GetCallCountingManager()->DisableCallCounting(nativeCodeVersion); @@ -1041,6 +1071,14 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) switch (nativeCodeVersion.GetOptimizationTier()) { + case NativeCodeVersion::OptimizationTier0Instrumented: + if (g_pConfig->TieredCompilation_QuickJit()) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROMOTED); + } + FALLTHROUGH; + case NativeCodeVersion::OptimizationTier0: if (g_pConfig->TieredCompilation_QuickJit()) { From 4b4967bd47b78f39fafef569c7050b8d517d9020 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 24 Jun 2022 11:25:27 +0200 Subject: [PATCH 02/53] Oops, it turns out that I didn't properly patch re-used callcountingstub --- src/coreclr/inc/corjitflags.h | 2 +- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.hpp | 5 ----- src/coreclr/jit/fgbasic.cpp | 6 ------ src/coreclr/jit/importer.cpp | 17 ++++++++++------- src/coreclr/jit/jitee.h | 7 ++++++- src/coreclr/vm/callcounting.cpp | 4 ++++ src/coreclr/vm/callcounting.h | 7 +++++++ src/coreclr/vm/tieredcompilation.cpp | 6 ------ 9 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/coreclr/inc/corjitflags.h b/src/coreclr/inc/corjitflags.h index 7d7eef15e94273..71e7af410eb94c 100644 --- a/src/coreclr/inc/corjitflags.h +++ b/src/coreclr/inc/corjitflags.h @@ -97,7 +97,7 @@ class CORJIT_FLAGS CORJIT_FLAG_UNUSED16 = 43, #endif // !defined(TARGET_ARM) - CORJIT_FLAG_PROMOTED = 44, // Method was promoted at least once and is likely hot + CORJIT_FLAG_UNUSED17 = 44, CORJIT_FLAG_UNUSED18 = 45, CORJIT_FLAG_UNUSED19 = 46, CORJIT_FLAG_UNUSED20 = 47, diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 21f1d1b48e7fd2..460412d84597e2 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -6668,7 +6668,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, } } - if ((reason != nullptr) && !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) + if (reason != nullptr) { fgSwitchToOptimized(reason); } diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index eba38000537686..f6fa0e2d946b64 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4751,11 +4751,6 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason) { whyNot = "OSR can't handle reverse pinvoke"; } - else if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) - { - // This method will likely make it to tier1 on its own soon - whyNot = "OSR is not needed in an already promoted tier"; - } #else whyNot = "OSR feature not defined in build"; #endif diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index f421ef85233a58..541bdc27e7c962 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -3568,12 +3568,6 @@ void Compiler::fgCheckForLoopsInHandlers() return; } - if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) - { - // This method will likely make it to tier1 on its own soon - return; - } - if (info.compXcptnsCount == 0) { return; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 04aadf06247330..397a7f6f383348 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9343,7 +9343,16 @@ var_types Compiler::impImportCall(OPCODE opcode, { return impImportJitTestLabelMark(sig->numArgs); } -#endif // DEBUG + + // static long JitHelpers_JitFlags() => 0; + // can be defined anywhere and will be replaced by Debug-version of RyuJIT + if ((mflags & CORINFO_FLG_STATIC) && (sig->numArgs == 0) && (sig->retType == CorInfoType::CORINFO_TYPE_LONG) && + (strcmp("JitHelpers_JitFlags", eeGetMethodName(methHnd, nullptr)) == 0)) + { + call = gtNewIconNode(opts.jitFlags->GetRawFlags(), TYP_INT); + goto DONE_CALL; + } +#endif // Factor this into getCallInfo bool isSpecialIntrinsic = false; @@ -12390,12 +12399,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) bool enablePatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.TC_OnStackReplacement() > 0); - if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PROMOTED)) - { - // This method will likely make it to tier1 on its own soon - enablePatchpoints = false; - } - #ifdef DEBUG // Optionally suppress patchpoints by method hash diff --git a/src/coreclr/jit/jitee.h b/src/coreclr/jit/jitee.h index a47f698d06ad64..289773bb72cac4 100644 --- a/src/coreclr/jit/jitee.h +++ b/src/coreclr/jit/jitee.h @@ -87,7 +87,7 @@ class JitFlags JIT_FLAG_UNUSED16 = 43, #endif // !defined(TARGET_ARM) - JIT_FLAG_PROMOTED = 44, // Method was promoted at least once and is likely hot + JIT_FLAG_UNUSED17 = 44, JIT_FLAG_UNUSED18 = 45, JIT_FLAG_UNUSED19 = 46, JIT_FLAG_UNUSED20 = 47, @@ -157,6 +157,11 @@ class JitFlags return m_jitFlags == 0; } + unsigned __int64 GetRawFlags() const + { + return m_jitFlags; + } + void SetFromFlags(CORJIT_FLAGS flags) { // We don't want to have to check every one, so we assume it is exactly the same values as the JitFlag diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index bc991c3e09bd97..4b93c26bd04049 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -657,6 +657,10 @@ bool CallCountingManager::SetCodeEntryPoint( // Consider using a different threshold here CallCount callCountThreshold = g_pConfig->TieredCompilation_CallCountThreshold(); _ASSERTE(callCountThreshold != 0); + + CallCountingStub *stub = (CallCountingStub*)callCountingInfo->GetCallCountingStub(); + // Patch existing CallCountingStub to look at new target + stub->SetTargetForMethod(activeCodeVersion.GetNativeCode()); *callCountingInfo->GetRemainingCallCountCell() = callCountThreshold; break; } diff --git a/src/coreclr/vm/callcounting.h b/src/coreclr/vm/callcounting.h index e2864d85988015..122bd45df3250b 100644 --- a/src/coreclr/vm/callcounting.h +++ b/src/coreclr/vm/callcounting.h @@ -150,6 +150,7 @@ class CallCountingStub PTR_CallCount GetRemainingCallCountCell() const; PCODE GetTargetForMethod() const; + void SetTargetForMethod(PCODE code); protected: @@ -422,6 +423,12 @@ inline PCODE CallCountingStub::GetTargetForMethod() const return GetData()->TargetForMethod; } +inline void CallCountingStub::SetTargetForMethod(PCODE code) +{ + WRAPPER_NO_CONTRACT; + InterlockedExchangeT(&GetData()->TargetForMethod, code); +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CallCountingManager::CallCountingStubManager diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 4b157fa55f7550..4a1a7d1510c621 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -1043,10 +1043,6 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) { if (g_pConfig->TieredCompilation_QuickJit()) { - if ((nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0Instrumented)) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROMOTED); - } _ASSERTE(nativeCodeVersion.IsUnoptimizedTier()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); return flags; @@ -1058,7 +1054,6 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) { newOptimizationTier = NativeCodeVersion::OptimizationTier1; flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROMOTED); } methodDesc->GetLoaderAllocator()->GetCallCountingManager()->DisableCallCounting(nativeCodeVersion); @@ -1075,7 +1070,6 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (g_pConfig->TieredCompilation_QuickJit()) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROMOTED); } FALLTHROUGH; From 1392368a5a81d99fba83b346bcec1542e0f97402 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 24 Jun 2022 11:27:00 +0200 Subject: [PATCH 03/53] remove TieredPGO --- src/coreclr/inc/clrconfigvalues.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 55ad2de895e125..ee5c08709a45d1 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -623,9 +623,7 @@ CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of e RETAIL_CONFIG_STRING_INFO(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") - -// Enable for CI tests -RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 1, "Instrument Tier0 code and make counts available to Tier1") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") #endif /// From 1a4135567b69e894fcfb977c728c6a968f2f3957 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 24 Jun 2022 11:51:22 +0200 Subject: [PATCH 04/53] fix bug in the importer --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 397a7f6f383348..592b23f4e8860c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9349,7 +9349,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if ((mflags & CORINFO_FLG_STATIC) && (sig->numArgs == 0) && (sig->retType == CorInfoType::CORINFO_TYPE_LONG) && (strcmp("JitHelpers_JitFlags", eeGetMethodName(methHnd, nullptr)) == 0)) { - call = gtNewIconNode(opts.jitFlags->GetRawFlags(), TYP_INT); + call = gtNewIconNode(opts.jitFlags->GetRawFlags(), TYP_LONG); goto DONE_CALL; } #endif From 4828f3e8672fb662eb1134f3ddeae848acc2b553 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 24 Jun 2022 13:26:41 +0200 Subject: [PATCH 05/53] Add ability to instrument optimized code --- src/coreclr/inc/clrconfigvalues.h | 10 +++++++++- src/coreclr/jit/compiler.h | 9 +++++++++ src/coreclr/jit/fgprofile.cpp | 9 +++++---- src/coreclr/jit/importer.cpp | 5 ++--- src/coreclr/vm/tieredcompilation.cpp | 22 ++++++++++++++++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index ee5c08709a45d1..139bc4e3ea3e27 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -595,10 +595,18 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_BackgroundWorkerTimeoutMs, W("TC_Background RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountThreshold, W("TC_CallCountThreshold"), TC_CallCountThreshold, "Number of times a method must be called in tier 0 after which it is promoted to the next tier.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCountingDelayMs, W("TC_CallCountingDelayMs"), TC_CallCountingDelayMs, "A perpetual delay in milliseconds that is applied call counting in tier 0 and jitting at higher tiers, while there is startup-like activity.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DelaySingleProcMultiplier, W("TC_DelaySingleProcMultiplier"), TC_DelaySingleProcMultiplier, "Multiplier for TC_CallCountingDelayMs that is applied on a single-processor machine or when the process is affinitized to a single processor.") -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_DelayPgoDivider, W("TC_DelayPgoDivider"), TC_DelayPgoDivider, "PGO-specific Divider for TC_CallCountingDelayMs, needed because we jit more methods") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_UseCallCountingStubs, W("TC_UseCallCountingStubs"), 1, "Uses call counting stubs for faster call counting.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteCallCountingStubsAfter"), 0, "Deletes call counting stubs after this many have completed. Zero to disable deleting.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_DelayPgoDivider, W("TC_DelayPgoDivider"), TC_DelayPgoDivider, "PGO-specific Divider for TC_CallCountingDelayMs, needed because we jit more methods") + +// This is added for performance testing (performance impact from a profile collected in an optimized code vs unoptimized code) +// Eventually, we want to be able to instrument only optimized code because in the current workflow: +// +// R2R -> tier0 instrumentation -> tier1 +// +// that tier0 will never inline even small calls and we'll trigger a lot of redundant first-time compilations +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_InstrumentOptimizedCode, W("TC_InstrumentOptimizedCode"), 0, "Instrumented tier should use optimized code") #undef TC_BackgroundWorkerTimeoutMs #undef TC_CallCountThreshold #undef TC_CallCountingDelayMs diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b5bfdf926f2126..4c396339f6a2a2 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9075,6 +9075,15 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } #endif + bool IsInstrumented() + { + return jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR); + } + bool IsInstrumentedOptimized() + { + return IsInstrumented() && jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1); + } + // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating // PInvoke transitions inline. Normally used by R2R, but also used when generating a reverse pinvoke frame, as // the current logic for frame setup initializes and pushes diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 4eb555a65ca94e..42b98df9767de3 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -383,7 +383,8 @@ void BlockCountInstrumentor::Prepare(bool preImport) // // If we see any, we need to adjust our instrumentation pattern. // - if (m_comp->opts.IsOSR() && ((m_comp->optMethodFlags & OMF_HAS_TAILCALL_SUCCESSOR) != 0)) + if ((m_comp->opts.IsOSR() || m_comp->opts.IsInstrumentedOptimized()) && + ((m_comp->optMethodFlags & OMF_HAS_TAILCALL_SUCCESSOR) != 0)) { JITDUMP("OSR + PGO + potential tail call --- preparing to relocate block probes\n"); @@ -1887,8 +1888,8 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() (JitConfig.TC_PartialCompilation() > 0); const bool prejit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); const bool tier0WithPatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && mayHavePatchpoints; - const bool osrMethod = opts.IsOSR(); - const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !osrMethod; + const bool useEdgeProfiles = + (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !opts.IsInstrumentedOptimized(); if (useEdgeProfiles) { @@ -1899,7 +1900,7 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() JITDUMP("Using block profiling, because %s\n", (JitConfig.JitEdgeProfiling() == 0) ? "edge profiles disabled" - : prejit ? "prejitting" : osrMethod ? "OSR" : "tier0 with patchpoints"); + : prejit ? "prejitting" : opts.IsInstrumentedOptimized() ? "tier1" : "tier0 with patchpoints"); fgCountInstrumentor = new (this, CMK_Pgo) BlockCountInstrumentor(this); } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 592b23f4e8860c..5c0787b2eae6a1 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -10405,7 +10405,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // have to check for anything that might introduce a recursive tail call. // * We only instrument root method blocks in OSR methods, // - if (opts.IsOSR() && !compIsForInlining()) + if ((opts.IsOSR() || opts.IsInstrumentedOptimized()) && !compIsForInlining()) { // If a root method tail call candidate block is not a BBJ_RETURN, it should have a unique // BBJ_RETURN successor. Mark that successor so we can handle it specially during profile @@ -10429,7 +10429,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // Only schedule importation if we're not currently importing. // - if (mustImportEntryBlock && (compCurBB != fgEntryBB)) + if (opts.IsOSR() && mustImportEntryBlock && (compCurBB != fgEntryBB)) { JITDUMP("\nOSR: inlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB " for importation\n", @@ -22027,7 +22027,6 @@ bool Compiler::impConsiderCallProbe(GenTreeCall* call, IL_OFFSET ilOffset) return false; } - assert(opts.OptimizationDisabled() || opts.IsOSR()); assert(!compIsForInlining()); // During importation, optionally flag this block as one that diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 4a1a7d1510c621..11af30d7a430fc 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -1043,6 +1043,19 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) { if (g_pConfig->TieredCompilation_QuickJit()) { + if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier::OptimizationTier0Instrumented) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_InstrumentOptimizedCode) != 0) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + } + else + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); + } + return flags; + } _ASSERTE(nativeCodeVersion.IsUnoptimizedTier()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); return flags; @@ -1070,6 +1083,15 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (g_pConfig->TieredCompilation_QuickJit()) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_InstrumentOptimizedCode) != 0) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + } + else + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); + } + break; } FALLTHROUGH; From 1fa264b85bd80f0207570da252ed993126a28b4d Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 24 Jun 2022 14:32:01 +0200 Subject: [PATCH 06/53] fix 32bit --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 5c0787b2eae6a1..f9bf90cad90a77 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9349,7 +9349,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if ((mflags & CORINFO_FLG_STATIC) && (sig->numArgs == 0) && (sig->retType == CorInfoType::CORINFO_TYPE_LONG) && (strcmp("JitHelpers_JitFlags", eeGetMethodName(methHnd, nullptr)) == 0)) { - call = gtNewIconNode(opts.jitFlags->GetRawFlags(), TYP_LONG); + call = gtNewLconNode((__int64)opts.jitFlags->GetRawFlags()); goto DONE_CALL; } #endif From 71928f3ab9bd0026d702f025369809b8fbc9ff7a Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 24 Jun 2022 14:35:23 +0200 Subject: [PATCH 07/53] fix ret type --- src/coreclr/jit/importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index f9bf90cad90a77..2d518837635e5e 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9344,9 +9344,9 @@ var_types Compiler::impImportCall(OPCODE opcode, return impImportJitTestLabelMark(sig->numArgs); } - // static long JitHelpers_JitFlags() => 0; + // static ulong JitHelpers_JitFlags() => 0; // can be defined anywhere and will be replaced by Debug-version of RyuJIT - if ((mflags & CORINFO_FLG_STATIC) && (sig->numArgs == 0) && (sig->retType == CorInfoType::CORINFO_TYPE_LONG) && + if ((mflags & CORINFO_FLG_STATIC) && (sig->numArgs == 0) && (sig->retType == CorInfoType::CORINFO_TYPE_ULONG) && (strcmp("JitHelpers_JitFlags", eeGetMethodName(methHnd, nullptr)) == 0)) { call = gtNewLconNode((__int64)opts.jitFlags->GetRawFlags()); From 127b7d8031652d789b3f7ced83d82c092a9f9d31 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 24 Jun 2022 14:49:31 +0200 Subject: [PATCH 08/53] Clean up --- src/coreclr/inc/clrconfigvalues.h | 18 +++++------------- src/coreclr/vm/eeconfig.cpp | 10 ++++++---- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 139bc4e3ea3e27..0f0e1345d5e4e7 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -572,16 +572,11 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_HillClimbing_GainExponent, #define TC_CallCountThreshold (2) #define TC_CallCountingDelayMs (1) #define TC_DelaySingleProcMultiplier (2) -#define TC_DelayPgoDivider (1) #else // !_DEBUG #define TC_BackgroundWorkerTimeoutMs (4000) #define TC_CallCountThreshold (30) // TODO: Find better defaults for PGO #define TC_CallCountingDelayMs (100) #define TC_DelaySingleProcMultiplier (10) -#define TC_DelayPgoDivider (3) // in PGO mode we compile way more methods and don't want to stuck in tier0 - // waiting for a Delay tick to find out it's busy (see IsTieringDelayActive()) - // and wait again and again - #endif // _DEBUG RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredCompilation, W("TieredCompilation"), 1, "Enables tiered compilation") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_QuickJit, W("TC_QuickJit"), 1, "For methods that would be jitted, enable using quick JIT when appropriate.") @@ -598,15 +593,12 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DelaySingleProcMultiplier, W("TC_DelaySingl RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_UseCallCountingStubs, W("TC_UseCallCountingStubs"), 1, "Uses call counting stubs for faster call counting.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteCallCountingStubsAfter"), 0, "Deletes call counting stubs after this many have completed. Zero to disable deleting.") -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_DelayPgoDivider, W("TC_DelayPgoDivider"), TC_DelayPgoDivider, "PGO-specific Divider for TC_CallCountingDelayMs, needed because we jit more methods") -// This is added for performance testing (performance impact from a profile collected in an optimized code vs unoptimized code) -// Eventually, we want to be able to instrument only optimized code because in the current workflow: -// -// R2R -> tier0 instrumentation -> tier1 -// -// that tier0 will never inline even small calls and we'll trigger a lot of redundant first-time compilations -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_InstrumentOptimizedCode, W("TC_InstrumentOptimizedCode"), 0, "Instrumented tier should use optimized code") +// Optimized instrumented tier produces less accurate profile, but it's faster and is able to inline code, otherwise for: +// R2R -> Tier0 inst -> Tier1 +// we start to produce a lot of small redundant first-time compilations and delay promotion for tier1 pending methods +// each new compilation delays promotion by TC_CallCountingDelayMs +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_InstrumentOptimizedCode, W("TC_InstrumentOptimizedCode"), 1, "Instrumented tier should use optimized code") #undef TC_BackgroundWorkerTimeoutMs #undef TC_CallCountThreshold #undef TC_CallCountingDelayMs diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index b77d6836eaa2aa..997a3e3bf35b55 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -740,11 +740,13 @@ HRESULT EEConfig::sync() } } - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TieredPGO) != 0) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TieredPGO) != 0 && + CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_InstrumentOptimizedCode) == 0) { - DWORD divider = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_DelayPgoDivider); - _ASSERT(divider != 0); - tieredCompilation_CallCountingDelayMs /= divider; + // When we're not using optimizations in the instrumentation tier we produce a lot of new + // first-time compilation due to disabled inlining - such first-time compilations delay promotions + // by tieredCompilation_CallCountingDelayMs + tieredCompilation_CallCountingDelayMs /= 3; tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); } From 911af8a0a191188ac746f20252954c3dd04dea9c Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 26 Jun 2022 00:36:31 +0200 Subject: [PATCH 09/53] Clean up --- src/coreclr/inc/clrconfigvalues.h | 15 +++++++++------ src/coreclr/vm/eeconfig.cpp | 8 ++++---- src/coreclr/vm/tieredcompilation.cpp | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 0f0e1345d5e4e7..5d0ca19d150eed 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -594,11 +594,6 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Ena RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_UseCallCountingStubs, W("TC_UseCallCountingStubs"), 1, "Uses call counting stubs for faster call counting.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteCallCountingStubsAfter"), 0, "Deletes call counting stubs after this many have completed. Zero to disable deleting.") -// Optimized instrumented tier produces less accurate profile, but it's faster and is able to inline code, otherwise for: -// R2R -> Tier0 inst -> Tier1 -// we start to produce a lot of small redundant first-time compilations and delay promotion for tier1 pending methods -// each new compilation delays promotion by TC_CallCountingDelayMs -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_InstrumentOptimizedCode, W("TC_InstrumentOptimizedCode"), 1, "Instrumented tier should use optimized code") #undef TC_BackgroundWorkerTimeoutMs #undef TC_CallCountThreshold #undef TC_CallCountingDelayMs @@ -623,7 +618,15 @@ CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of e RETAIL_CONFIG_STRING_INFO(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") -RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") + +// Enabled for CI tests +RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 1, "Instrument Tier0 code and make counts available to Tier1") + +// Optimized instrumented tier produces less accurate profile, but it's faster and is able to inline code, otherwise for: +// R2R -> Tier0 inst -> Tier1 +// we start to produce a lot of small redundant first-time compilations and delay promotion for tier1 pending methods +// each new compilation delays promotion by TC_CallCountingDelayMs +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_OptimizedInstrumentedTier, W("TC_OptimizedInstrumentedTier"), 1, "Instrumented tier should be optimized") #endif /// diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 997a3e3bf35b55..e1c7d06065ebb4 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -741,11 +741,11 @@ HRESULT EEConfig::sync() } if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TieredPGO) != 0 && - CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_InstrumentOptimizedCode) == 0) + CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_OptimizedInstrumentedTier) == 0) { - // When we're not using optimizations in the instrumentation tier we produce a lot of new - // first-time compilation due to disabled inlining - such first-time compilations delay promotions - // by tieredCompilation_CallCountingDelayMs + // When we're not using optimizations in the instrumented tier we produce a lot of new first-time compilation + // due to disabled inlining even for very small methods - such first-time compilations delay promotions by + // tieredCompilation_CallCountingDelayMs tieredCompilation_CallCountingDelayMs /= 3; tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); } diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 11af30d7a430fc..d4a93439c036c8 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -1046,7 +1046,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier::OptimizationTier0Instrumented) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_InstrumentOptimizedCode) != 0) + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_OptimizedInstrumentedTier) != 0) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); } @@ -1083,7 +1083,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (g_pConfig->TieredCompilation_QuickJit()) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_InstrumentOptimizedCode) != 0) + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_OptimizedInstrumentedTier) != 0) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); } From 0316842acbb742cdf0d5f339c3f0a63d6bab99c9 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 26 Jun 2022 14:39:07 +0200 Subject: [PATCH 10/53] test fix --- src/coreclr/jit/fginline.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index d59301991aaa61..5479ef85a01ed0 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -786,7 +786,8 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD if (condTree->OperGet() == GT_CNS_INT) { JITDUMP(" ... found foldable jtrue at [%06u] in " FMT_BB "\n", dspTreeID(tree), block->bbNum); - noway_assert((block->bbNext->countOfInEdges() > 0) && (block->bbJumpDest->countOfInEdges() > 0)); + noway_assert(!comp->fgCheapPredsValid || + ((block->bbNext->countOfInEdges() > 0) && (block->bbJumpDest->countOfInEdges() > 0))); // We have a constant operand, and should have the all clear to optimize. // Update side effects on the tree, assert there aren't any, and bash to nop. From 2022e68d06176504adb2333d0810bf071d0a9c39 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 26 Jun 2022 18:02:13 +0200 Subject: [PATCH 11/53] test2 --- src/coreclr/jit/fginline.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 5479ef85a01ed0..8d7865c3d65243 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -786,7 +786,7 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD if (condTree->OperGet() == GT_CNS_INT) { JITDUMP(" ... found foldable jtrue at [%06u] in " FMT_BB "\n", dspTreeID(tree), block->bbNum); - noway_assert(!comp->fgCheapPredsValid || + noway_assert(!comp->fgComputePredsDone || ((block->bbNext->countOfInEdges() > 0) && (block->bbJumpDest->countOfInEdges() > 0))); // We have a constant operand, and should have the all clear to optimize. @@ -809,7 +809,10 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD bNotTaken = block->bbJumpDest; } - comp->fgRemoveRefPred(bNotTaken, block); + if (comp->fgComputePredsDone) + { + comp->fgRemoveRefPred(bNotTaken, block); + } // If that was the last ref, a subsequent flow-opt pass // will clean up the now-unreachable bNotTaken, and any From 3a718afe6e0bcc6a2bab435957db1bea690a5315 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 15 Jul 2022 02:05:57 +0200 Subject: [PATCH 12/53] Address feedback --- src/coreclr/inc/clrconfigvalues.h | 11 +++++++++++ src/coreclr/jit/importer.cpp | 5 +++-- src/coreclr/vm/codeversion.h | 2 +- src/coreclr/vm/eeconfig.cpp | 8 ++++++-- src/coreclr/vm/eeconfig.h | 4 ++++ src/coreclr/vm/tieredcompilation.cpp | 6 +++--- 6 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 7009ed0093f52d..d63c79dc404863 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -620,6 +620,17 @@ RETAIL_CONFIG_STRING_INFO(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PG RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") + +// Use optimizations inside instrumented tier +// Pros: +// InstrumentedTier produces faster code, we can perform probe-specific optimizations and emit less of those +// Optimized code is able to inlines methods so we won't be producing new Compilation units for even small methods +// +// Cons: +// We won't instrument inlinees -> probably missing a lot of oportunities and produce less accurate profile +// +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_OptimizeInstrumentedTier, W("TieredPGO_OptimizeInstrumentedTier"), 0, "Use code optimizations inside the instrumented tier") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_UseInstrumentedTierForR2R, W("TieredPGO_UseInstrumentedTierForR2R"), 0, "Hot R2R code might be promoted to an instrumented tier to collect profile") #endif /// diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d952fe108f69d8..65699f7f6ee4e0 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -10636,7 +10636,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // have to check for anything that might introduce a recursive tail call. // * We only instrument root method blocks in OSR methods, // - if ((opts.IsOSR() || opts.IsInstrumentedOptimized()) && !compIsForInlining()) + if (opts.IsOSR() && !compIsForInlining()) { // If a root method tail call candidate block is not a BBJ_RETURN, it should have a unique // BBJ_RETURN successor. Mark that successor so we can handle it specially during profile @@ -10660,7 +10660,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // Only schedule importation if we're not currently importing. // - if (opts.IsOSR() && mustImportEntryBlock && (compCurBB != fgEntryBB)) + if (mustImportEntryBlock && (compCurBB != fgEntryBB)) { JITDUMP("\nOSR: inlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB " for importation\n", @@ -22265,6 +22265,7 @@ bool Compiler::impConsiderCallProbe(GenTreeCall* call, IL_OFFSET ilOffset) return false; } + assert(opts.OptimizationDisabled() || opts.IsOSR()); assert(!compIsForInlining()); // During importation, optionally flag this block as one that diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index e4905ed834557a..6757da925d7be0 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -74,10 +74,10 @@ class NativeCodeVersion enum OptimizationTier { OptimizationTier0, - OptimizationTier0Instrumented, OptimizationTier1, OptimizationTier1OSR, OptimizationTierOptimized, // may do less optimizations than tier 1 + OptimizationTier0Instrumented, }; #ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index f3e6474ee81786..f0bb84ffd01a52 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -239,6 +239,8 @@ HRESULT EEConfig::Init() #if defined(FEATURE_PGO) fTieredPGO = false; + fTieredPGO_OptimizeInstrumentedTier = false; + fTieredPGO_UseInstrumentedTierForR2R = false; #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) @@ -744,8 +746,8 @@ HRESULT EEConfig::sync() } } - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TieredPGO) != 0 && - CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_OptimizedInstrumentedTier) == 0) + if (g_pConfig->TieredPGO() && g_pConfig->TieredPGO_UseInstrumentedTierForR2R() && + !g_pConfig->TieredPGO_OptimizeInstrumentedTier()) { // When we're not using optimizations in the instrumented tier we produce a lot of new first-time compilation // due to disabled inlining even for very small methods - such first-time compilations delay promotions by @@ -785,6 +787,8 @@ HRESULT EEConfig::sync() #if defined(FEATURE_PGO) fTieredPGO = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredPGO"), CLRConfig::EXTERNAL_TieredPGO); + fTieredPGO_OptimizeInstrumentedTier = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_OptimizeInstrumentedTier) != 0; + fTieredPGO_UseInstrumentedTierForR2R = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_UseInstrumentedTierForR2R) != 0; #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index 0cb79d7f85fdcf..a18536599b998e 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -92,6 +92,8 @@ class EEConfig #if defined(FEATURE_PGO) bool TieredPGO(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO; } + bool TieredPGO_OptimizeInstrumentedTier(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO_OptimizeInstrumentedTier; } + bool TieredPGO_UseInstrumentedTierForR2R(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO_UseInstrumentedTierForR2R; } #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) @@ -654,6 +656,8 @@ class EEConfig #if defined(FEATURE_PGO) bool fTieredPGO; + bool fTieredPGO_OptimizeInstrumentedTier; + bool fTieredPGO_UseInstrumentedTierForR2R; #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index d4a93439c036c8..b9e9e059cd89d7 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -272,7 +272,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( // likely classes in the current implementation) // Also, this Tier0 likely doesn't need any patchpoints since it's already survived a promotion and will // likely survive it once again - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TieredPGO) != 0 && + if (g_pConfig->TieredPGO() && pMethodDesc->IsEligibleForTieredCompilation() && tier0NativeCodeVersion.IsDefaultVersion() && tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && @@ -1046,7 +1046,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier::OptimizationTier0Instrumented) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_OptimizedInstrumentedTier) != 0) + if (g_pConfig->TieredPGO_OptimizeInstrumentedTier()) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); } @@ -1083,7 +1083,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (g_pConfig->TieredCompilation_QuickJit()) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TC_OptimizedInstrumentedTier) != 0) + if (g_pConfig->TieredPGO()) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); } From 41733fede75f7c0b0cf9015a6842a4cb2d5221c5 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 15 Jul 2022 11:51:16 +0200 Subject: [PATCH 13/53] Don't re-use callcountingstubs for now --- src/coreclr/inc/clrconfigvalues.h | 1 - src/coreclr/jit/compiler.h | 9 ----- src/coreclr/jit/fgprofile.cpp | 9 +++-- src/coreclr/vm/callcounting.cpp | 59 +++---------------------------- src/coreclr/vm/callcounting.h | 12 +------ 5 files changed, 9 insertions(+), 81 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index d63c79dc404863..6c1b0fe7b12790 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -594,7 +594,6 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DelaySingleProcMultiplier, W("TC_DelaySingl RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_UseCallCountingStubs, W("TC_UseCallCountingStubs"), 1, "Uses call counting stubs for faster call counting.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteCallCountingStubsAfter"), 0, "Deletes call counting stubs after this many have completed. Zero to disable deleting.") - #undef TC_BackgroundWorkerTimeoutMs #undef TC_CallCountThreshold #undef TC_CallCountingDelayMs diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 68fb523c004bbd..be9610aa3c3d76 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9101,15 +9101,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } #endif - bool IsInstrumented() - { - return jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR); - } - bool IsInstrumentedOptimized() - { - return IsInstrumented() && jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1); - } - // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating // PInvoke transitions inline. Normally used by R2R, but also used when generating a reverse pinvoke frame, as // the current logic for frame setup initializes and pushes diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 6f813dc1a1cb62..ae79f10af131af 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -383,8 +383,7 @@ void BlockCountInstrumentor::Prepare(bool preImport) // // If we see any, we need to adjust our instrumentation pattern. // - if ((m_comp->opts.IsOSR() || m_comp->opts.IsInstrumentedOptimized()) && - ((m_comp->optMethodFlags & OMF_HAS_TAILCALL_SUCCESSOR) != 0)) + if (m_comp->opts.IsOSR() && ((m_comp->optMethodFlags & OMF_HAS_TAILCALL_SUCCESSOR) != 0)) { JITDUMP("OSR + PGO + potential tail call --- preparing to relocate block probes\n"); @@ -1888,8 +1887,8 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() (JitConfig.TC_PartialCompilation() > 0); const bool prejit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); const bool tier0WithPatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && mayHavePatchpoints; - const bool useEdgeProfiles = - (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !opts.IsInstrumentedOptimized(); + const bool osrMethod = opts.IsOSR(); + const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !osrMethod; if (useEdgeProfiles) { @@ -1900,7 +1899,7 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() JITDUMP("Using block profiling, because %s\n", (JitConfig.JitEdgeProfiling() == 0) ? "edge profiles disabled" - : prejit ? "prejitting" : opts.IsInstrumentedOptimized() ? "tier1" : "tier0 with patchpoints"); + : prejit ? "prejitting" : osrMethod ? "OSR" : "tier0 with patchpoints"); fgCountInstrumentor = new (this, CMK_Pgo) BlockCountInstrumentor(this); } diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index 4b93c26bd04049..09fb428b454a38 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -147,18 +147,13 @@ FORCEINLINE void CallCountingManager::CallCountingInfo::SetStage(Stage stage) --s_activeCallCountingStubCount; break; - case Stage::PendingReset: - _ASSERT(m_stage >= Stage::PendingCompletion); - --s_activeCallCountingStubCount; - break; - case Stage::StubMayBeActive: _ASSERTE(m_callCountingStub != nullptr); FALLTHROUGH; case Stage::PendingCompletion: - _ASSERTE(m_stage == Stage::StubIsNotActive || m_stage == Stage::StubMayBeActive || m_stage == Stage::PendingReset); - if ((m_stage == Stage::StubIsNotActive || m_stage == Stage::PendingReset) && m_callCountingStub != nullptr) + _ASSERTE(m_stage == Stage::StubIsNotActive || m_stage == Stage::StubMayBeActive); + if (m_stage == Stage::StubIsNotActive && m_callCountingStub != nullptr) { ++s_activeCallCountingStubCount; } @@ -183,13 +178,6 @@ FORCEINLINE void CallCountingManager::CallCountingInfo::SetStage(Stage stage) m_stage = stage; } - -void CallCountingManager::CallCountingInfo::ReuseForNewVersion(NativeCodeVersion version) -{ - WRAPPER_NO_CONTRACT; - m_codeVersion = version; - SetStage(Stage::PendingReset); -} #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -551,28 +539,6 @@ void CallCountingManager::DisableCallCounting(NativeCodeVersion codeVersion) callCountingInfoHolder.SuppressRelease(); } -void CallCountingManager::ReuseStubForNewVersion(NativeCodeVersion oldVersion, NativeCodeVersion newVersion) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - CodeVersionManager::LockHolder codeVersioningLockHolder; - CallCountingInfo* callCountingInfo = m_callCountingInfoByCodeVersionHash.Lookup(oldVersion); - - // No problem if it's null, it's just an optimization to allocate less call counting stubs - if (callCountingInfo != nullptr) - { - m_callCountingInfoByCodeVersionHash.Remove(oldVersion); - callCountingInfo->ReuseForNewVersion(newVersion); - m_callCountingInfoByCodeVersionHash.Add(callCountingInfo); - } -} - // Returns true if the code entry point was updated to reflect the active code version, false otherwise. In normal paths, the // code entry point is not updated only when the use of call counting stubs is disabled, as in that case returning to the // prestub is necessary for further call counting. On exception, the code entry point may or may not have been updated and it's @@ -652,19 +618,6 @@ bool CallCountingManager::SetCodeEntryPoint( do { - if (callCountingStage == CallCountingInfo::Stage::PendingReset) - { - // Consider using a different threshold here - CallCount callCountThreshold = g_pConfig->TieredCompilation_CallCountThreshold(); - _ASSERTE(callCountThreshold != 0); - - CallCountingStub *stub = (CallCountingStub*)callCountingInfo->GetCallCountingStub(); - // Patch existing CallCountingStub to look at new target - stub->SetTargetForMethod(activeCodeVersion.GetNativeCode()); - *callCountingInfo->GetRemainingCallCountCell() = callCountThreshold; - break; - } - if (!wasMethodCalled) { break; @@ -844,7 +797,7 @@ PCODE CallCountingManager::OnCallCountThresholdReached(TransitionBlock *transiti } CallCountingInfo::Stage callCountingStage = callCountingInfo->GetStage(); - if (callCountingStage >= CallCountingInfo::Stage::PendingReset) + if (callCountingStage >= CallCountingInfo::Stage::PendingCompletion) { break; } @@ -977,11 +930,7 @@ void CallCountingManager::CompleteCallCounting() methodDesc->ResetCodeEntryPoint(); } while (false); - // stub was about to be completed but we decided to re-use it - if (callCountingInfo->GetStage() != CallCountingInfo::Stage::PendingReset) - { - callCountingInfo->SetStage(CallCountingInfo::Stage::Complete); - } + callCountingInfo->SetStage(CallCountingInfo::Stage::Complete); } EX_CATCH { diff --git a/src/coreclr/vm/callcounting.h b/src/coreclr/vm/callcounting.h index 122bd45df3250b..c197c2bfd76eae 100644 --- a/src/coreclr/vm/callcounting.h +++ b/src/coreclr/vm/callcounting.h @@ -150,7 +150,6 @@ class CallCountingStub PTR_CallCount GetRemainingCallCountCell() const; PCODE GetTargetForMethod() const; - void SetTargetForMethod(PCODE code); protected: @@ -183,9 +182,6 @@ class CallCountingManager // Stub may be called, don't know if it's actually active (changes to code versions, etc.) StubMayBeActive, - // Stub is being considered to re-use - PendingReset, - // Stub may be active, call counting complete, not yet promoted PendingCompletion, @@ -197,7 +193,7 @@ class CallCountingManager }; private: - NativeCodeVersion m_codeVersion; + const NativeCodeVersion m_codeVersion; const CallCountingStub *m_callCountingStub; CallCount m_remainingCallCount; Stage m_stage; @@ -423,12 +419,6 @@ inline PCODE CallCountingStub::GetTargetForMethod() const return GetData()->TargetForMethod; } -inline void CallCountingStub::SetTargetForMethod(PCODE code) -{ - WRAPPER_NO_CONTRACT; - InterlockedExchangeT(&GetData()->TargetForMethod, code); -} - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CallCountingManager::CallCountingStubManager From 21c8f6ef22ce9017fa80ab9af63c2565087326d5 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 15 Jul 2022 11:52:54 +0200 Subject: [PATCH 14/53] Clean up --- src/coreclr/vm/callcounting.h | 2 -- src/coreclr/vm/tieredcompilation.cpp | 6 ------ 2 files changed, 8 deletions(-) diff --git a/src/coreclr/vm/callcounting.h b/src/coreclr/vm/callcounting.h index c197c2bfd76eae..3d25e1c2826267 100644 --- a/src/coreclr/vm/callcounting.h +++ b/src/coreclr/vm/callcounting.h @@ -224,7 +224,6 @@ class CallCountingManager #ifndef DACCESS_COMPILE public: void SetStage(Stage stage); - void ReuseForNewVersion(NativeCodeVersion version); #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -356,7 +355,6 @@ class CallCountingManager #ifndef DACCESS_COMPILE public: void DisableCallCounting(NativeCodeVersion codeVersion); - void ReuseStubForNewVersion(NativeCodeVersion oldVersion, NativeCodeVersion newVersion); public: static bool SetCodeEntryPoint( diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index b9e9e059cd89d7..71e31b53f8c863 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -290,12 +290,6 @@ void TieredCompilationManager::AsyncPromoteToTier1( ThrowHR(hr); } - // Try to re-use previous callcounting stub for instrumented tier0 - if (nextTier == NativeCodeVersion::OptimizationTier0Instrumented) - { - pMethodDesc->GetLoaderAllocator()->GetCallCountingManager()->ReuseStubForNewVersion(tier0NativeCodeVersion, t1NativeCodeVersion); - } - // Insert the method into the optimization queue and trigger a thread to service // the queue if needed. SListElem* pMethodListItem = new SListElem(t1NativeCodeVersion); From f37012de69fbe155808545a027bc9af2010abd30 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 15 Jul 2022 15:37:35 +0200 Subject: [PATCH 15/53] Address feedback --- src/coreclr/debug/daccess/request.cpp | 6 ++ src/coreclr/inc/clrconfigvalues.h | 20 ++++-- src/coreclr/inc/dacprivate.h | 2 + src/coreclr/vm/codeversion.cpp | 2 +- src/coreclr/vm/codeversion.h | 4 +- src/coreclr/vm/eeconfig.cpp | 36 +++++++---- src/coreclr/vm/eeconfig.h | 29 +++++++-- src/coreclr/vm/jitinterface.cpp | 17 ----- src/coreclr/vm/method.hpp | 2 + src/coreclr/vm/prestub.cpp | 11 +++- src/coreclr/vm/tieredcompilation.cpp | 93 +++++++++++++++------------ 11 files changed, 135 insertions(+), 87 deletions(-) diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 6aae7bd1b4c131..f6dea74e8e9338 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -1183,6 +1183,12 @@ HRESULT ClrDataAccess::GetTieredVersions( case NativeCodeVersion::OptimizationTierOptimized: nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_Optimized; break; + case NativeCodeVersion::OptimizationTierInstrumented: + nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_InstrumentedTier; + break; + case NativeCodeVersion::OptimizationTierInstrumentedOptimized: + nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_InstrumentedTierOptimized; + break; } } else if (pMD->IsJitOptimizationDisabled()) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 6c1b0fe7b12790..9428427ea5d1a1 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -620,16 +620,24 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO da RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") -// Use optimizations inside instrumented tier +// TieredPGO_Strategy values: +// +// 0 - [Default] Use InstrumentedTier for non-R2R code as the initial tier +// 1 - Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTier +// 2 - [NYI] Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTierOptimized +// 3 - [NYI] Promote hot Tier0/R2R to InstrumentedTier +// 4 - [NYI] Promote hot Tier0/R2R to InstrumentedTierOptimized +// +// Pros & cons of using optimizations inside the instrumented tiers (modes '2' and '4') // Pros: -// InstrumentedTier produces faster code, we can perform probe-specific optimizations and emit less of those -// Optimized code is able to inlines methods so we won't be producing new Compilation units for even small methods +// * Lower overhead from instrumentation (and thanks to optimizations we _can_ optimize probes and emit less of those) +// * Optimized code is able to inline methods so we won't be producing new Compilation units for even small methods // // Cons: -// We won't instrument inlinees -> probably missing a lot of oportunities and produce less accurate profile +// * Currently, we won't instrument inlinees -> we'll probably miss a lot of oportunities and produce less accurate profile +// leading to a less optimized final tier // -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_OptimizeInstrumentedTier, W("TieredPGO_OptimizeInstrumentedTier"), 0, "Use code optimizations inside the instrumented tier") -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_UseInstrumentedTierForR2R, W("TieredPGO_UseInstrumentedTierForR2R"), 0, "Hot R2R code might be promoted to an instrumented tier to collect profile") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 0, "Strategy for TieredPGO, see comments in clrconfigvalues.h") #endif /// diff --git a/src/coreclr/inc/dacprivate.h b/src/coreclr/inc/dacprivate.h index 1e345810445c8a..7c41b55dbd7609 100644 --- a/src/coreclr/inc/dacprivate.h +++ b/src/coreclr/inc/dacprivate.h @@ -610,6 +610,8 @@ struct MSLAYOUT DacpTieredVersionData OptimizationTier_OptimizedTier1, OptimizationTier_ReadyToRun, OptimizationTier_OptimizedTier1OSR, + OptimizationTier_InstrumentedTier, + OptimizationTier_InstrumentedTierOptimized, }; CLRDATA_ADDRESS NativeCodeAddr; diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp index 490c1a99bd6f0b..899e32736e0de0 100644 --- a/src/coreclr/vm/codeversion.cpp +++ b/src/coreclr/vm/codeversion.cpp @@ -337,7 +337,7 @@ bool NativeCodeVersion::IsUnoptimizedTier() const { LIMITED_METHOD_DAC_CONTRACT; OptimizationTier tier = GetOptimizationTier(); - return tier == OptimizationTier0 || tier == OptimizationTier0Instrumented; + return tier == OptimizationTier0 || tier == OptimizationTierInstrumented; } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 6757da925d7be0..36cb4ec418e3f8 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -71,13 +71,15 @@ class NativeCodeVersion BOOL SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected = NULL); #endif + // NOTE: Don't change existing values to avoid breaking changes in event tracing enum OptimizationTier { OptimizationTier0, OptimizationTier1, OptimizationTier1OSR, OptimizationTierOptimized, // may do less optimizations than tier 1 - OptimizationTier0Instrumented, + OptimizationTierInstrumented, + OptimizationTierInstrumentedOptimized, }; #ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index f0bb84ffd01a52..2ba89a5d7a4fd8 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -239,8 +239,7 @@ HRESULT EEConfig::Init() #if defined(FEATURE_PGO) fTieredPGO = false; - fTieredPGO_OptimizeInstrumentedTier = false; - fTieredPGO_UseInstrumentedTierForR2R = false; + fTieredPGO_Strategy = (TieredPGOStrategy)0; #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) @@ -746,16 +745,6 @@ HRESULT EEConfig::sync() } } - if (g_pConfig->TieredPGO() && g_pConfig->TieredPGO_UseInstrumentedTierForR2R() && - !g_pConfig->TieredPGO_OptimizeInstrumentedTier()) - { - // When we're not using optimizations in the instrumented tier we produce a lot of new first-time compilation - // due to disabled inlining even for very small methods - such first-time compilations delay promotions by - // tieredCompilation_CallCountingDelayMs - tieredCompilation_CallCountingDelayMs /= 3; - tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); - } - if (fTieredCompilation_CallCounting) { fTieredCompilation_UseCallCountingStubs = @@ -787,8 +776,27 @@ HRESULT EEConfig::sync() #if defined(FEATURE_PGO) fTieredPGO = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredPGO"), CLRConfig::EXTERNAL_TieredPGO); - fTieredPGO_OptimizeInstrumentedTier = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_OptimizeInstrumentedTier) != 0; - fTieredPGO_UseInstrumentedTierForR2R = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_UseInstrumentedTierForR2R) != 0; + fTieredPGO_Strategy = (TieredPGOStrategy)CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_Strategy); + + // We need quick jit for TieredPGO + if (!fTieredCompilation_QuickJit) + { + fTieredPGO = false; + } + else + { + if (fTieredPGO_Strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier || + fTieredPGO_Strategy == PromoteHotTier0ToInstrumentedTier) + { + // When we're not using optimizations in the instrumented tiers we produce a lot of new first-time compilation + // due to disabled inlining even for very small methods - such first-time compilations delay promotions by + // tieredCompilation_CallCountingDelayMs + tieredCompilation_CallCountingDelayMs /= 3; + tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); + } + _ASSERTE(fTieredPGO_Strategy >= 0 && fTieredPGO_Strategy <= 2 && + "Only '0', '1' and '2' strategies are currently supported"); + } #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index a18536599b998e..a9add99f55756a 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -51,6 +51,27 @@ enum ParseCtl { stopAfterRuntimeSection // stop after ... section }; +// Keep in sync with clrconfigvalues.h, see TieredPGO_Strategy +enum TieredPGOStrategy +{ + // Use InstrumentedTier for new code without R2R (or if it's disabled), R2R won't instrumented + UseInstrumentedTierForILOnly = 0, // Default behavior + + // Use InstrumentedTier for new code without R2R (or if it's disabled), hot R2R + // will be promoted to an intermediate InstrumentedTier (without optimizations in it) + UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier = 1, + + // Use InstrumentedTier for new code without R2R (or if it's disabled), hot R2R + // will be promoted to an intermediate InstrumentedTierOptimized (without optimizations in it) + UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized = 2, + + // NYI: + // In these modes we never instrument Tier0 and only promote hot Tier0 and R2R + // code to intermediate tiers with instrumentation + PromoteHotTier0ToInstrumentedTier = 3, + PromoteHotTier0ToInstrumentedTierOptimized = 4, +}; + class EEConfig { public: @@ -91,9 +112,8 @@ class EEConfig #endif #if defined(FEATURE_PGO) - bool TieredPGO(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO; } - bool TieredPGO_OptimizeInstrumentedTier(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO_OptimizeInstrumentedTier; } - bool TieredPGO_UseInstrumentedTierForR2R(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO_UseInstrumentedTierForR2R; } + bool TieredPGO(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO; } + TieredPGOStrategy TieredPGO_Strategy(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO_Strategy; } #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) @@ -656,8 +676,7 @@ class EEConfig #if defined(FEATURE_PGO) bool fTieredPGO; - bool fTieredPGO_OptimizeInstrumentedTier; - bool fTieredPGO_UseInstrumentedTierForR2R; + TieredPGOStrategy fTieredPGO_Strategy; #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 78a439d4332af1..c6266d36abe57b 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12595,23 +12595,6 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO #ifdef FEATURE_PGO - // Instrument, if - // - // * We're writing pgo data and we're jitting at Tier0. - // * Tiered PGO is enabled and we're jitting at Tier0. - // * Tiered PGO is enabled and we are jitting an OSR method. - // - if ((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_WritePGOData) > 0) - && flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0)) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - } - else if ((g_pConfig->TieredPGO()) - && (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0) || flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_OSR))) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - } - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ReadPGOData) > 0) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBOPT); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 7ee14b8d83b813..c1cf7559444453 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -2002,6 +2002,8 @@ class PrepareCodeConfig QuickJitted, OptimizedTier1, OptimizedTier1OSR, + InstrumentedTier, + InstrumentedTierOptimized, Count }; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 9149468d0cadd8..3cb5d4da04af1d 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1223,7 +1223,6 @@ PrepareCodeConfig::JitOptimizationTier PrepareCodeConfig::GetJitOptimizationTier switch (config->GetCodeVersion().GetOptimizationTier()) { case NativeCodeVersion::OptimizationTier0: - case NativeCodeVersion::OptimizationTier0Instrumented: return JitOptimizationTier::QuickJitted; case NativeCodeVersion::OptimizationTier1: @@ -1235,6 +1234,12 @@ PrepareCodeConfig::JitOptimizationTier PrepareCodeConfig::GetJitOptimizationTier case NativeCodeVersion::OptimizationTierOptimized: return JitOptimizationTier::Optimized; + case NativeCodeVersion::OptimizationTierInstrumented: + return JitOptimizationTier::InstrumentedTier; + + case NativeCodeVersion::OptimizationTierInstrumentedOptimized: + return JitOptimizationTier::InstrumentedTierOptimized; + default: UNREACHABLE(); } @@ -1257,6 +1262,8 @@ const char *PrepareCodeConfig::GetJitOptimizationTierStr(PrepareCodeConfig *conf case JitOptimizationTier::QuickJitted: return "QuickJitted"; case JitOptimizationTier::OptimizedTier1: return "OptimizedTier1"; case JitOptimizationTier::OptimizedTier1OSR: return "OptimizedTier1OSR"; + case JitOptimizationTier::InstrumentedTier: return "InstrumentedTier"; + case JitOptimizationTier::InstrumentedTierOptimized: return "InstrumentedTierOptimized"; default: UNREACHABLE(); @@ -1306,7 +1313,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() NativeCodeVersion::OptimizationTier previousOptimizationTier = GetCodeVersion().GetOptimizationTier(); _ASSERTE( previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || - previousOptimizationTier == NativeCodeVersion::OptimizationTier0Instrumented || + previousOptimizationTier == NativeCodeVersion::OptimizationTierInstrumented || previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); #endif // _DEBUG diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 71e31b53f8c863..cd87deb7582299 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -112,6 +112,13 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza return NativeCodeVersion::OptimizationTierOptimized; } +#ifdef FEATURE_PGO + if (g_pConfig->TieredPGO()) + { + return NativeCodeVersion::OptimizationTierInstrumented; + } +#endif + return NativeCodeVersion::OptimizationTier0; #else return NativeCodeVersion::OptimizationTierOptimized; @@ -266,20 +273,28 @@ void TieredCompilationManager::AsyncPromoteToTier1( NativeCodeVersion::OptimizationTier nextTier = NativeCodeVersion::OptimizationTier1; - // Compile to Tier0 with instrumentation if we promote from R2R and PGO is enabled. - // If that R2R has a static profile we might consider skipping it if we can rely on it being accurate - // e.g. if the current R2R image is Composite (in the default mode we might lose cross-module - // likely classes in the current implementation) - // Also, this Tier0 likely doesn't need any patchpoints since it's already survived a promotion and will - // likely survive it once again - if (g_pConfig->TieredPGO() && - pMethodDesc->IsEligibleForTieredCompilation() && - tier0NativeCodeVersion.IsDefaultVersion() && - tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && - ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + // If TieredPGO is enabled, follow TieredPGO_Strategy, see comments in clrconfigvalues.h around it + if (g_pConfig->TieredPGO() && pMethodDesc->IsEligibleForTieredCompilation()) { - _ASSERT(!pMethodDesc->RequestedAggressiveOptimization()); - nextTier = NativeCodeVersion::OptimizationTier0Instrumented; + TieredPGOStrategy strategy = g_pConfig->TieredPGO_Strategy(); + if ((strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier || + strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized) && + tier0NativeCodeVersion.IsDefaultVersion() && + tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && + ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + { + if (strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier) + { + // Promote hot R2R code to InstrumentedTier + nextTier = NativeCodeVersion::OptimizationTierInstrumented; + } + else + { + // Promote hot R2R code to InstrumentedTierOptimized + assert(strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized); + nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; + } + } } ILCodeVersion ilCodeVersion = tier0NativeCodeVersion.GetILCodeVersion(); @@ -1035,21 +1050,23 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) NativeCodeVersion::OptimizationTier newOptimizationTier; if (!methodDesc->RequestedAggressiveOptimization()) { + NativeCodeVersion::OptimizationTier currentTier = nativeCodeVersion.GetOptimizationTier(); + if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumented) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); + return flags; + } + + if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumentedOptimized) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + return flags; + } + if (g_pConfig->TieredCompilation_QuickJit()) { - if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier::OptimizationTier0Instrumented) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - if (g_pConfig->TieredPGO_OptimizeInstrumentedTier()) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); - } - else - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); - } - return flags; - } _ASSERTE(nativeCodeVersion.IsUnoptimizedTier()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); return flags; @@ -1073,21 +1090,15 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) switch (nativeCodeVersion.GetOptimizationTier()) { - case NativeCodeVersion::OptimizationTier0Instrumented: - if (g_pConfig->TieredCompilation_QuickJit()) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - if (g_pConfig->TieredPGO()) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); - } - else - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); - } - break; - } - FALLTHROUGH; + case NativeCodeVersion::OptimizationTierInstrumented: + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); + break; + + case NativeCodeVersion::OptimizationTierInstrumentedOptimized: + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + break; case NativeCodeVersion::OptimizationTier0: if (g_pConfig->TieredCompilation_QuickJit()) From db9c71d5242446980c629eb46b145256bab9a5a6 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 15 Jul 2022 17:13:53 +0200 Subject: [PATCH 16/53] Address feedback --- src/coreclr/inc/clrconfigvalues.h | 10 +++++----- src/coreclr/vm/callcounting.cpp | 8 ++++---- src/coreclr/vm/codeversion.cpp | 10 ++++------ src/coreclr/vm/codeversion.h | 2 +- src/coreclr/vm/interpreter.cpp | 2 +- src/coreclr/vm/prestub.cpp | 7 ++++--- src/coreclr/vm/tieredcompilation.cpp | 6 +++--- 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 9428427ea5d1a1..bfade5c074ad04 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -622,11 +622,11 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // TieredPGO_Strategy values: // -// 0 - [Default] Use InstrumentedTier for non-R2R code as the initial tier -// 1 - Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTier -// 2 - [NYI] Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTierOptimized -// 3 - [NYI] Promote hot Tier0/R2R to InstrumentedTier -// 4 - [NYI] Promote hot Tier0/R2R to InstrumentedTierOptimized +// 0 - [Default] Use InstrumentedTier for non-R2R code as the initial tier +// 1 - Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTier +// 2 - [Not tested] Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTierOptimized +// 3 - [NYI] Promote hot Tier0/R2R to InstrumentedTier +// 4 - [NYI] Promote hot Tier0/R2R to InstrumentedTierOptimized // // Pros & cons of using optimizations inside the instrumented tiers (modes '2' and '4') // Pros: diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index 09fb428b454a38..799521b94453f7 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -574,7 +574,7 @@ bool CallCountingManager::SetCodeEntryPoint( // For a default code version that is not tier 0, call counting will have been disabled by this time (checked // below). Avoid the redundant and not-insignificant expense of GetOptimizationTier() on a default code version. !activeCodeVersion.IsDefaultVersion() && - !activeCodeVersion.IsUnoptimizedTier() + activeCodeVersion.IsFinalTier() ) || !g_pConfig->TieredCompilation_CallCounting()) { @@ -602,7 +602,7 @@ bool CallCountingManager::SetCodeEntryPoint( return true; } - _ASSERTE(activeCodeVersion.IsUnoptimizedTier()); + _ASSERTE(!activeCodeVersion.IsFinalTier()); // If the tiering delay is active, postpone further work if (GetAppDomain() @@ -649,7 +649,7 @@ bool CallCountingManager::SetCodeEntryPoint( } else { - _ASSERTE(activeCodeVersion.IsUnoptimizedTier()); + _ASSERTE(!activeCodeVersion.IsFinalTier()); // If the tiering delay is active, postpone further work if (GetAppDomain() @@ -780,7 +780,7 @@ PCODE CallCountingManager::OnCallCountThresholdReached(TransitionBlock *transiti // used going forward under appropriate locking to synchronize further with deletion. GCX_PREEMP_THREAD_EXISTS(CURRENT_THREAD); - _ASSERTE(codeVersion.IsUnoptimizedTier()); + _ASSERTE(!codeVersion.IsFinalTier()); codeEntryPoint = codeVersion.GetNativeCode(); do diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp index 899e32736e0de0..81017ee322a292 100644 --- a/src/coreclr/vm/codeversion.cpp +++ b/src/coreclr/vm/codeversion.cpp @@ -333,11 +333,11 @@ NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() con } } -bool NativeCodeVersion::IsUnoptimizedTier() const +bool NativeCodeVersion::IsFinalTier() const { LIMITED_METHOD_DAC_CONTRACT; OptimizationTier tier = GetOptimizationTier(); - return tier == OptimizationTier0 || tier == OptimizationTierInstrumented; + return tier == OptimizationTier1 || tier == OptimizationTierOptimized; } #ifndef DACCESS_COMPILE @@ -815,7 +815,7 @@ bool ILCodeVersion::HasAnyOptimizedNativeCodeVersion(NativeCodeVersion tier0Nati _ASSERTE(!tier0NativeCodeVersion.IsNull()); _ASSERTE(tier0NativeCodeVersion.GetILCodeVersion() == *this); _ASSERTE(tier0NativeCodeVersion.GetMethodDesc()->IsEligibleForTieredCompilation()); - _ASSERTE(tier0NativeCodeVersion.IsUnoptimizedTier()); + _ASSERTE(!tier0NativeCodeVersion.IsFinalTier()); NativeCodeVersionCollection nativeCodeVersions = GetNativeCodeVersions(tier0NativeCodeVersion.GetMethodDesc()); for (auto itEnd = nativeCodeVersions.End(), it = nativeCodeVersions.Begin(); it != itEnd; ++it) @@ -1715,9 +1715,7 @@ PCODE CodeVersionManager::PublishVersionableCodeIfNecessary( { #ifdef FEATURE_TIERED_COMPILATION _ASSERTE(!config->ShouldCountCalls() || pMethodDesc->IsEligibleForTieredCompilation()); - _ASSERTE( - !config->ShouldCountCalls() || - activeVersion.IsUnoptimizedTier()); + _ASSERTE(!config->ShouldCountCalls() || !activeVersion.IsFinalTier()); if (config->ShouldCountCalls()) // the generated code was at a tier that is call-counted { // This is the first call to a call-counted code version of the method diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 36cb4ec418e3f8..7558031cdf6d73 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -83,7 +83,7 @@ class NativeCodeVersion }; #ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; - bool IsUnoptimizedTier() const; + bool IsFinalTier() const; #ifndef DACCESS_COMPILE void SetOptimizationTier(OptimizationTier tier); #endif diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 8a6dfa6596534b..d5078538577c8f 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -1802,7 +1802,7 @@ void Interpreter::JitMethodIfAppropriate(InterpreterMethodInfo* interpMethInfo, CodeVersionManager::LockHolder _lockHolder; NativeCodeVersion activeCodeVersion = md->GetCodeVersionManager()->GetActiveILCodeVersion(md).GetActiveNativeCodeVersion(md); ILCodeVersion ilCodeVersion = activeCodeVersion.GetILCodeVersion(); - if (activeCodeVersion.IsUnoptimizedTier() && + if (!activeCodeVersion.IsFinalTier() && !ilCodeVersion.HasAnyOptimizedNativeCodeVersion(activeCodeVersion)) { tieredCompilationManager->AsyncPromoteToTier1(activeCodeVersion, &scheduleTieringBackgroundWork); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 3cb5d4da04af1d..312044e60c9d1b 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -364,9 +364,9 @@ PCODE MethodDesc::PrepareILBasedCode(PrepareCodeConfig* pConfig) if (codeVersion.IsDefaultVersion()) { pConfig->GetMethodDesc()->GetLoaderAllocator()->GetCallCountingManager()->DisableCallCounting(codeVersion); - _ASSERTE(!codeVersion.IsUnoptimizedTier()); + _ASSERTE(codeVersion.IsFinalTier()); } - else if (codeVersion.IsUnoptimizedTier()) + else if (!codeVersion.IsFinalTier()) { codeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); } @@ -465,7 +465,7 @@ PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig, bool shouldTier #ifdef FEATURE_TIERED_COMPILATION if (shouldCountCalls) { - _ASSERTE(pConfig->GetCodeVersion().IsUnoptimizedTier()); + _ASSERTE(!pConfig->GetCodeVersion().IsFinalTier()); pConfig->SetShouldCountCalls(); } #endif @@ -1314,6 +1314,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() _ASSERTE( previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || previousOptimizationTier == NativeCodeVersion::OptimizationTierInstrumented || + previousOptimizationTier == NativeCodeVersion::OptimizationTierInstrumentedOptimized || previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); #endif // _DEBUG diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index cd87deb7582299..bf96c2dd37d59c 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -258,7 +258,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread()); _ASSERTE(!tier0NativeCodeVersion.IsNull()); - _ASSERTE(tier0NativeCodeVersion.IsUnoptimizedTier()); + _ASSERTE(!tier0NativeCodeVersion.IsFinalTier()); _ASSERTE(createTieringBackgroundWorkerRef != nullptr); NativeCodeVersion t1NativeCodeVersion; @@ -1027,7 +1027,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) _ASSERTE(config != nullptr); _ASSERTE( !config->WasTieringDisabledBeforeJitting() || - !config->GetCodeVersion().IsUnoptimizedTier()); + config->GetCodeVersion().IsFinalTier()); CORJIT_FLAGS flags; @@ -1067,7 +1067,7 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (g_pConfig->TieredCompilation_QuickJit()) { - _ASSERTE(nativeCodeVersion.IsUnoptimizedTier()); + _ASSERTE(!nativeCodeVersion.IsFinalTier()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); return flags; } From 59b2bc98b323538b2da9d39d6210d0568cde4423 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 15 Jul 2022 18:43:58 +0200 Subject: [PATCH 17/53] Add docs --- ...micPgo-InstrumentedTiers-Plaintext-opt.png | Bin 0 -> 64795 bytes ...DynamicPgo-InstrumentedTiers-Plaintext.png | Bin 0 -> 64761 bytes .../features/DynamicPgo-InstrumentedTiers.md | 69 ++++++++++++++++++ docs/design/features/DynamicPgo.md | 6 +- src/coreclr/vm/prestub.cpp | 7 +- 5 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 docs/design/features/DynamicPgo-InstrumentedTiers-Plaintext-opt.png create mode 100644 docs/design/features/DynamicPgo-InstrumentedTiers-Plaintext.png create mode 100644 docs/design/features/DynamicPgo-InstrumentedTiers.md diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers-Plaintext-opt.png b/docs/design/features/DynamicPgo-InstrumentedTiers-Plaintext-opt.png new file mode 100644 index 0000000000000000000000000000000000000000..9ba88e141bf3a7f55858815e0d3e014c1ee707a8 GIT binary patch literal 64795 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 rEN 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

S4jt!4 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

C(7Gb2DR7c{diSDZjJhAj62^}r>l;0us_I? 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 hasAO{"Marked with
[AggressiveOpts]?"} + hasAO-->|Yes|tier1ao["JIT to Tier1

(that attribute is extremely
rarely a good idea)"] + hasAO-->|No|hasR2R + hasR2R{"Is prejitted (R2R)
and ReadyToRun==1"?} -->|No| tier00 + + + tier00["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot55 + ishot55{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo22 + tier1pgo22["JIT to Tier1

(optimized with profile data)"] + ishot55-.->|No,
keep running...|ishot55 + + + hasR2R -->|Yes| R2R + R2R["Use R2R code

(optimized, not instrumented,
no patchpoints)"] -->|Running...|ishot1 + ishot1{"Is hot?
(called >30 times)"}-.->|No,
keep running...|ishot1 + ishot1--->|"Yes"|istier1inst + tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 + tier1pgo2["JIT to Tier1

(optimized with profile data)"] + + istier1inst{"Enable optimizations
for InstrumentedTier?"}-->|"No (default)"|tier0 + istier1inst-.->|"Yes (not well tested yet)"|tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
no patchpoints)"] + tier1inst-->|Running...|ishot5 + ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 + ishot5-.->|No,
keep running...|ishot5 +``` +(_VSCode doesn't support mermaid diagrams, consider installing external add-ins_) + +As we can see from the diagram, it's also possible to choose whether to use optimizations in the instrumentation tier or not - it's controlled via `DOTNET_TieredPGO_Strategy` knob. + +## Pros & cons of using optimizations inside the instrumented tiers + +Pros: +* Lower overhead from instrumentation (and thanks to optimizations we _can_ optimize probes and emit less of those) +* Optimized code is able to inline methods so we won't be producing new Compilation units for even small methods + +Cons: +* Currently, we won't instrument inlinees -> we'll probably miss a lot of opportunities and produce less accurate profile leading to a less optimized final tier \ No newline at end of file diff --git a/docs/design/features/DynamicPgo.md b/docs/design/features/DynamicPgo.md index 464e87950cbece..2bd227d9618b8e 100644 --- a/docs/design/features/DynamicPgo.md +++ b/docs/design/features/DynamicPgo.md @@ -257,9 +257,9 @@ If we confidently could identify the top N% of methods (say 5%) then one could i R2R methods bypass Tier0 and so don't get instrumentation in the current TieredPGO prototype. We probably don't want to instrument the code in the R2R image. And many of these R2R methods are key framework methods that are important for performance. So we need to find a way to get data for these methods. There are a few basic ideas: -* Leverage IBC. If there is IBC data in the R2R image then we can make that data available to the JIT. It may not be as relevant as in-process collected data, but it's quite likely better than synthetic data or no data. -* Sampled instrumentation for R2R methods. Produce an instrumented version and run it every so often before the method gets promoted to Tier1. This may be costly, especially if we have to use unoptimized methods for instrumentation, as we'll do quite a bit of extra jitting. -* Make R2R methods go through Tier0 on their way to Tier1. Likely introduces an unacceptable perf hit. +1) Leverage IBC. If there is IBC data in the R2R image then we can make that data available to the JIT. It may not be as relevant as in-process collected data, but it's quite likely better than synthetic data or no data. +2) Sampled instrumentation for R2R methods. Produce an instrumented version and run it every so often before the method gets promoted to Tier1. This may be costly, especially if we have to use unoptimized methods for instrumentation, as we'll do quite a bit of extra jitting. +3) Make R2R methods go through a separate instrumentation tier on their way to Tier1, see [DynamicPgo-InstrumentedTiers.md](DynamicPgo-InstrumentedTiers.md) prototype. #### Dynamic PGO, QuickJitForLoops, OSR diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 312044e60c9d1b..3ab7dd90ce499e 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1310,12 +1310,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() // native code version of a method. The current optimization tier should be consistent with the change being made // (Tier 0 to Optimized), such that the tier is not changed in an unexpected way or at an unexpected time. Since changes // to the optimization tier are unlocked, this assertion is just a speculative check on possible values. - NativeCodeVersion::OptimizationTier previousOptimizationTier = GetCodeVersion().GetOptimizationTier(); - _ASSERTE( - previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || - previousOptimizationTier == NativeCodeVersion::OptimizationTierInstrumented || - previousOptimizationTier == NativeCodeVersion::OptimizationTierInstrumentedOptimized || - previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); + _ASSERTE(!GetCodeVersion().IsFinalTier()); #endif // _DEBUG // Update the tier in the code version. The JIT may have decided to switch from tier 0 to optimized, in which case From ca5c347829e48963b939d449273d8bfdefd666d1 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 16 Jul 2022 10:18:34 +0200 Subject: [PATCH 18/53] Enable other strategies --- src/coreclr/inc/clrconfigvalues.h | 4 +- src/coreclr/vm/eeconfig.cpp | 3 +- src/coreclr/vm/tieredcompilation.cpp | 61 ++++++++++++++++++++-------- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index bfade5c074ad04..942df5b1d52505 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -625,8 +625,8 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // 0 - [Default] Use InstrumentedTier for non-R2R code as the initial tier // 1 - Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTier // 2 - [Not tested] Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTierOptimized -// 3 - [NYI] Promote hot Tier0/R2R to InstrumentedTier -// 4 - [NYI] Promote hot Tier0/R2R to InstrumentedTierOptimized +// 3 - [Not tested] Promote hot Tier0/R2R to InstrumentedTier +// 4 - [Not tested] Promote hot Tier0/R2R to InstrumentedTierOptimized // // Pros & cons of using optimizations inside the instrumented tiers (modes '2' and '4') // Pros: diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 2ba89a5d7a4fd8..2f257f7167c7f9 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -794,8 +794,7 @@ HRESULT EEConfig::sync() tieredCompilation_CallCountingDelayMs /= 3; tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); } - _ASSERTE(fTieredPGO_Strategy >= 0 && fTieredPGO_Strategy <= 2 && - "Only '0', '1' and '2' strategies are currently supported"); + _ASSERTE(fTieredPGO_Strategy >= 0 && fTieredPGO_Strategy <= 4); } #endif diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index bf96c2dd37d59c..dd2eda1e865d09 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -115,7 +115,15 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza #ifdef FEATURE_PGO if (g_pConfig->TieredPGO()) { - return NativeCodeVersion::OptimizationTierInstrumented; + switch (g_pConfig->TieredPGO_Strategy()) + { + case UseInstrumentedTierForILOnly: + case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier: + case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized: + return NativeCodeVersion::OptimizationTierInstrumented; + default: + return NativeCodeVersion::OptimizationTier0; + } } #endif @@ -276,23 +284,44 @@ void TieredCompilationManager::AsyncPromoteToTier1( // If TieredPGO is enabled, follow TieredPGO_Strategy, see comments in clrconfigvalues.h around it if (g_pConfig->TieredPGO() && pMethodDesc->IsEligibleForTieredCompilation()) { - TieredPGOStrategy strategy = g_pConfig->TieredPGO_Strategy(); - if ((strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier || - strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized) && - tier0NativeCodeVersion.IsDefaultVersion() && - tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && - ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + if (tier0NativeCodeVersion.IsDefaultVersion() && + tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0) { - if (strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier) - { - // Promote hot R2R code to InstrumentedTier - nextTier = NativeCodeVersion::OptimizationTierInstrumented; - } - else + switch (g_pConfig->TieredPGO_Strategy()) { - // Promote hot R2R code to InstrumentedTierOptimized - assert(strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized); - nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; + // 0: Always use TierInstrumented for new ILOnly code + case UseInstrumentedTierForILOnly: + nextTier = NativeCodeVersion::OptimizationTierInstrumented; + break; + + // 1: Promote hot R2R code to TierInstrumented + case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier: + if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + { + nextTier = NativeCodeVersion::OptimizationTierInstrumented; + } + break; + + // 2: Promote hot R2R code to TierInstrumentedOptimized + case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized: + if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + { + nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; + } + break; + + // 3: Promote hot Tier0/R2R code to TierInstrumented + case PromoteHotTier0ToInstrumentedTier: + nextTier = NativeCodeVersion::OptimizationTierInstrumented; + break; + + // 4: Promote hot Tier0/R2R code to TierInstrumentedOptimized + case PromoteHotTier0ToInstrumentedTierOptimized: + nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; + break; + + default: + _ASSERT("Unknown TieredPGO_Strategy"); } } } From a6318a0dea5be6f71bbd2007b158f50298d55eeb Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 16 Jul 2022 10:20:25 +0200 Subject: [PATCH 19/53] Clean up --- src/coreclr/vm/eeconfig.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index a9add99f55756a..93d399089e467c 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -61,11 +61,12 @@ enum TieredPGOStrategy // will be promoted to an intermediate InstrumentedTier (without optimizations in it) UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier = 1, + // NOTE: The following strategies work but aren't tested yet: + // Use InstrumentedTier for new code without R2R (or if it's disabled), hot R2R // will be promoted to an intermediate InstrumentedTierOptimized (without optimizations in it) UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized = 2, - // NYI: // In these modes we never instrument Tier0 and only promote hot Tier0 and R2R // code to intermediate tiers with instrumentation PromoteHotTier0ToInstrumentedTier = 3, From f75e2898d081eeba0387c79afcd74623e65c5dc7 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 16 Jul 2022 10:39:56 +0200 Subject: [PATCH 20/53] Fix GetInitialOptimizationTier --- src/coreclr/vm/tieredcompilation.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index dd2eda1e865d09..930e643ad30de1 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -120,7 +120,16 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza case UseInstrumentedTierForILOnly: case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier: case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized: + { + CodeVersionManager::LockHolder codeVersioningLockHolder; + NativeCodeVersion version = pMethodDesc->GetCodeVersionManager()->GetActiveILCodeVersion(pMethodDesc) + .GetActiveNativeCodeVersion(pMethodDesc); + if (!version.IsNull() && ExecutionManager::IsReadyToRunCode(version.GetNativeCode())) + { + return NativeCodeVersion::OptimizationTier0; + } return NativeCodeVersion::OptimizationTierInstrumented; + } default: return NativeCodeVersion::OptimizationTier0; } From e9e12ea59d6b1bd5e11e74726e67d490af6869b6 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 16 Jul 2022 11:14:55 +0200 Subject: [PATCH 21/53] Enable optimized Instrumentations --- docs/design/features/DynamicPgo-InstrumentedTiers.md | 2 +- src/coreclr/jit/compiler.h | 10 ++++++++++ src/coreclr/jit/fgprofile.cpp | 8 ++++---- src/coreclr/jit/importer.cpp | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index fede0523bcf8bd..e4903bbc589340 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -43,7 +43,7 @@ flowchart hasR2R -->|Yes| R2R - R2R["Use R2R code

(optimized, not instrumented,
no patchpoints)"] -->|Running...|ishot1 + R2R["Use R2R code

(optimized, not instrumented,
with patchpoints)"] -->|Running...|ishot1 ishot1{"Is hot?
(called >30 times)"}-.->|No,
keep running...|ishot1 ishot1--->|"Yes"|istier1inst tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index be9610aa3c3d76..aff6733f38e9e4 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9101,6 +9101,16 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } #endif + bool IsInstrumented() const + { + return jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR); + } + + bool IsInstrumentedOptimized() const + { + return IsInstrumented() && jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1); + } + // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating // PInvoke transitions inline. Normally used by R2R, but also used when generating a reverse pinvoke frame, as // the current logic for frame setup initializes and pushes diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index ae79f10af131af..e2b5bb3f6c64fe 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -383,7 +383,7 @@ void BlockCountInstrumentor::Prepare(bool preImport) // // If we see any, we need to adjust our instrumentation pattern. // - if (m_comp->opts.IsOSR() && ((m_comp->optMethodFlags & OMF_HAS_TAILCALL_SUCCESSOR) != 0)) + if (m_comp->opts.IsInstrumentedOptimized() && ((m_comp->optMethodFlags & OMF_HAS_TAILCALL_SUCCESSOR) != 0)) { JITDUMP("OSR + PGO + potential tail call --- preparing to relocate block probes\n"); @@ -1887,8 +1887,8 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() (JitConfig.TC_PartialCompilation() > 0); const bool prejit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); const bool tier0WithPatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && mayHavePatchpoints; - const bool osrMethod = opts.IsOSR(); - const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !osrMethod; + const bool instrOpt = opts.IsInstrumentedOptimized(); + const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !instrOpt; if (useEdgeProfiles) { @@ -1899,7 +1899,7 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() JITDUMP("Using block profiling, because %s\n", (JitConfig.JitEdgeProfiling() == 0) ? "edge profiles disabled" - : prejit ? "prejitting" : osrMethod ? "OSR" : "tier0 with patchpoints"); + : prejit ? "prejitting" : instrOpt ? "optimized instr" : "tier0 with patchpoints"); fgCountInstrumentor = new (this, CMK_Pgo) BlockCountInstrumentor(this); } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 65699f7f6ee4e0..095756b32736ae 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -22265,7 +22265,7 @@ bool Compiler::impConsiderCallProbe(GenTreeCall* call, IL_OFFSET ilOffset) return false; } - assert(opts.OptimizationDisabled() || opts.IsOSR()); + assert(opts.OptimizationDisabled() || opts.IsInstrumentedOptimized()); assert(!compIsForInlining()); // During importation, optionally flag this block as one that From 81496c425231010cc132d33415b02ea295c6f1bd Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 16 Jul 2022 11:34:43 +0200 Subject: [PATCH 22/53] Update diagram --- .../features/DynamicPgo-InstrumentedTiers.md | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index e4903bbc589340..c97ccd6a89e733 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -7,9 +7,7 @@ two existing limitations of the current design: 1) R2R code never benefits from Dynamic PGO as it's not instrumented and is promoted straight to Tier1 when it's hot 2) Instrumentation in Tier0 comes with a big overhead and it's better to only instrument hot Tier0 code (whether it's ILOnly or R2R) -As of today, [#70941](https://github.com/dotnet/runtime/pull/70941) only provides a solution for the 1st problem by introducing a new tier for hot R2R code in order to instrument it first, collect profile and only then compile to the final tier. Thus, we don't sacrifice start up speed and still benefit from Dynamic PGO. -A good example explaining the problem is this TechEmpower benchmark: - +A good example explaining the problem with non-instrumented R2R code is this TechEmpower benchmark: ![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext.png) @@ -33,32 +31,36 @@ flowchart prestub(.NET Function) -->|Compilation| hasAO{"Marked with
[AggressiveOpts]?"} hasAO-->|Yes|tier1ao["JIT to Tier1

(that attribute is extremely
rarely a good idea)"] hasAO-->|No|hasR2R - hasR2R{"Is prejitted (R2R)
and ReadyToRun==1"?} -->|No| tier00 - - - tier00["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot55 - ishot55{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo22 - tier1pgo22["JIT to Tier1

(optimized with profile data)"] - ishot55-.->|No,
keep running...|ishot55 - + hasR2R{"Is prejitted (R2R)
and ReadyToRun==1"?} -->|No| istrTier0Q + + istrTier0Q{"TieredPGO_Strategy:
Instrument only
hot Tier0 code?"} + istrTier0Q-->|No, always instrument tier0|tier0 + istrTier0Q-->|Yes, only hot|tier000 + tier000["JIT to Tier0

(not optimized, not instrumented,
with patchpoints)"]-->ishot555 + ishot555{"Is hot?
(called >30 times)"} + ishot555-.->|No,
keep running...|ishot555 + ishot555-->|Yes|istier1inst hasR2R -->|Yes| R2R R2R["Use R2R code

(optimized, not instrumented,
with patchpoints)"] -->|Running...|ishot1 ishot1{"Is hot?
(called >30 times)"}-.->|No,
keep running...|ishot1 - ishot1--->|"Yes"|istier1inst + ishot1--->|"Yes"|instrumentR2R + + instrumentR2R{"TieredPGO_Strategy:
Instrument hot
R2R'd code?"} + instrumentR2R-->|Yes, instrument R2R'd code|istier1inst + instrumentR2R-->|No, don't instrument R2R'd code|tier1nopgo["JIT to Tier1

(no dynamic profile data)"] + tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 tier1pgo2["JIT to Tier1

(optimized with profile data)"] - istier1inst{"Enable optimizations
for InstrumentedTier?"}-->|"No (default)"|tier0 - istier1inst-.->|"Yes (not well tested yet)"|tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
no patchpoints)"] + istier1inst{"TieredPGO_Strategy:
Enable optimizations
for InstrumentedTier?"}-->|"No"|tier0 + istier1inst--->|"Yes"|tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
with patchpoints)"] tier1inst-->|Running...|ishot5 ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 ishot5-.->|No,
keep running...|ishot5 ``` (_VSCode doesn't support mermaid diagrams, consider installing external add-ins_) -As we can see from the diagram, it's also possible to choose whether to use optimizations in the instrumentation tier or not - it's controlled via `DOTNET_TieredPGO_Strategy` knob. - ## Pros & cons of using optimizations inside the instrumented tiers Pros: From be329a19fc2db4c5fc847dbe19277655014eb114 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 16 Jul 2022 14:27:53 +0200 Subject: [PATCH 23/53] Fix assert --- src/coreclr/vm/codeversion.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp index 81017ee322a292..ac3a50787a3ce1 100644 --- a/src/coreclr/vm/codeversion.cpp +++ b/src/coreclr/vm/codeversion.cpp @@ -151,7 +151,13 @@ NativeCodeVersion::OptimizationTier NativeCodeVersionNode::GetOptimizationTier() void NativeCodeVersionNode::SetOptimizationTier(NativeCodeVersion::OptimizationTier tier) { LIMITED_METHOD_CONTRACT; - _ASSERTE(tier >= m_optTier); + + if (tier != m_optTier && + (m_optTier == NativeCodeVersion::OptimizationTier::OptimizationTier1 || + m_optTier == NativeCodeVersion::OptimizationTier::OptimizationTierOptimized)) + { + _ASSERTE("Unexpected deoptimization"); + } m_optTier = tier; } From 48e375e42790f684d6891f7a65104fdad4387f01 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 16 Jul 2022 15:19:35 +0200 Subject: [PATCH 24/53] Update --- ...micPgo-InstrumentedTiers-Plaintext-opt.png | Bin 64795 -> 72967 bytes .../features/DynamicPgo-InstrumentedTiers.md | 5 ++--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers-Plaintext-opt.png b/docs/design/features/DynamicPgo-InstrumentedTiers-Plaintext-opt.png index 9ba88e141bf3a7f55858815e0d3e014c1ee707a8..c795c7d067b674a8c1ce58a8a5fa43ca042ec264 100644 GIT binary patch literal 72967 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

S4jt!4 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 k*!aJ_wy|5u@(A@9n5e^*8;qm=*Oyn4Q Date: Sat, 16 Jul 2022 15:28:00 +0200 Subject: [PATCH 25/53] Add test --- .../InstrumentedTiers/InstrumentedTiers.cs | 33 +++++++++++++++++++ .../InstrumentedTiers_strat1.csproj | 21 ++++++++++++ .../InstrumentedTiers_strat2.csproj | 21 ++++++++++++ .../InstrumentedTiers_strat3.csproj | 21 ++++++++++++ .../InstrumentedTiers_strat4.csproj | 21 ++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers.cs create mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat1.csproj create mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj create mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj create mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers.cs b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers.cs new file mode 100644 index 00000000000000..f3705502e9967c --- /dev/null +++ b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +// A smoke test for all DOTNET_TieredPGO strategies +class Program : IDisposable +{ + static int Main() + { + Program p = new(); + for (int i = 0; i < 100; i++) + { + HotLoop(p); + Thread.Sleep(40); // cold loop + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void HotLoop(IDisposable d) + { + for (int i = 0; i < 100000; i++) // hot loop + d?.Dispose(); + } + + public void Dispose() => Test(); + + [MethodImpl(MethodImplOptions.NoInlining)] + void Test() { } +} \ No newline at end of file diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat1.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat1.csproj new file mode 100644 index 00000000000000..8d85f1a8ae5a49 --- /dev/null +++ b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat1.csproj @@ -0,0 +1,21 @@ + + + Exe + True + + + + + + + \ No newline at end of file diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj new file mode 100644 index 00000000000000..6e797ba5b97714 --- /dev/null +++ b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj @@ -0,0 +1,21 @@ + + + Exe + True + + + + + + + \ No newline at end of file diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj new file mode 100644 index 00000000000000..8b44f43d986597 --- /dev/null +++ b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj @@ -0,0 +1,21 @@ + + + Exe + True + + + + + + + \ No newline at end of file diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj new file mode 100644 index 00000000000000..6830c1c6e6873f --- /dev/null +++ b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj @@ -0,0 +1,21 @@ + + + Exe + True + + + + + + + \ No newline at end of file From f6b457ac08ac6f1878711e50107acddf60124b8f Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 17 Jul 2022 12:20:48 +0200 Subject: [PATCH 26/53] Update clrconfigvalues.h CI Test --- src/coreclr/inc/clrconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 942df5b1d52505..bb7ee5a69a2020 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -637,7 +637,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // * Currently, we won't instrument inlinees -> we'll probably miss a lot of oportunities and produce less accurate profile // leading to a less optimized final tier // -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 0, "Strategy for TieredPGO, see comments in clrconfigvalues.h") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 1, "Strategy for TieredPGO, see comments in clrconfigvalues.h") #endif /// From cbddf5bc42833e95a0feea14f487439f9f5d8df3 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 17 Jul 2022 14:44:11 +0200 Subject: [PATCH 27/53] Update clrconfigvalues.h --- src/coreclr/inc/clrconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index bb7ee5a69a2020..ac9f4f5274f191 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -637,7 +637,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // * Currently, we won't instrument inlinees -> we'll probably miss a lot of oportunities and produce less accurate profile // leading to a less optimized final tier // -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 1, "Strategy for TieredPGO, see comments in clrconfigvalues.h") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 3, "Strategy for TieredPGO, see comments in clrconfigvalues.h") #endif /// From 9345815ca75d6dd5e10a10e27089bee0f957fcd7 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 17 Jul 2022 20:26:05 +0200 Subject: [PATCH 28/53] Update clrconfigvalues.h --- src/coreclr/inc/clrconfigvalues.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index ac9f4f5274f191..0be17dd57c01b0 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -625,7 +625,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // 0 - [Default] Use InstrumentedTier for non-R2R code as the initial tier // 1 - Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTier // 2 - [Not tested] Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTierOptimized -// 3 - [Not tested] Promote hot Tier0/R2R to InstrumentedTier +// 3 - Promote hot Tier0/R2R to InstrumentedTier // 4 - [Not tested] Promote hot Tier0/R2R to InstrumentedTierOptimized // // Pros & cons of using optimizations inside the instrumented tiers (modes '2' and '4') @@ -637,7 +637,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // * Currently, we won't instrument inlinees -> we'll probably miss a lot of oportunities and produce less accurate profile // leading to a less optimized final tier // -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 3, "Strategy for TieredPGO, see comments in clrconfigvalues.h") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 0, "Strategy for TieredPGO, see comments in clrconfigvalues.h") #endif /// From a073ed7e9a190d29ee6f0e7c6244732daab41a0b Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 6 Aug 2022 22:54:08 +0200 Subject: [PATCH 29/53] Resolve conflicts, address feedback --- src/coreclr/inc/clrconfigvalues.h | 3 +-- src/coreclr/vm/eeconfig.cpp | 21 +++++++++++++++++++++ src/coreclr/vm/eeconfig.h | 5 ++--- src/coreclr/vm/prestub.cpp | 19 ++++++++++++------- src/coreclr/vm/tieredcompilation.cpp | 13 +++++-------- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index fe2fc5c5ad965c..bf95a7a1eb8e28 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -619,9 +619,8 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // 1 - Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTier // 2 - [Not tested] Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTierOptimized // 3 - Promote hot Tier0/R2R to InstrumentedTier -// 4 - [Not tested] Promote hot Tier0/R2R to InstrumentedTierOptimized // -// Pros & cons of using optimizations inside the instrumented tiers (modes '2' and '4') +// Pros & cons of using optimizations inside the instrumented tiers (mode '2') // Pros: // * Lower overhead from instrumentation (and thanks to optimizations we _can_ optimize probes and emit less of those) // * Optimized code is able to inline methods so we won't be producing new Compilation units for even small methods diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 883d602b1f2309..612c15c7cb3879 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -239,6 +239,7 @@ HRESULT EEConfig::Init() #if defined(FEATURE_PGO) fTieredPGO = false; + tieredPGO_Strategy = (TieredPGOStrategy)0; #endif #if defined(FEATURE_READYTORUN) @@ -701,6 +702,26 @@ HRESULT EEConfig::sync() #if defined(FEATURE_PGO) fTieredPGO = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredPGO"), CLRConfig::EXTERNAL_TieredPGO); + tieredPGO_Strategy = (TieredPGOStrategy)CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_Strategy); + + // We need quick jit for TieredPGO + if (!fTieredCompilation_QuickJit) + { + fTieredPGO = false; + } + else + { + if (tieredPGO_Strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier || + tieredPGO_Strategy == PromoteHotTier0ToInstrumentedTier) + { + // When we're not using optimizations in the instrumented tiers we produce a lot of new first-time compilation + // due to disabled inlining even for very small methods - such first-time compilations delay promotions by + // tieredCompilation_CallCountingDelayMs + tieredCompilation_CallCountingDelayMs /= 3; + tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); + } + _ASSERTE(tieredPGO_Strategy >= 0 && tieredPGO_Strategy <= 4); + } #endif #if defined(FEATURE_TIERED_COMPILATION) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index 2e87a86eccc869..990073c41095c7 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -70,7 +70,6 @@ enum TieredPGOStrategy // In these modes we never instrument Tier0 and only promote hot Tier0 and R2R // code to intermediate tiers with instrumentation PromoteHotTier0ToInstrumentedTier = 3, - PromoteHotTier0ToInstrumentedTierOptimized = 4, }; class EEConfig @@ -114,7 +113,7 @@ class EEConfig #if defined(FEATURE_PGO) bool TieredPGO(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO; } - TieredPGOStrategy TieredPGO_Strategy(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO_Strategy; } + TieredPGOStrategy TieredPGO_Strategy(void) const { LIMITED_METHOD_CONTRACT; return tieredPGO_Strategy; } #endif #if defined(FEATURE_READYTORUN) @@ -681,7 +680,7 @@ class EEConfig #if defined(FEATURE_PGO) bool fTieredPGO; - TieredPGOStrategy fTieredPGO_Strategy; + TieredPGOStrategy tieredPGO_Strategy; #endif #if defined(FEATURE_READYTORUN) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 7df9cf15540ed9..53a6a85bdb4c18 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -364,9 +364,9 @@ PCODE MethodDesc::PrepareILBasedCode(PrepareCodeConfig* pConfig) if (codeVersion.IsDefaultVersion()) { pConfig->GetMethodDesc()->GetLoaderAllocator()->GetCallCountingManager()->DisableCallCounting(codeVersion); - _ASSERTE(codeVersion.GetOptimizationTier() != NativeCodeVersion::OptimizationTier0); + _ASSERTE(codeVersion.IsFinalTier()); } - else if (codeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0) + else if (!codeVersion.IsFinalTier()) { codeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); } @@ -457,7 +457,7 @@ PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig, bool shouldTier #ifdef FEATURE_TIERED_COMPILATION if (shouldCountCalls) { - _ASSERTE(pConfig->GetCodeVersion().GetOptimizationTier() == NativeCodeVersion::OptimizationTier0); + _ASSERTE(!pConfig->GetCodeVersion().IsFinalTier()); pConfig->SetShouldCountCalls(); } #endif @@ -1225,6 +1225,12 @@ PrepareCodeConfig::JitOptimizationTier PrepareCodeConfig::GetJitOptimizationTier case NativeCodeVersion::OptimizationTierOptimized: return JitOptimizationTier::Optimized; + case NativeCodeVersion::OptimizationTierInstrumented: + return JitOptimizationTier::InstrumentedTier; + + case NativeCodeVersion::OptimizationTierInstrumentedOptimized: + return JitOptimizationTier::InstrumentedTierOptimized; + default: UNREACHABLE(); } @@ -1247,6 +1253,8 @@ const char *PrepareCodeConfig::GetJitOptimizationTierStr(PrepareCodeConfig *conf case JitOptimizationTier::QuickJitted: return "QuickJitted"; case JitOptimizationTier::OptimizedTier1: return "OptimizedTier1"; case JitOptimizationTier::OptimizedTier1OSR: return "OptimizedTier1OSR"; + case JitOptimizationTier::InstrumentedTier: return "InstrumentedTier"; + case JitOptimizationTier::InstrumentedTierOptimized: return "InstrumentedTierOptimized"; default: UNREACHABLE(); @@ -1293,10 +1301,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() // native code version of a method. The current optimization tier should be consistent with the change being made // (Tier 0 to Optimized), such that the tier is not changed in an unexpected way or at an unexpected time. Since changes // to the optimization tier are unlocked, this assertion is just a speculative check on possible values. - NativeCodeVersion::OptimizationTier previousOptimizationTier = GetCodeVersion().GetOptimizationTier(); - _ASSERTE( - previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || - previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); + _ASSERTE(!GetCodeVersion().IsFinalTier()); #endif // _DEBUG // Update the tier in the code version. The JIT may have decided to switch from tier 0 to optimized, in which case diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 930e643ad30de1..cd6f3c7a5f620c 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -290,6 +290,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( NativeCodeVersion::OptimizationTier nextTier = NativeCodeVersion::OptimizationTier1; +#ifdef FEATURE_PGO // If TieredPGO is enabled, follow TieredPGO_Strategy, see comments in clrconfigvalues.h around it if (g_pConfig->TieredPGO() && pMethodDesc->IsEligibleForTieredCompilation()) { @@ -303,7 +304,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( nextTier = NativeCodeVersion::OptimizationTierInstrumented; break; - // 1: Promote hot R2R code to TierInstrumented + // 1: Promote hot R2R code to TierInstrumented case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier: if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) { @@ -311,7 +312,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( } break; - // 2: Promote hot R2R code to TierInstrumentedOptimized + // 2: Promote hot R2R code to TierInstrumentedOptimized case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized: if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) { @@ -324,16 +325,12 @@ void TieredCompilationManager::AsyncPromoteToTier1( nextTier = NativeCodeVersion::OptimizationTierInstrumented; break; - // 4: Promote hot Tier0/R2R code to TierInstrumentedOptimized - case PromoteHotTier0ToInstrumentedTierOptimized: - nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; - break; - default: - _ASSERT("Unknown TieredPGO_Strategy"); + UNREACHABLE_MSG("Unknown TieredPGO_Strategy"); } } } +#endif ILCodeVersion ilCodeVersion = tier0NativeCodeVersion.GetILCodeVersion(); _ASSERTE(!ilCodeVersion.HasAnyOptimizedNativeCodeVersion(tier0NativeCodeVersion)); From 75da822c4f9c852919fa53c720ee66fa96201678 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 6 Aug 2022 22:55:36 +0200 Subject: [PATCH 30/53] Apply suggestions from code review Co-authored-by: Koundinya Veluri --- src/coreclr/vm/codeversion.cpp | 10 ++++------ src/coreclr/vm/eeconfig.h | 4 ++-- src/coreclr/vm/tieredcompilation.cpp | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp index ac3a50787a3ce1..9bc971033915e0 100644 --- a/src/coreclr/vm/codeversion.cpp +++ b/src/coreclr/vm/codeversion.cpp @@ -152,12 +152,10 @@ void NativeCodeVersionNode::SetOptimizationTier(NativeCodeVersion::OptimizationT { LIMITED_METHOD_CONTRACT; - if (tier != m_optTier && - (m_optTier == NativeCodeVersion::OptimizationTier::OptimizationTier1 || - m_optTier == NativeCodeVersion::OptimizationTier::OptimizationTierOptimized)) - { - _ASSERTE("Unexpected deoptimization"); - } + _ASSERTE( + tier == m_optTier || + (m_optTier != NativeCodeVersion::OptimizationTier::OptimizationTier1 && + m_optTier != NativeCodeVersion::OptimizationTier::OptimizationTierOptimized)); m_optTier = tier; } diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index 990073c41095c7..eb5fae783fc956 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -54,8 +54,8 @@ enum ParseCtl { // Keep in sync with clrconfigvalues.h, see TieredPGO_Strategy enum TieredPGOStrategy { - // Use InstrumentedTier for new code without R2R (or if it's disabled), R2R won't instrumented - UseInstrumentedTierForILOnly = 0, // Default behavior + // Use InstrumentedTier for new code without R2R (or if it's disabled), R2R won't be instrumented + UseInstrumentedTierForILOnly = 0, // Use InstrumentedTier for new code without R2R (or if it's disabled), hot R2R // will be promoted to an intermediate InstrumentedTier (without optimizations in it) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index cd6f3c7a5f620c..25c63abe41ffe3 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -292,7 +292,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( #ifdef FEATURE_PGO // If TieredPGO is enabled, follow TieredPGO_Strategy, see comments in clrconfigvalues.h around it - if (g_pConfig->TieredPGO() && pMethodDesc->IsEligibleForTieredCompilation()) + if (g_pConfig->TieredPGO()) { if (tier0NativeCodeVersion.IsDefaultVersion() && tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0) From e83eb68f869a3f25359cf59ce5b7c014e032bd96 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 6 Aug 2022 22:56:18 +0200 Subject: [PATCH 31/53] Remove InstrumentedTiers_strat4.csproj --- .../InstrumentedTiers_strat4.csproj | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj deleted file mode 100644 index 6830c1c6e6873f..00000000000000 --- a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat4.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - Exe - True - - - - - - - \ No newline at end of file From b1f4d2e489e0d2c2dfde1af4f12d6998fe3d754d Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 6 Aug 2022 22:58:19 +0200 Subject: [PATCH 32/53] Address feedback --- src/coreclr/vm/tieredcompilation.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index cd6f3c7a5f620c..009f61627ad1b9 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -1086,22 +1086,23 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (!methodDesc->RequestedAggressiveOptimization()) { NativeCodeVersion::OptimizationTier currentTier = nativeCodeVersion.GetOptimizationTier(); - if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumented) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); - return flags; - } - - if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumentedOptimized) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); - return flags; - } if (g_pConfig->TieredCompilation_QuickJit()) { + if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumented) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); + return flags; + } + + if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumentedOptimized) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + return flags; + } + _ASSERTE(!nativeCodeVersion.IsFinalTier()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); return flags; From 27f7228d6021681264c66d1d2013ae5c12213051 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 6 Aug 2022 23:18:54 +0200 Subject: [PATCH 33/53] update DynamicPgo-InstrumentedTiers.md --- .../features/DynamicPgo-InstrumentedTiers.md | 17 +++++++++++++---- src/coreclr/vm/eeconfig.h | 2 -- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index 26de69ab2fe450..b6bb4c399a2297 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -35,10 +35,10 @@ flowchart istrTier0Q{"TieredPGO_Strategy:
Instrument only
hot Tier0 code?"} istrTier0Q-->|No, always instrument tier0|tier0 istrTier0Q-->|Yes, only hot|tier000 - tier000["JIT to Tier0

(not optimized, not instrumented,
with patchpoints)"]-->ishot555 + tier000["JIT to Tier0

(not optimized, not instrumented,
with patchpoints)"]-->|Running...|ishot555 ishot555{"Is hot?
(called >30 times)"} ishot555-.->|No,
keep running...|ishot555 - ishot555-->|Yes|istier1inst + ishot555-->|Yes|tier0 hasR2R -->|Yes| R2R R2R["Use R2R code

(optimized, not instrumented,
with patchpoints)"] -->|Running...|ishot1 @@ -51,12 +51,21 @@ flowchart tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 tier1pgo2["JIT to Tier1

(optimized with profile data)"] + tier1pgo2_1["JIT to Tier1

(optimized with profile data)"] - istier1inst{"TieredPGO_Strategy:
Enable optimizations
for InstrumentedTier?"}-->|"No"|tier0 + istier1inst{"TieredPGO_Strategy:
Enable optimizations
for InstrumentedTier?"}-->|"No"|tier0_1 istier1inst--->|"Yes"|tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
with patchpoints)"] - tier1inst-->|Running...|ishot5 + tier1inst-->|Running...|ishot5_1 ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 ishot5-.->|No,
keep running...|ishot5 + + + ishot5_1{"Is hot?
(called >30 times)"} + ishot5_1-.->|No,
keep running...|ishot5_1 + ishot5_1{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2_1 + + tier0_1["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"] + tier0_1-->|Running...|ishot5_1 ``` (_VSCode doesn't support mermaid diagrams, consider installing external add-ins_) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index eb5fae783fc956..eec51d43a2499d 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -61,8 +61,6 @@ enum TieredPGOStrategy // will be promoted to an intermediate InstrumentedTier (without optimizations in it) UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier = 1, - // NOTE: The following strategies work but aren't tested yet: - // Use InstrumentedTier for new code without R2R (or if it's disabled), hot R2R // will be promoted to an intermediate InstrumentedTierOptimized (without optimizations in it) UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized = 2, From 2e478c0c2a7e9d18e02abeddf24822b510ec246a Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 00:11:56 +0200 Subject: [PATCH 34/53] Clean up --- src/coreclr/inc/clrconfigvalues.h | 20 +++++++++------- src/coreclr/vm/eeconfig.cpp | 4 ++-- src/coreclr/vm/eeconfig.h | 24 +++++++++---------- src/coreclr/vm/tieredcompilation.cpp | 36 +++++++++++++++++++++------- 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index bf95a7a1eb8e28..aaba3fa6947a27 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -615,19 +615,21 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // TieredPGO_Strategy values: // -// 0 - [Default] Use InstrumentedTier for non-R2R code as the initial tier -// 1 - Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTier -// 2 - [Not tested] Use InstrumentedTier for non-R2R code as the initial tier, promote hot R2R to InstrumentedTierOptimized -// 3 - Promote hot Tier0/R2R to InstrumentedTier +// 0) Instrument any non-prejitted code +// 1) Instrument any non-prejitted code and only hot R2R code +// 2) Instrument any non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) +// 3) Instrument only hot non-prejitted code and only hot R2R code +// 4) Instrument only hot non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) // -// Pros & cons of using optimizations inside the instrumented tiers (mode '2') +// +// Pros & cons of using optimizations inside the instrumented tiers (mode '2' and '4') // Pros: -// * Lower overhead from instrumentation (and thanks to optimizations we _can_ optimize probes and emit less of those) -// * Optimized code is able to inline methods so we won't be producing new Compilation units for even small methods +// * Lower overhead from instrumentation (and thanks to optimizations we _can_ optimize probes and emit less of those) +// * Optimized code is able to inline methods so we won't be producing new Compilation units for even small methods // // Cons: -// * Currently, we won't instrument inlinees -> we'll probably miss a lot of oportunities and produce less accurate profile -// leading to a less optimized final tier +// * Currently, we won't instrument inlinees -> we'll probably miss a lot of oportunities and produce less accurate profile +// leading to a less optimized final tier // RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 0, "Strategy for TieredPGO, see comments in clrconfigvalues.h") #endif diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 612c15c7cb3879..9eef84e63567ea 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -711,8 +711,8 @@ HRESULT EEConfig::sync() } else { - if (tieredPGO_Strategy == UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier || - tieredPGO_Strategy == PromoteHotTier0ToInstrumentedTier) + if (tieredPGO_Strategy == InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode || + tieredPGO_Strategy == InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode) { // When we're not using optimizations in the instrumented tiers we produce a lot of new first-time compilation // due to disabled inlining even for very small methods - such first-time compilations delay promotions by diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index eec51d43a2499d..d1b98f52eff81c 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -51,23 +51,23 @@ enum ParseCtl { stopAfterRuntimeSection // stop after ... section }; -// Keep in sync with clrconfigvalues.h, see TieredPGO_Strategy +// Keep in sync with comments in clrconfigvalues.h around "TieredPGO_Strategy" enum TieredPGOStrategy { - // Use InstrumentedTier for new code without R2R (or if it's disabled), R2R won't be instrumented - UseInstrumentedTierForILOnly = 0, + // Instrument any non-prejitted code + InstrumentColdNonPrejittedCode = 0, - // Use InstrumentedTier for new code without R2R (or if it's disabled), hot R2R - // will be promoted to an intermediate InstrumentedTier (without optimizations in it) - UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier = 1, + // Instrument any non-prejitted code and only hot R2R code + InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode = 1, - // Use InstrumentedTier for new code without R2R (or if it's disabled), hot R2R - // will be promoted to an intermediate InstrumentedTierOptimized (without optimizations in it) - UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized = 2, + // Instrument any non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) + InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized = 2, - // In these modes we never instrument Tier0 and only promote hot Tier0 and R2R - // code to intermediate tiers with instrumentation - PromoteHotTier0ToInstrumentedTier = 3, + // Instrument only hot non-prejitted code and only hot R2R code + InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode = 3, + + // Instrument only hot non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) + InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode_Optimized = 4 }; class EEConfig diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 27c0f281128563..c144678fcd2aa8 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -117,9 +117,11 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza { switch (g_pConfig->TieredPGO_Strategy()) { - case UseInstrumentedTierForILOnly: - case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier: - case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized: + // Use OptimizationTierInstrumented for non-prejitted code as the initial tier + // and OptimizationTier0 for R2R'd + case InstrumentColdNonPrejittedCode: + case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode: + case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: { CodeVersionManager::LockHolder codeVersioningLockHolder; NativeCodeVersion version = pMethodDesc->GetCodeVersionManager()->GetActiveILCodeVersion(pMethodDesc) @@ -130,8 +132,14 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza } return NativeCodeVersion::OptimizationTierInstrumented; } - default: + + // These never start with OptimizationTierInstrumented + case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode: + case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: return NativeCodeVersion::OptimizationTier0; + + default: + UNREACHABLE_MSG("Unknown TieredPGO_Strategy"); } } #endif @@ -300,12 +308,12 @@ void TieredCompilationManager::AsyncPromoteToTier1( switch (g_pConfig->TieredPGO_Strategy()) { // 0: Always use TierInstrumented for new ILOnly code - case UseInstrumentedTierForILOnly: + case InstrumentColdNonPrejittedCode: nextTier = NativeCodeVersion::OptimizationTierInstrumented; break; // 1: Promote hot R2R code to TierInstrumented - case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTier: + case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode: if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) { nextTier = NativeCodeVersion::OptimizationTierInstrumented; @@ -313,7 +321,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( break; // 2: Promote hot R2R code to TierInstrumentedOptimized - case UseInstrumentedTierForILOnly_PromoteHotR2RToInstrumentedTierOptimized: + case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) { nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; @@ -321,10 +329,22 @@ void TieredCompilationManager::AsyncPromoteToTier1( break; // 3: Promote hot Tier0/R2R code to TierInstrumented - case PromoteHotTier0ToInstrumentedTier: + case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode: nextTier = NativeCodeVersion::OptimizationTierInstrumented; break; + // 4: Promote hot Tier0 to TierInstrumented and hot R2R to TierInstrumentedOptimized + case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: + if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + { + nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; + } + else + { + nextTier = NativeCodeVersion::OptimizationTierInstrumented; + } + break; + default: UNREACHABLE_MSG("Unknown TieredPGO_Strategy"); } From 557ec0c08656edce2a02994275b32560fae41dcf Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 00:15:44 +0200 Subject: [PATCH 35/53] Clean up --- src/coreclr/vm/tieredcompilation.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index c144678fcd2aa8..d99881db728928 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -1147,14 +1147,24 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) switch (nativeCodeVersion.GetOptimizationTier()) { case NativeCodeVersion::OptimizationTierInstrumented: - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); - break; + if (g_pConfig->TieredCompilation_QuickJit()) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); + break; + } + nativeCodeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); + goto Optimized; case NativeCodeVersion::OptimizationTierInstrumentedOptimized: - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); - break; + if (g_pConfig->TieredCompilation_QuickJit()) + { + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + break; + } + nativeCodeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); + goto Optimized; case NativeCodeVersion::OptimizationTier0: if (g_pConfig->TieredCompilation_QuickJit()) @@ -1162,7 +1172,6 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); break; } - nativeCodeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); goto Optimized; From 48cf9455c0c53db31b6d1585e9511272391ef19c Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 00:19:18 +0200 Subject: [PATCH 36/53] clean up --- src/coreclr/vm/tieredcompilation.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index d99881db728928..9b381609dc6fc0 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -1147,24 +1147,16 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) switch (nativeCodeVersion.GetOptimizationTier()) { case NativeCodeVersion::OptimizationTierInstrumented: - if (g_pConfig->TieredCompilation_QuickJit()) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); - break; - } - nativeCodeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); - goto Optimized; + _ASSERT(g_pConfig->TieredCompilation_QuickJit()); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); + break; case NativeCodeVersion::OptimizationTierInstrumentedOptimized: - if (g_pConfig->TieredCompilation_QuickJit()) - { - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); - flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); - break; - } - nativeCodeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTierOptimized); - goto Optimized; + _ASSERT(g_pConfig->TieredCompilation_QuickJit()); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); + break; case NativeCodeVersion::OptimizationTier0: if (g_pConfig->TieredCompilation_QuickJit()) From ca491a0c5ebaedb4728a28e6ba68caa3a48d03e0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 00:55:05 +0200 Subject: [PATCH 37/53] Rename arg in AsyncPromoteToTier1 --- src/coreclr/vm/tieredcompilation.cpp | 22 +++++++++++----------- src/coreclr/vm/tieredcompilation.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 9b381609dc6fc0..8a43672dc0c5f5 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -270,7 +270,7 @@ bool TieredCompilationManager::TrySetCodeEntryPointAndRecordMethodForCallCountin } void TieredCompilationManager::AsyncPromoteToTier1( - NativeCodeVersion tier0NativeCodeVersion, + NativeCodeVersion currentNativeCodeVersion, bool *createTieringBackgroundWorkerRef) { CONTRACTL @@ -282,8 +282,8 @@ void TieredCompilationManager::AsyncPromoteToTier1( CONTRACTL_END; _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread()); - _ASSERTE(!tier0NativeCodeVersion.IsNull()); - _ASSERTE(!tier0NativeCodeVersion.IsFinalTier()); + _ASSERTE(!currentNativeCodeVersion.IsNull()); + _ASSERTE(!currentNativeCodeVersion.IsFinalTier()); _ASSERTE(createTieringBackgroundWorkerRef != nullptr); NativeCodeVersion t1NativeCodeVersion; @@ -294,7 +294,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( // particular version of the IL code regardless of any changes that may // occur between now and when jitting completes. If the IL does change in that // interval the new code entry won't be activated. - MethodDesc *pMethodDesc = tier0NativeCodeVersion.GetMethodDesc(); + MethodDesc *pMethodDesc = currentNativeCodeVersion.GetMethodDesc(); NativeCodeVersion::OptimizationTier nextTier = NativeCodeVersion::OptimizationTier1; @@ -302,8 +302,8 @@ void TieredCompilationManager::AsyncPromoteToTier1( // If TieredPGO is enabled, follow TieredPGO_Strategy, see comments in clrconfigvalues.h around it if (g_pConfig->TieredPGO()) { - if (tier0NativeCodeVersion.IsDefaultVersion() && - tier0NativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0) + if (currentNativeCodeVersion.IsDefaultVersion() && + currentNativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0) { switch (g_pConfig->TieredPGO_Strategy()) { @@ -314,7 +314,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( // 1: Promote hot R2R code to TierInstrumented case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode: - if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) { nextTier = NativeCodeVersion::OptimizationTierInstrumented; } @@ -322,7 +322,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( // 2: Promote hot R2R code to TierInstrumentedOptimized case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: - if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) { nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; } @@ -335,7 +335,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( // 4: Promote hot Tier0 to TierInstrumented and hot R2R to TierInstrumentedOptimized case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: - if (ExecutionManager::IsReadyToRunCode(tier0NativeCodeVersion.GetNativeCode())) + if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) { nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; } @@ -352,8 +352,8 @@ void TieredCompilationManager::AsyncPromoteToTier1( } #endif - ILCodeVersion ilCodeVersion = tier0NativeCodeVersion.GetILCodeVersion(); - _ASSERTE(!ilCodeVersion.HasAnyOptimizedNativeCodeVersion(tier0NativeCodeVersion)); + ILCodeVersion ilCodeVersion = currentNativeCodeVersion.GetILCodeVersion(); + _ASSERTE(!ilCodeVersion.HasAnyOptimizedNativeCodeVersion(currentNativeCodeVersion)); hr = ilCodeVersion.AddNativeCodeVersion(pMethodDesc, nextTier, &t1NativeCodeVersion); if (FAILED(hr)) { diff --git a/src/coreclr/vm/tieredcompilation.h b/src/coreclr/vm/tieredcompilation.h index 4ad624a3f7f1c2..bf078dbc2979e8 100644 --- a/src/coreclr/vm/tieredcompilation.h +++ b/src/coreclr/vm/tieredcompilation.h @@ -42,7 +42,7 @@ class TieredCompilationManager public: void HandleCallCountingForFirstCall(MethodDesc* pMethodDesc); bool TrySetCodeEntryPointAndRecordMethodForCallCounting(MethodDesc* pMethodDesc, PCODE codeEntryPoint); - void AsyncPromoteToTier1(NativeCodeVersion tier0NativeCodeVersion, bool *createTieringBackgroundWorkerRef); + void AsyncPromoteToTier1(NativeCodeVersion currentNativeCodeVersion, bool *createTieringBackgroundWorkerRef); static CORJIT_FLAGS GetJitFlags(PrepareCodeConfig *config); #if !defined(DACCESS_COMPILE) && defined(_DEBUG) From 25bddf0e9b318d94f89843909992a11f6dabdac8 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 01:09:11 +0200 Subject: [PATCH 38/53] Address feedback --- src/coreclr/vm/tieredcompilation.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 8a43672dc0c5f5..37eceb000a5f44 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -307,9 +307,8 @@ void TieredCompilationManager::AsyncPromoteToTier1( { switch (g_pConfig->TieredPGO_Strategy()) { - // 0: Always use TierInstrumented for new ILOnly code + // 0: Since previous tier is not instrumented we just promote to Tier1 without any Dynamic PGO case InstrumentColdNonPrejittedCode: - nextTier = NativeCodeVersion::OptimizationTierInstrumented; break; // 1: Promote hot R2R code to TierInstrumented From f777274eff29b357981b4e671d0403debff46ae0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 01:13:00 +0200 Subject: [PATCH 39/53] Address feedback --- src/coreclr/vm/tieredcompilation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 37eceb000a5f44..d73caa812194a6 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -307,8 +307,9 @@ void TieredCompilationManager::AsyncPromoteToTier1( { switch (g_pConfig->TieredPGO_Strategy()) { - // 0: Since previous tier is not instrumented we just promote to Tier1 without any Dynamic PGO + // 0: In this mode previous tier is OptimizationTier0 only in case of R2R case InstrumentColdNonPrejittedCode: + _ASSERT(ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())); break; // 1: Promote hot R2R code to TierInstrumented From 13e12114b0e91c3f269cd64b26b4638aa1311881 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 02:25:05 +0200 Subject: [PATCH 40/53] Address feedback --- src/coreclr/vm/prestub.cpp | 5 ++++- src/coreclr/vm/tieredcompilation.cpp | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 53a6a85bdb4c18..75b0b5008beb82 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1301,7 +1301,10 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() // native code version of a method. The current optimization tier should be consistent with the change being made // (Tier 0 to Optimized), such that the tier is not changed in an unexpected way or at an unexpected time. Since changes // to the optimization tier are unlocked, this assertion is just a speculative check on possible values. - _ASSERTE(!GetCodeVersion().IsFinalTier()); + NativeCodeVersion::OptimizationTier previousOptimizationTier = GetCodeVersion().GetOptimizationTier(); + _ASSERTE( + previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || + previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); #endif // _DEBUG // Update the tier in the code version. The JIT may have decided to switch from tier 0 to optimized, in which case diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index d73caa812194a6..50bedb95499ae5 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -123,10 +123,7 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode: case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: { - CodeVersionManager::LockHolder codeVersioningLockHolder; - NativeCodeVersion version = pMethodDesc->GetCodeVersionManager()->GetActiveILCodeVersion(pMethodDesc) - .GetActiveNativeCodeVersion(pMethodDesc); - if (!version.IsNull() && ExecutionManager::IsReadyToRunCode(version.GetNativeCode())) + if (ExecutionManager::IsReadyToRunCode(pMethodDesc->GetNativeCode())) { return NativeCodeVersion::OptimizationTier0; } From 3d882524980154a31e4dd06fbfed518ee6c4bb64 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Aug 2022 13:00:04 +0200 Subject: [PATCH 41/53] Fix issues found during testing --- src/coreclr/vm/eeconfig.cpp | 48 ++++++++++++++++++------------------- src/coreclr/vm/prestub.cpp | 1 + 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 9eef84e63567ea..9b04dfc579a396 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -700,30 +700,6 @@ HRESULT EEConfig::sync() dwSleepOnExit = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_SleepOnExit); -#if defined(FEATURE_PGO) - fTieredPGO = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredPGO"), CLRConfig::EXTERNAL_TieredPGO); - tieredPGO_Strategy = (TieredPGOStrategy)CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_Strategy); - - // We need quick jit for TieredPGO - if (!fTieredCompilation_QuickJit) - { - fTieredPGO = false; - } - else - { - if (tieredPGO_Strategy == InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode || - tieredPGO_Strategy == InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode) - { - // When we're not using optimizations in the instrumented tiers we produce a lot of new first-time compilation - // due to disabled inlining even for very small methods - such first-time compilations delay promotions by - // tieredCompilation_CallCountingDelayMs - tieredCompilation_CallCountingDelayMs /= 3; - tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); - } - _ASSERTE(tieredPGO_Strategy >= 0 && tieredPGO_Strategy <= 4); - } -#endif - #if defined(FEATURE_TIERED_COMPILATION) fTieredCompilation = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredCompilation"), CLRConfig::EXTERNAL_TieredCompilation); if (fTieredCompilation) @@ -805,6 +781,30 @@ HRESULT EEConfig::sync() } #endif +#if defined(FEATURE_PGO) + fTieredPGO = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredPGO"), CLRConfig::EXTERNAL_TieredPGO); + tieredPGO_Strategy = (TieredPGOStrategy)CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_Strategy); + + // We need quick jit for TieredPGO + if (!fTieredCompilation_QuickJit) + { + fTieredPGO = false; + } + else + { + if (tieredPGO_Strategy == InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode || + tieredPGO_Strategy == InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode) + { + // When we're not using optimizations in the instrumented tiers we produce a lot of new first-time compilation + // due to disabled inlining even for very small methods - such first-time compilations delay promotions by + // tieredCompilation_CallCountingDelayMs + tieredCompilation_CallCountingDelayMs /= 3; + tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); + } + _ASSERTE(tieredPGO_Strategy >= 0 && tieredPGO_Strategy <= 4); + } +#endif + #if defined(FEATURE_ON_STACK_REPLACEMENT) dwOSR_HitLimit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_HitLimit); dwOSR_CounterBump = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_CounterBump); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 75b0b5008beb82..4d1f78c91e51f4 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1304,6 +1304,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() NativeCodeVersion::OptimizationTier previousOptimizationTier = GetCodeVersion().GetOptimizationTier(); _ASSERTE( previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || + previousOptimizationTier == NativeCodeVersion::OptimizationTierInstrumented || previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); #endif // _DEBUG From df19c6890d980c52388a50fa87a4e0a214c5cac4 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 14 Oct 2022 03:47:24 +0200 Subject: [PATCH 42/53] Simplify PR, get rid of strategies --- .../features/DynamicPgo-InstrumentedTiers.md | 40 +++------ src/coreclr/inc/clrconfigvalues.h | 22 +---- src/coreclr/jit/importercalls.cpp | 11 ++- src/coreclr/vm/eeconfig.cpp | 17 +--- src/coreclr/vm/eeconfig.h | 25 +----- src/coreclr/vm/tieredcompilation.cpp | 87 +++++-------------- ...strat1.csproj => InstrumentedTiers.csproj} | 4 +- .../InstrumentedTiers_strat2.csproj | 21 ----- .../InstrumentedTiers_strat3.csproj | 21 ----- 9 files changed, 57 insertions(+), 191 deletions(-) rename src/tests/JIT/PGO/InstrumentedTiers/{InstrumentedTiers_strat1.csproj => InstrumentedTiers.csproj} (84%) delete mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj delete mode 100644 src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index b6bb4c399a2297..9cc2f592d57443 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -23,57 +23,43 @@ Yellow line provides the highest level of performance (RPS) by sacrificing start # Tiered compilation workflow in TieredPGO mode -The following diagram explains how the instrumentation for hot R2R code works under the hood when TieredPGO is enabled (it's disabled by default): +The following diagram explains how the instrumentation for hot code works under the hood when TieredPGO is enabled (it's disabled by default): ```mermaid flowchart prestub(.NET Function) -->|Compilation| hasAO{"Marked with
[AggressiveOpts]?"} hasAO-->|Yes|tier1ao["JIT to Tier1

(that attribute is extremely
rarely a good idea)"] hasAO-->|No|hasR2R - hasR2R{"Is prejitted (R2R)
and ReadyToRun==1"?} -->|No| istrTier0Q + hasR2R{"Is prejitted (R2R)
and ReadyToRun==1"?} -->|No| tier000 - istrTier0Q{"TieredPGO_Strategy:
Instrument only
hot Tier0 code?"} - istrTier0Q-->|No, always instrument tier0|tier0 - istrTier0Q-->|Yes, only hot|tier000 tier000["JIT to Tier0

(not optimized, not instrumented,
with patchpoints)"]-->|Running...|ishot555 ishot555{"Is hot?
(called >30 times)"} ishot555-.->|No,
keep running...|ishot555 ishot555-->|Yes|tier0 hasR2R -->|Yes| R2R - R2R["Use R2R code

(optimized, not instrumented,
with patchpoints)"] -->|Running...|ishot1 + R2R["Use R2R code

(optimized, not instrumented,
no patchpoints)"] -->|Running...|ishot1 ishot1{"Is hot?
(called >30 times)"}-.->|No,
keep running...|ishot1 - ishot1--->|"Yes"|instrumentR2R - - instrumentR2R{"TieredPGO_Strategy:
Instrument hot
R2R'd code?"} - instrumentR2R-->|Yes, instrument R2R'd code|istier1inst - instrumentR2R-->|No, don't instrument R2R'd code|tier1nopgo["JIT to Tier1

(no dynamic profile data)"] + ishot1--->|"Yes"|tier1inst tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 tier1pgo2["JIT to Tier1

(optimized with profile data)"] - tier1pgo2_1["JIT to Tier1

(optimized with profile data)"] - istier1inst{"TieredPGO_Strategy:
Enable optimizations
for InstrumentedTier?"}-->|"No"|tier0_1 - istier1inst--->|"Yes"|tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
with patchpoints)"] - tier1inst-->|Running...|ishot5_1 + tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
no patchpoints)"] + tier1inst-->|Running...|ishot5 ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 ishot5-.->|No,
keep running...|ishot5 - - - ishot5_1{"Is hot?
(called >30 times)"} - ishot5_1-.->|No,
keep running...|ishot5_1 - ishot5_1{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2_1 - - tier0_1["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"] - tier0_1-->|Running...|ishot5_1 ``` -(_VSCode doesn't support mermaid diagrams, consider installing external add-ins_) +(_VSCode doesn't support mermaid diagrams out of the box, consider installing external add-ins_) ## Pros & cons of using optimizations inside the instrumented tiers +Pros & cons of using optimizations inside the instrumented tiers Pros: -* Lower overhead from instrumentation (and thanks to optimizations we _can_ optimize probes and emit less of those) -* Optimized code is able to inline methods so we won't be producing new Compilation units for even small methods + * Lower overhead from instrumentation + * We definitely don't want to use unoptimized instrumented tier for R2R - it will produce a lot of new first-time compilations for non-inlined methods + and performance impact might be noticeable (when we switch from fast R2R to extremely slow Tier0+instrumentation) Cons: -* Currently, we won't instrument inlinees -> we'll probably miss a lot of opportunities and produce less accurate profile leading to a less optimized final tier \ No newline at end of file + * Currently, we don't instrument inlinees -> we'll probably miss some oportunities and produce less accurate profile leading to a less optimized final tier + * Non-optimized instrumented tier is faster to prepare for use \ No newline at end of file diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index c748739bbf1ca6..7624a43b5f5aa4 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -607,25 +607,11 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO da RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") -// TieredPGO_Strategy values: +// TieredPGO_InstrumentOnlyHotCode values: // -// 0) Instrument any non-prejitted code -// 1) Instrument any non-prejitted code and only hot R2R code -// 2) Instrument any non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) -// 3) Instrument only hot non-prejitted code and only hot R2R code -// 4) Instrument only hot non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) -// -// -// Pros & cons of using optimizations inside the instrumented tiers (mode '2' and '4') -// Pros: -// * Lower overhead from instrumentation (and thanks to optimizations we _can_ optimize probes and emit less of those) -// * Optimized code is able to inline methods so we won't be producing new Compilation units for even small methods -// -// Cons: -// * Currently, we won't instrument inlinees -> we'll probably miss a lot of oportunities and produce less accurate profile -// leading to a less optimized final tier -// -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_Strategy, W("TieredPGO_Strategy"), 0, "Strategy for TieredPGO, see comments in clrconfigvalues.h") +// 0) Instrument all IL-only code, R2R'd code is never instrumented +// 1) Instrument only hot IL-only and hot R2R code (use optimizations in the instrumented tier for hot R2R and no optimizations for hot IL-only) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_InstrumentOnlyHotCode, W("TieredPGO_InstrumentOnlyHotCode"), 0, "Strategy for TieredPGO, see comments in clrconfigvalues.h") #endif /// diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 2d4907c2d7ce5a..91bf493e83bb95 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -218,6 +218,15 @@ var_types Compiler::impImportCall(OPCODE opcode, { return impImportJitTestLabelMark(sig->numArgs); } + + // static ulong JitHelpers_JitFlags() => 0; + // can be defined anywhere and will be replaced by Debug-version of RyuJIT + if ((mflags & CORINFO_FLG_STATIC) && (sig->numArgs == 0) && (sig->retType == CorInfoType::CORINFO_TYPE_ULONG) && + (strcmp("JitHelpers_JitFlags", eeGetMethodName(methHnd, nullptr)) == 0)) + { + call = gtNewLconNode((__int64)opts.jitFlags->GetRawFlags()); + goto DONE_CALL; + } #endif // DEBUG const bool isIntrinsic = (mflags & CORINFO_FLG_INTRINSIC) != 0; @@ -6290,7 +6299,7 @@ bool Compiler::impConsiderCallProbe(GenTreeCall* call, IL_OFFSET ilOffset) return false; } - assert(opts.OptimizationDisabled() || opts.IsOSR()); + assert(opts.OptimizationDisabled() || opts.IsInstrumentedOptimized()); assert(!compIsForInlining()); // During importation, optionally flag this block as one that diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 9b04dfc579a396..84ba02b5b2469a 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -239,7 +239,7 @@ HRESULT EEConfig::Init() #if defined(FEATURE_PGO) fTieredPGO = false; - tieredPGO_Strategy = (TieredPGOStrategy)0; + tieredPGO_InstrumentOnlyHotCode = false; #endif #if defined(FEATURE_READYTORUN) @@ -783,26 +783,13 @@ HRESULT EEConfig::sync() #if defined(FEATURE_PGO) fTieredPGO = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredPGO"), CLRConfig::EXTERNAL_TieredPGO); - tieredPGO_Strategy = (TieredPGOStrategy)CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_Strategy); + tieredPGO_InstrumentOnlyHotCode = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_InstrumentOnlyHotCode) == 1; // We need quick jit for TieredPGO if (!fTieredCompilation_QuickJit) { fTieredPGO = false; } - else - { - if (tieredPGO_Strategy == InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode || - tieredPGO_Strategy == InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode) - { - // When we're not using optimizations in the instrumented tiers we produce a lot of new first-time compilation - // due to disabled inlining even for very small methods - such first-time compilations delay promotions by - // tieredCompilation_CallCountingDelayMs - tieredCompilation_CallCountingDelayMs /= 3; - tieredCompilation_CallCountingDelayMs = max(1, tieredCompilation_CallCountingDelayMs); - } - _ASSERTE(tieredPGO_Strategy >= 0 && tieredPGO_Strategy <= 4); - } #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index d1b98f52eff81c..684f04181fa3b9 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -51,25 +51,6 @@ enum ParseCtl { stopAfterRuntimeSection // stop after ... section }; -// Keep in sync with comments in clrconfigvalues.h around "TieredPGO_Strategy" -enum TieredPGOStrategy -{ - // Instrument any non-prejitted code - InstrumentColdNonPrejittedCode = 0, - - // Instrument any non-prejitted code and only hot R2R code - InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode = 1, - - // Instrument any non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) - InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized = 2, - - // Instrument only hot non-prejitted code and only hot R2R code - InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode = 3, - - // Instrument only hot non-prejitted code and only hot R2R code (use optimizations in the instrumented tier for hot R2R) - InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode_Optimized = 4 -}; - class EEConfig { public: @@ -110,8 +91,8 @@ class EEConfig #endif #if defined(FEATURE_PGO) - bool TieredPGO(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO; } - TieredPGOStrategy TieredPGO_Strategy(void) const { LIMITED_METHOD_CONTRACT; return tieredPGO_Strategy; } + bool TieredPGO(void) const { LIMITED_METHOD_CONTRACT; return fTieredPGO; } + bool TieredPGO_InstrumentOnlyHotCode(void) const { LIMITED_METHOD_CONTRACT; return tieredPGO_InstrumentOnlyHotCode; } #endif #if defined(FEATURE_READYTORUN) @@ -678,7 +659,7 @@ class EEConfig #if defined(FEATURE_PGO) bool fTieredPGO; - TieredPGOStrategy tieredPGO_Strategy; + bool tieredPGO_InstrumentOnlyHotCode; #endif #if defined(FEATURE_READYTORUN) diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 890d7da4f8c4f5..9ed15f4707048d 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -114,29 +114,16 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza #ifdef FEATURE_PGO if (g_pConfig->TieredPGO()) { - switch (g_pConfig->TieredPGO_Strategy()) + // Initial tier for R2R is always just OptimizationTier0 + // For ILOnly it depends on TieredPGO_InstrumentOnlyHotCode: + // 1 - OptimizationTier0 as we don't want to instrument the initial version (will only instrument hot Tier0) + // 2 - OptimizationTierInstrumented - instrument all ILOnly code + if (g_pConfig->TieredPGO_InstrumentOnlyHotCode() || + ExecutionManager::IsReadyToRunCode(pMethodDesc->GetNativeCode())) { - // Use OptimizationTierInstrumented for non-prejitted code as the initial tier - // and OptimizationTier0 for R2R'd - case InstrumentColdNonPrejittedCode: - case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode: - case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: - { - if (ExecutionManager::IsReadyToRunCode(pMethodDesc->GetNativeCode())) - { - return NativeCodeVersion::OptimizationTier0; - } - return NativeCodeVersion::OptimizationTierInstrumented; - } - - // These never start with OptimizationTierInstrumented - case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode: - case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: - return NativeCodeVersion::OptimizationTier0; - - default: - UNREACHABLE_MSG("Unknown TieredPGO_Strategy"); + return NativeCodeVersion::OptimizationTier0; } + return NativeCodeVersion::OptimizationTierInstrumented; } #endif @@ -295,54 +282,26 @@ void TieredCompilationManager::AsyncPromoteToTier1( NativeCodeVersion::OptimizationTier nextTier = NativeCodeVersion::OptimizationTier1; #ifdef FEATURE_PGO - // If TieredPGO is enabled, follow TieredPGO_Strategy, see comments in clrconfigvalues.h around it if (g_pConfig->TieredPGO()) { if (currentNativeCodeVersion.IsDefaultVersion() && - currentNativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0) + currentNativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && + g_pConfig->TieredPGO_InstrumentOnlyHotCode()) { - switch (g_pConfig->TieredPGO_Strategy()) + if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) { - // 0: In this mode previous tier is OptimizationTier0 only in case of R2R - case InstrumentColdNonPrejittedCode: - _ASSERT(ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())); - break; - - // 1: Promote hot R2R code to TierInstrumented - case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode: - if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) - { - nextTier = NativeCodeVersion::OptimizationTierInstrumented; - } - break; - - // 2: Promote hot R2R code to TierInstrumentedOptimized - case InstrumentColdNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: - if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) - { - nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; - } - break; - - // 3: Promote hot Tier0/R2R code to TierInstrumented - case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode: - nextTier = NativeCodeVersion::OptimizationTierInstrumented; - break; - - // 4: Promote hot Tier0 to TierInstrumented and hot R2R to TierInstrumentedOptimized - case InstrumentHotNonPrejittedCode_InstrumentHotPrejittedCode_Optimized: - if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) - { - nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; - } - else - { - nextTier = NativeCodeVersion::OptimizationTierInstrumented; - } - break; - - default: - UNREACHABLE_MSG("Unknown TieredPGO_Strategy"); + // We definitely don't want to use unoptimized instrumentation tier for hot R2R: + // 1) It will produce a lot of new compilations for small methods which were inlined in R2R + // 2) Noticeable performance regression from fast R2R to slow instrumented Tier0 + nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; + } + else + { + // For ILOnly it's fine to use unoptimized instrumented tier: + // 1) No new compilations since previous tier already triggered them + // 2) Better profile since we'll be able to instrument inlinees + // 3) Unoptimized instrumented tier is faster to produce and wire up + nextTier = NativeCodeVersion::OptimizationTierInstrumented; } } } diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat1.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers.csproj similarity index 84% rename from src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat1.csproj rename to src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers.csproj index 8d85f1a8ae5a49..0d7ec3d6e64703 100644 --- a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat1.csproj +++ b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers.csproj @@ -6,13 +6,13 @@ $(CLRTestBatchPreCommands) set DOTNET_TieredCompilation=1 set DOTNET_TieredPGO=1 - set TieredPGO_Strategy=1 + set DOTNET_TieredPGO_InstrumentOnlyHotCode=1 ]]> diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj deleted file mode 100644 index 6e797ba5b97714..00000000000000 --- a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat2.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - Exe - True - - - - - - - \ No newline at end of file diff --git a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj b/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj deleted file mode 100644 index 8b44f43d986597..00000000000000 --- a/src/tests/JIT/PGO/InstrumentedTiers/InstrumentedTiers_strat3.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - Exe - True - - - - - - - \ No newline at end of file From f46679caf01eeeaf48ba32a223a3b0c40e1b6b97 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 14 Oct 2022 15:44:27 +0200 Subject: [PATCH 43/53] Enable TieredPGO_InstrumentOnlyHotCode by default --- src/coreclr/inc/clrconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 7624a43b5f5aa4..02c0d8487cfd4a 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -611,7 +611,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier // // 0) Instrument all IL-only code, R2R'd code is never instrumented // 1) Instrument only hot IL-only and hot R2R code (use optimizations in the instrumented tier for hot R2R and no optimizations for hot IL-only) -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_InstrumentOnlyHotCode, W("TieredPGO_InstrumentOnlyHotCode"), 0, "Strategy for TieredPGO, see comments in clrconfigvalues.h") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TieredPGO_InstrumentOnlyHotCode, W("TieredPGO_InstrumentOnlyHotCode"), 1, "Strategy for TieredPGO, see comments in clrconfigvalues.h") #endif /// From c9bd079a171dd7aeaa676d2fa060291e1383d160 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 18 Oct 2022 02:51:16 +0200 Subject: [PATCH 44/53] Fix an assert in optimized instrumented tier --- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/importercalls.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 5af1161f9f20f0..04456d4fb25930 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -4095,7 +4095,7 @@ const char* Compiler::compGetTieringName(bool wantShortName) const } else if (tier1) { - if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_OSR)) + if (opts.IsOSR()) { return instrumenting ? "Instrumented Tier1-OSR" : "Tier1-OSR"; } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index c11a627f35c4c1..f5c334bcb1cff0 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1297,7 +1297,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // have to check for anything that might introduce a recursive tail call. // * We only instrument root method blocks in OSR methods, // - if (opts.IsOSR() && !compIsForInlining()) + if (opts.IsInstrumentedOptimized() && !compIsForInlining()) { // If a root method tail call candidate block is not a BBJ_RETURN, it should have a unique // BBJ_RETURN successor. Mark that successor so we can handle it specially during profile @@ -1319,14 +1319,16 @@ var_types Compiler::impImportCall(OPCODE opcode, const bool mustImportEntryBlock = gtIsRecursiveCall(methHnd) || actualCall->IsInlineCandidate() || actualCall->IsGuardedDevirtualizationCandidate(); + BasicBlock* entryBb = opts.IsOSR() ? fgEntryBB : fgFirstBB; + // Only schedule importation if we're not currently importing. // - if (mustImportEntryBlock && (compCurBB != fgEntryBB)) + if (opts.IsInstrumentedOptimized() && mustImportEntryBlock && (compCurBB != entryBb)) { - JITDUMP("\nOSR: inlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB + JITDUMP("\ninlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB " for importation\n", - dspTreeID(call), fgEntryBB->bbNum); - impImportBlockPending(fgEntryBB); + dspTreeID(call), entryBb->bbNum); + impImportBlockPending(entryBb); } } } From d3f205b6db8580e1145a9837d985bb975fd35d90 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 18 Oct 2022 11:06:25 +0200 Subject: [PATCH 45/53] fix osr tests --- src/coreclr/jit/importercalls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index f5c334bcb1cff0..cbcac15b5ea0aa 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1297,7 +1297,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // have to check for anything that might introduce a recursive tail call. // * We only instrument root method blocks in OSR methods, // - if (opts.IsInstrumentedOptimized() && !compIsForInlining()) + if ((opts.IsInstrumentedOptimized() || opts.IsOSR()) && !compIsForInlining()) { // If a root method tail call candidate block is not a BBJ_RETURN, it should have a unique // BBJ_RETURN successor. Mark that successor so we can handle it specially during profile @@ -1323,7 +1323,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // Only schedule importation if we're not currently importing. // - if (opts.IsInstrumentedOptimized() && mustImportEntryBlock && (compCurBB != entryBb)) + if ((opts.IsInstrumentedOptimized() || opts.IsOSR()) && mustImportEntryBlock && (compCurBB != entryBb)) { JITDUMP("\ninlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB " for importation\n", From ef4ae584dd575dc2a914849941348ad430eb64c1 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 18 Oct 2022 17:00:50 +0200 Subject: [PATCH 46/53] Update docs --- ...go-InstrumentedTiers-ilsize-histogram1.png | Bin 0 -> 27810 bytes .../features/DynamicPgo-InstrumentedTiers.md | 669 +++++++++++++++++- 2 files changed, 637 insertions(+), 32 deletions(-) create mode 100644 docs/design/features/DynamicPgo-InstrumentedTiers-ilsize-histogram1.png diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers-ilsize-histogram1.png b/docs/design/features/DynamicPgo-InstrumentedTiers-ilsize-histogram1.png new file mode 100644 index 0000000000000000000000000000000000000000..9eb74ee70a24145ff8af9f77135ec35e1070bcc5 GIT binary patch literal 27810 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 QM95ll!ntCLcOSj{UpWJaLjV8( literal 0 HcmV?d00001 diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index 9cc2f592d57443..d73852c60ac0d3 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -1,29 +1,10 @@ # Instrumented Tiers -_Disclaimer: the functionality described in this doc is still in the preview stage and is not enabled by default even for `DOTNET_TieredPGO=1`._ +[#70941](https://github.com/dotnet/runtime/pull/70941) introduced separate tiers to instrument hot code mainly to address the following problems: +1) R2R code should still benefit from Dynamic PGO despite being not instrumented in the first place +2) Instrumentation in Tier0 should not affect startup -[#70941](https://github.com/dotnet/runtime/pull/70941) introduced new opt-in strategies for Tiered Compilation + TieredPGO mainly to address -two existing limitations of the current design: -1) R2R code never benefits from Dynamic PGO as it's not instrumented and is promoted straight to Tier1 when it's hot -2) Instrumentation in Tier0 comes with a big overhead and it's better to only instrument hot Tier0 code (whether it's ILOnly or R2R) - -A good example explaining boths problems is this TechEmpower benchmark (plaintext-plaintext): - -![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext.png) - -Legend: -* Red - `DOTNET_TieredPGO=0`, `DOTNET_ReadyToRun=1` (default) -* Black - `DOTNET_TieredPGO=1`, `DOTNET_ReadyToRun=1` -* Yellow - `DOTNET_TieredPGO=1`, `DOTNET_ReadyToRun=0` - -Yellow line provides the highest level of performance (RPS) by sacrificing start up speed (and, hence, time it takes to process the first request). It happens because the benchmark is quite simple and most of its code is already prejitted so we can only instrument it when we completely drop R2R and compile everything from scratch. It also explains why the black line (when we enable Dynamic PGO but still rely on R2R) didn't really show a lot of improvements. With the separate instrumentation tier for hot R2R we achieve "Yellow"-level of performance while maintaining the same start up speed as it was before. Also, for the mode where we have to compile a lot of code to Tier0, switching to "instrument only hot Tier0 code" strategy shows ~8% time-to-first-request reduction across all TE benchmarks. - -![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext-opt.png) -(_predicted results according to local runs of crank with custom binaries_) - -# Tiered compilation workflow in TieredPGO mode - -The following diagram explains how the instrumentation for hot code works under the hood when TieredPGO is enabled (it's disabled by default): +To address both of the problems the following workflow (when TieredPGO is enabled) was introduced: ```mermaid flowchart @@ -52,14 +33,638 @@ flowchart ``` (_VSCode doesn't support mermaid diagrams out of the box, consider installing external add-ins_) -## Pros & cons of using optimizations inside the instrumented tiers +It's easier to explain this on a concrete example: + +```csharp +class Program : IDisposable +{ + static int Main() + { + Program p = new(); + for (int i = 0; i < 500; i++) + { + HotLoop(p); + Thread.Sleep(40); // cold loop + } + + Console.ReadKey(); + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void HotLoop(IDisposable d) + { + for (int i = 0; i < 500000; i++) // hot loop + d?.Dispose(); + } + + public void Dispose() => Test(); + + [MethodImpl(MethodImplOptions.NoInlining)] + void Test() { } +} +``` + +The method we'll be looking at is `HotLoop`. The method itself has a hot loop (to show how this work interacts with OSR) but the whole method is expected to be promoted to Tier1 too since it's invoked also in a loop (cold loop). The method also has a virtual call to showcase GDV. + +# Case 1: Program is prejitted (R2R) + +Let's see what happens with this program when it's prejitted: + +1) When we start the app, vm picks up R2R'd version of `HotLoop` that looks like this: + +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; ReadyToRun compilation +; optimized code +; rsp based frame +; fully interruptible +; No PGO data +G_M43040_IG01: ;; offset=0000H + 57 push rdi + 56 push rsi + 4883EC28 sub rsp, 40 + 488BF1 mov rsi, rcx + ;; size=9 bbWeight=1 PerfScore 2.50 +G_M43040_IG02: ;; offset=0009H + 33FF xor edi, edi + ;; size=2 bbWeight=1 PerfScore 0.25 +G_M43040_IG03: ;; offset=000BH + 4885F6 test rsi, rsi + 740D je SHORT G_M43040_IG05 + ;; size=5 bbWeight=4 PerfScore 5.00 +G_M43040_IG04: ;; offset=0010H + 488BCE mov rcx, rsi + 4C8D1D00000000 lea r11, [(reloc 0x4000000000420270)] + 41FF13 call [r11]System.IDisposable:Dispose():this + ;; size=13 bbWeight=2 PerfScore 7.50 +G_M43040_IG05: ;; offset=001DH + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7CE4 jl SHORT G_M43040_IG03 + ;; size=10 bbWeight=4 PerfScore 6.00 +G_M43040_IG06: ;; offset=0027H + 4883C428 add rsp, 40 + 5E pop rsi + 5F pop rdi + C3 ret + ;; size=7 bbWeight=1 PerfScore 2.25 + +; Total bytes of code 46 +``` + +As we can see from the codegen: it's not instrumented (we never instrument R2R'd code - it would increas binary size by quite a lot), it doesn't have patchpoints for OSR (since it's already optimized) and is optimized. Technically, t can be optimized with a Static PGO but, presumably, it's a rare case in the real world so we left that virtual call here non-devirtualized. + +2) HotLoop is invoked >30 times meaning it's likely a hot method so VM "promotes" it to InstrumentedTierOptimized: +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; Tier-1 compilation +; optimized code +; instrumented for collecting profile data +; rsp based frame +; fully interruptible +; No PGO data +G_M43040_IG01: ;; offset=0000H + 57 push rdi + 56 push rsi + 4883EC28 sub rsp, 40 + 488BF1 mov rsi, rcx + ;; size=9 bbWeight=1 PerfScore 2.50 +G_M43040_IG02: ;; offset=0009H + FF05F9FE5500 inc dword ptr [(reloc 0x7ffd5edb4948)] + 33FF xor edi, edi + EB3B jmp SHORT G_M43040_IG05 + ;; size=10 bbWeight=1 PerfScore 5.25 +G_M43040_IG03: ;; offset=0013H + FF05F3FE5500 inc dword ptr [(reloc 0x7ffd5edb494c)] + 4885F6 test rsi, rsi + 7428 je SHORT G_M43040_IG04 + FF05ECFE5500 inc dword ptr [(reloc 0x7ffd5edb4950)] + 488BCE mov rcx, rsi + 48BA5849DB5EFD7F0000 mov rdx, 0x7FFD5EDB4958 + E81ACB105F call CORINFO_HELP_CLASSPROFILE32 + 488BCE mov rcx, rsi + 49BB5000595EFD7F0000 mov r11, 0x7FFD5E590050 ; code for System.IDisposable:Dispose + 41FF13 call [r11]System.IDisposable:Dispose():this + ;; size=51 bbWeight=2 PerfScore 24.50 +G_M43040_IG04: ;; offset=0046H + FF0514FF5500 inc dword ptr [(reloc 0x7ffd5edb49a0)] + FFC7 inc edi + ;; size=8 bbWeight=2 PerfScore 6.50 +G_M43040_IG05: ;; offset=004EH + FF0510FF5500 inc dword ptr [(reloc 0x7ffd5edb49a4)] + 81FF20A10700 cmp edi, 0x7A120 + 7CB7 jl SHORT G_M43040_IG03 + ;; size=14 bbWeight=8 PerfScore 34.00 +G_M43040_IG06: ;; offset=005CH + FF0506FF5500 inc dword ptr [(reloc 0x7ffd5edb49a8)] + ;; size=6 bbWeight=1 PerfScore 3.00 +G_M43040_IG07: ;; offset=0062H + 4883C428 add rsp, 40 + 5E pop rsi + 5F pop rdi + C3 ret + ;; size=7 bbWeight=1 PerfScore 2.25 +; Total bytes of code 105 +``` + +We had to instrument **optimized** code here to mitigate two issues: +1) We don't want to see a significant performance degradation (even temporarily) after fast R2R +2) Unoptimized code tends to spawn a lot of new unnecessary jit compilations because it doesn't inline code, even simple properties + +As a downside - the profile is less accurate and it doesn't instrument inlinees. + +3) Th new code version of `HotLoop` is also invoked >30 times leading to a final promotion to Tier1: +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; Tier-1 compilation +; optimized code +; optimized using profile data +; rsp based frame +; fully interruptible +; with Dynamic PGO: edge weights are invalid, and fgCalledCount is 48 +; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data +G_M43040_IG01: ;; offset=0000H + 57 push rdi + 56 push rsi + 4883EC28 sub rsp, 40 + 488BF1 mov rsi, rcx + ;; size=9 bbWeight=1 PerfScore 2.50 +G_M43040_IG02: ;; offset=0009H + 33FF xor edi, edi + 4885F6 test rsi, rsi + 7424 je SHORT G_M43040_IG05 + 48B9A023C861FD7F0000 mov rcx, 0x7FFD61C823A0 ; Program + 48390E cmp qword ptr [rsi], rcx + 7515 jne SHORT G_M43040_IG05 + ;; size=22 bbWeight=1 PerfScore 5.75 +G_M43040_IG03: ;; offset=001FH + 488BCE mov rcx, rsi + FF1550771B00 call [Program:Test():this] + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7CED jl SHORT G_M43040_IG03 + ;; size=19 bbWeight=484693.69 PerfScore 2302295.02 +G_M43040_IG04: ;; offset=0032H + EB27 jmp SHORT G_M43040_IG07 + ;; size=2 bbWeight=1 PerfScore 2.00 +G_M43040_IG05: ;; offset=0034H + 4885F6 test rsi, rsi + 7418 je SHORT G_M43040_IG06 + 48B9A023C861FD7F0000 mov rcx, 0x7FFD61C823A0 ; Program + 48390E cmp qword ptr [rsi], rcx + 751A jne SHORT G_M43040_IG08 + 488BCE mov rcx, rsi + FF1527771B00 call [Program:Test():this] + ;; size=29 bbWeight=4895.90 PerfScore 42839.09 +G_M43040_IG06: ;; offset=0051H + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7CD9 jl SHORT G_M43040_IG05 + ;; size=10 bbWeight=4895.90 PerfScore 7343.84 +G_M43040_IG07: ;; offset=005BH + 4883C428 add rsp, 40 + 5E pop rsi + 5F pop rdi + C3 ret + ;; size=7 bbWeight=0.98 PerfScore 2.20 +G_M43040_IG08: ;; offset=0062H + 488BCE mov rcx, rsi + 49BB10007E61FD7F0000 mov r11, 0x7FFD617E0010 ; code for System.IDisposable:Dispose + 41FF13 call [r11]System.IDisposable:Dispose():this + EBDD jmp SHORT G_M43040_IG06 + +; Total bytes of code 116 +``` +The codegen looks a bit bulky but if we look closer we'll see that we clonned the loop to have a fast version with a devirtualized call inside (see `G_M43040_IG03`) without guards inside. To summarize what happened with `HotLoop` we can take a look at this part of the diagram: +```mermaid +flowchart + hasR2R("...") -->|Yes| R2R + R2R["Use R2R code

(optimized, not instrumented,
no patchpoints)"] -->|Running...|ishot1 + ishot1{"Is hot?
(called >30 times)"}-.->|No,
keep running...|ishot1 + ishot1--->|"Yes"|tier1inst + tier1pgo2["JIT to Tier1

(optimized with profile data)"] + tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
no patchpoints)"] + tier1inst-->|Running...|ishot5 + ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 + ishot5-.->|No,
keep running...|ishot5 +``` + + +# Case 2: Program is not prejitted + +This case is a bit more complicated since it involves OSR. + +1) Since no R2R version exists for `HotLoop` VM has to ask JIT to compile a Tier0 version of it as fast as it can: +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; Tier-0 compilation +; MinOpts code +; rbp based frame +; fully interruptible +G_M43040_IG01: ;; offset=0000H + 55 push rbp + 4883EC70 sub rsp, 112 + 488D6C2470 lea rbp, [rsp+70H] + 33C0 xor eax, eax + 8945C4 mov dword ptr [rbp-3CH], eax + 48894D10 mov gword ptr [rbp+10H], rcx + ;; size=19 bbWeight=1 PerfScore 4.00 +G_M43040_IG02: ;; offset=0013H + 33C9 xor ecx, ecx + 894DC4 mov dword ptr [rbp-3CH], ecx + C745B8E8030000 mov dword ptr [rbp-48H], 0x3E8 + EB20 jmp SHORT G_M43040_IG05 + ;; size=14 bbWeight=1 PerfScore 4.25 +G_M43040_IG03: ;; offset=0021H + 48837D1000 cmp gword ptr [rbp+10H], 0 + 7411 je SHORT G_M43040_IG04 + 488B4D10 mov rcx, gword ptr [rbp+10H] + 49BB90027E61FD7F0000 mov r11, 0x7FFD617E0290 ; code for System.IDisposable:Dispose + 41FF13 call [r11]System.IDisposable:Dispose():this + ;; size=24 bbWeight=1 PerfScore 7.25 +G_M43040_IG04: ;; offset=0039H + 8B45C4 mov eax, dword ptr [rbp-3CH] + FFC0 inc eax + 8945C4 mov dword ptr [rbp-3CH], eax + ;; size=8 bbWeight=1 PerfScore 2.25 +G_M43040_IG05: ;; offset=0041H + 8B4DB8 mov ecx, dword ptr [rbp-48H] + FFC9 dec ecx + 894DB8 mov dword ptr [rbp-48H], ecx + 837DB800 cmp dword ptr [rbp-48H], 0 + 7F0E jg SHORT G_M43040_IG07 + ;; size=14 bbWeight=1 PerfScore 5.25 +G_M43040_IG06: ;; offset=004FH + 488D4DB8 lea rcx, [rbp-48H] + BA11000000 mov edx, 17 + E8338F045F call CORINFO_HELP_PATCHPOINT + ;; size=14 bbWeight=0.01 PerfScore 0.02 +G_M43040_IG07: ;; offset=005DH + 817DC420A10700 cmp dword ptr [rbp-3CH], 0x7A120 + 7CBB jl SHORT G_M43040_IG03 + ;; size=9 bbWeight=1 PerfScore 3.00 +G_M43040_IG08: ;; offset=0066H + 4883C470 add rsp, 112 + 5D pop rbp + C3 ret + ;; size=6 bbWeight=1 PerfScore 1.75 +; Total bytes of code 108 +``` + +The codegen is unoptimized, with patchpoints for OSR and without instrumentation (to avoid spending time on it for methods which will never make it to tier1) + +2) Its loop body triggers OSR: +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; Tier-1 compilation +; OSR variant for entry point 0x11 +; optimized code +; rsp based frame +; fully interruptible +; No PGO data +G_M43040_IG01: ;; offset=0000H + 4883EC38 sub rsp, 56 + 4889BC24A8000000 mov qword ptr [rsp+A8H], rdi + 4889B424A0000000 mov qword ptr [rsp+A0H], rsi + 488BB424C0000000 mov rsi, gword ptr [rsp+C0H] + 8B7C2474 mov edi, dword ptr [rsp+74H] + ;; size=32 bbWeight=1 PerfScore 6.25 +G_M43040_IG02: ;; offset=0020H + 81FF20A10700 cmp edi, 0x7A120 + 7D1F jge SHORT G_M43040_IG06 + ;; size=8 bbWeight=1 PerfScore 1.25 +G_M43040_IG03: ;; offset=0028H + 4885F6 test rsi, rsi + 7410 je SHORT G_M43040_IG05 + ;; size=5 bbWeight=4 PerfScore 5.00 +G_M43040_IG04: ;; offset=002DH + 488BCE mov rcx, rsi + 49BB98027E61FD7F0000 mov r11, 0x7FFD617E0298 ; code for System.IDisposable:Dispose + 41FF13 call [r11]System.IDisposable:Dispose():this + ;; size=16 bbWeight=2 PerfScore 7.00 +G_M43040_IG05: ;; offset=003DH + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7CE1 jl SHORT G_M43040_IG03 + ;; size=10 bbWeight=4 PerfScore 6.00 +G_M43040_IG06: ;; offset=0047H + 4881C4A0000000 add rsp, 160 + 5E pop rsi + 5F pop rdi + 5D pop rbp + C3 ret + ;; size=11 bbWeight=1 PerfScore 2.75 +; Total bytes of code 82 +``` + +Now the loop is faster because of optimizations but is still not instrumented/devirtualized. + +3) `HotLoop` itself is invoked > 30 times, that triggers promotion to InstrumentedTier: +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; Tier-0 compilation +; MinOpts code +; instrumented for collecting profile data +; rbp based frame +; fully interruptible +G_M43040_IG01: ;; offset=0000H + 55 push rbp + 4881EC80000000 sub rsp, 128 + 488DAC2480000000 lea rbp, [rsp+80H] + 33C0 xor eax, eax + 488945A8 mov qword ptr [rbp-58H], rax + C5D857E4 vxorps xmm4, xmm4 + C5F97F65B0 vmovdqa xmmword ptr [rbp-50H], xmm4 + 488945C0 mov qword ptr [rbp-40H], rax + 48894D10 mov gword ptr [rbp+10H], rcx + ;; size=39 bbWeight=1 PerfScore 7.33 +G_M43040_IG02: ;; offset=0027H + FF05A3846000 inc dword ptr [(reloc 0x7ffd6214fc10)] + 33C9 xor ecx, ecx + 894DC4 mov dword ptr [rbp-3CH], ecx + C745B8E8030000 mov dword ptr [rbp-48H], 0x3E8 + EB55 jmp SHORT G_M43040_IG05 + ;; size=20 bbWeight=1 PerfScore 7.25 +G_M43040_IG03: ;; offset=003BH + FF0593846000 inc dword ptr [(reloc 0x7ffd6214fc14)] + 48837D1000 cmp gword ptr [rbp+10H], 0 + 743A je SHORT G_M43040_IG04 + FF058A846000 inc dword ptr [(reloc 0x7ffd6214fc18)] + 488B4D10 mov rcx, gword ptr [rbp+10H] + 48894DB0 mov gword ptr [rbp-50H], rcx + 488B4DB0 mov rcx, gword ptr [rbp-50H] + 48BA20FC1462FD7F0000 mov rdx, 0x7FFD6214FC20 + E8E79D045F call CORINFO_HELP_CLASSPROFILE32 + 488B4DB0 mov rcx, gword ptr [rbp-50H] + 48894DA8 mov gword ptr [rbp-58H], rcx + 488B4DA8 mov rcx, gword ptr [rbp-58H] + 49BBA0027E61FD7F0000 mov r11, 0x7FFD617E02A0 ; code for System.IDisposable:Dispose + 41FF13 call [r11]System.IDisposable:Dispose():this + ;; size=71 bbWeight=1 PerfScore 19.50 +G_M43040_IG04: ;; offset=0082H + FF05A0846000 inc dword ptr [(reloc 0x7ffd6214fc68)] + 8B45C4 mov eax, dword ptr [rbp-3CH] + FFC0 inc eax + 8945C4 mov dword ptr [rbp-3CH], eax + ;; size=14 bbWeight=1 PerfScore 5.25 +G_M43040_IG05: ;; offset=0090H + 8B4DB8 mov ecx, dword ptr [rbp-48H] + FFC9 dec ecx + 894DB8 mov dword ptr [rbp-48H], ecx + 837DB800 cmp dword ptr [rbp-48H], 0 + 7F0E jg SHORT G_M43040_IG07 + ;; size=14 bbWeight=1 PerfScore 5.25 +G_M43040_IG06: ;; offset=009EH + 488D4DB8 lea rcx, [rbp-48H] + BA11000000 mov edx, 17 + E8248C045F call CORINFO_HELP_PATCHPOINT + ;; size=14 bbWeight=0.01 PerfScore 0.02 +G_M43040_IG07: ;; offset=00ACH + FF057A846000 inc dword ptr [(reloc 0x7ffd6214fc6c)] + 817DC420A10700 cmp dword ptr [rbp-3CH], 0x7A120 + 7C80 jl SHORT G_M43040_IG03 + FF056F846000 inc dword ptr [(reloc 0x7ffd6214fc70)] + ;; size=21 bbWeight=1 PerfScore 9.00 +G_M43040_IG08: ;; offset=00C1H + 4881C480000000 add rsp, 128 + 5D pop rbp + C3 ret + ;; size=9 bbWeight=1 PerfScore 1.75 +; Total bytes of code 202 +``` +Now the whole method is compiled to Tier0 with instrumentation and patchpoints. No optimizations. +We decided to promote hot Tier0 to InstrumentedTier without optimizations for the following reasons: +1) We won't notice a big performance regression from going from Tier0 to InstrumentedTier +2) InstrumentedTier is faster to compile +3) Its profile is more accurate + +Although, in this specific case we could consider using InstrumentedTierOptimized since we had a faster loop in previous code version due to Tier1-OSR but since OSR events are rare and we don't want to produce a less accurate profile that we had before https://github.com/dotnet/runtime/pull/70941 it's left as is. We might re-consider this if we improve instrumentation for the optimized code to produce more accurate profile including inlinees. + +4) The loop of `HotLoop` triggered OSR once again: +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; Tier-1 compilation +; OSR variant for entry point 0x11 +; optimized code +; optimized using profile data +; rsp based frame +; fully interruptible +; with Dynamic PGO: edge weights are invalid, and fgCalledCount is 9999 +; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data +G_M43040_IG01: ;; offset=0000H + 4883EC38 sub rsp, 56 + 4889BC24B8000000 mov qword ptr [rsp+B8H], rdi + 4889B424B0000000 mov qword ptr [rsp+B0H], rsi + 488BB424D0000000 mov rsi, gword ptr [rsp+D0H] + 8BBC2484000000 mov edi, dword ptr [rsp+84H] + ;; size=35 bbWeight=1 PerfScore 6.25 +G_M43040_IG02: ;; offset=0023H + 81FF20A10700 cmp edi, 0x7A120 + 7D50 jge SHORT G_M43040_IG06 + 4885F6 test rsi, rsi + 7424 je SHORT G_M43040_IG04 + 48B9C86CCC61FD7F0000 mov rcx, 0x7FFD61CC6CC8 ; Program + 48390E cmp qword ptr [rsi], rcx + 7515 jne SHORT G_M43040_IG04 + ;; size=28 bbWeight=1 PerfScore 6.75 +G_M43040_IG03: ;; offset=003FH + 488BCE mov rcx, rsi + FF15605A1500 call [Program:Test():this] + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7D29 jge SHORT G_M43040_IG06 + EBEB jmp SHORT G_M43040_IG03 + ;; size=21 bbWeight=0.99 PerfScore 6.68 +G_M43040_IG04: ;; offset=0054H + 4885F6 test rsi, rsi + 7418 je SHORT G_M43040_IG05 + 48B9C86CCC61FD7F0000 mov rcx, 0x7FFD61CC6CC8 ; Program + 48390E cmp qword ptr [rsi], rcx + 751E jne SHORT G_M43040_IG07 + 488BCE mov rcx, rsi + FF15375A1500 call [Program:Test():this] + ;; size=29 bbWeight=0.01 PerfScore 0.09 +G_M43040_IG05: ;; offset=0071H + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7CD9 jl SHORT G_M43040_IG04 + ;; size=10 bbWeight=0.01 PerfScore 0.02 +G_M43040_IG06: ;; offset=007BH + 4881C4B0000000 add rsp, 176 + 5E pop rsi + 5F pop rdi + 5D pop rbp + C3 ret + ;; size=11 bbWeight=0 PerfScore 0.00 +G_M43040_IG07: ;; offset=0086H + 488BCE mov rcx, rsi + 49BBA8027E61FD7F0000 mov r11, 0x7FFD617E02A8 ; code for System.IDisposable:Dispose + 41FF13 call [r11]System.IDisposable:Dispose():this + EBD9 jmp SHORT G_M43040_IG05 + ;; size=18 bbWeight=0 PerfScore 0.00 +; Total bytes of code 152 +``` +We ended up with a very fast version of the method with optimal loop `G_M43040_IG03` that calls devirtualized call each iteration without any guards. The outsides of the loop are still unoptimized Tier0 codegen. + +5) ``HotLoop` method is invoked 30 more times and triggers the final promotion to the last tier: +```asm +; Assembly listing for method Program:HotLoop(System.IDisposable) +; Emitting BLENDED_CODE for X64 CPU with AVX - Windows +; Tier-1 compilation +; optimized code +; optimized using profile data +; rsp based frame +; fully interruptible +; with Dynamic PGO: edge weights are invalid, and fgCalledCount is 48 +; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data +G_M43040_IG01: ;; offset=0000H + 57 push rdi + 56 push rsi + 4883EC28 sub rsp, 40 + 488BF1 mov rsi, rcx + ;; size=9 bbWeight=1 PerfScore 2.50 +G_M43040_IG02: ;; offset=0009H + 33FF xor edi, edi + 4885F6 test rsi, rsi + 7424 je SHORT G_M43040_IG04 + 48B9C86CCC61FD7F0000 mov rcx, 0x7FFD61CC6CC8 ; Program + 48390E cmp qword ptr [rsi], rcx + 7515 jne SHORT G_M43040_IG04 + ;; size=22 bbWeight=1 PerfScore 5.75 +G_M43040_IG03: ;; offset=001FH + 488BCE mov rcx, rsi + FF15C0591500 call [Program:Test():this] + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7D29 jge SHORT G_M43040_IG06 + EBEB jmp SHORT G_M43040_IG03 + ;; size=21 bbWeight=1158.09 PerfScore 7817.13 +G_M43040_IG04: ;; offset=0034H + 4885F6 test rsi, rsi + 7418 je SHORT G_M43040_IG05 + 48B9C86CCC61FD7F0000 mov rcx, 0x7FFD61CC6CC8 ; Program + 48390E cmp qword ptr [rsi], rcx + 751A jne SHORT G_M43040_IG07 + 488BCE mov rcx, rsi + FF1597591500 call [Program:Test():this] + ;; size=29 bbWeight=11.70 PerfScore 102.36 +G_M43040_IG05: ;; offset=0051H + FFC7 inc edi + 81FF20A10700 cmp edi, 0x7A120 + 7CD9 jl SHORT G_M43040_IG04 + ;; size=10 bbWeight=11.70 PerfScore 17.55 +G_M43040_IG06: ;; offset=005BH + 4883C428 add rsp, 40 + 5E pop rsi + 5F pop rdi + C3 ret + ;; size=7 bbWeight=0 PerfScore 0.00 +G_M43040_IG07: ;; offset=0062H + 488BCE mov rcx, rsi + 49BBB0027E61FD7F0000 mov r11, 0x7FFD617E02B0 ; code for System.IDisposable:Dispose + 41FF13 call [r11]System.IDisposable:Dispose():this + EBDD jmp SHORT G_M43040_IG05 + ;; size=18 bbWeight=0 PerfScore 0.00 +; Total bytes of code 116 +``` +Again, to summarize the workflow for non-prejitted case let's take a look at this branch of the diagram (OSR details are omitted to showcase the most common case): + +```mermaid +flowchart + hasR2R("...") -->tier000 + tier000["JIT to Tier0

(not optimized, not instrumented,
with patchpoints)"]-->|Running...|ishot555 + ishot555{"Is hot?
(called >30 times)"} + ishot555-.->|No,
keep running...|ishot555 + ishot555-->|Yes|tier0 + tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 + tier1pgo2["JIT to Tier1

(optimized with profile data)"] + ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 + ishot5-.->|No,
keep running...|ishot5 +``` + +It's worth noting that we analyzed the worst (in case of working set) case with OSR, normally (in 99% of cases) we end up only with three code versions for hot code: +1) Tier0 +2) Instrumented Tier0 +3) Tier1 + +# Impact on Working set + +The general rule of thumb that only 10-20% of methods make it to Tier1 and about to 40-60% of all methods are less than 8 bytes of IL (e.g., getters/setters) so we're effectively double the size of Tier1 with this approach (including call counting stubs, etc.) How bad it is compared to overall working set in various apps? let's consider these two examples: + +## 1) A 1st party service (build size is >100Gb) + +| Metric | Number of methods | Share, % | Total size, Mb | Share, % | +|------------------|-------------------|----------|----------------|----------| +| **Tier0** | 115862 | 59.36% | 60.06 | 83.89% | +| **Tier1** | 30942 | 15.85% | 8.22 | 11.48% | +| **FullOpts** | 48384 | 24.79% | 3.26 | 4.55% | +| **Contains OSR** | 55 | 0.03% | 0.06 | 0.08% | +| **Total jitted** | 195188 | 100.00% | 71.60 | 100.00% | + + +![IL Histogram 1](DynamicPgo-InstrumentedTiers-ilsize-histogram1.png) + +In this app Tier1 code occupies 8.22mb in the loader heap (we can add a few megabytes on top of it for call counting stubs, jump-stubs, etc.) meaning that instrumentation tier is expected to add a similar amount (~13Mb). The total working set of the service is 7.5Gb so instrumentation tier contributes less than 0.2% of that. We're adding +30k new jit compilations which we can fully compensate with https://github.com/dotnet/runtime/issues/76402 work to avoid potential problems connected with too big queues of methods pending call counting installation/promotions to tier1. + +## 2) A desktop application [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy) + +`ReadyToRun=0`: + +| Metric | Number of methods | Share, % | Total size, Mb | Share, % | +|------------------|-------------------|----------|----------------|----------| +| **Tier0** | 19968 | 79.09% | 4.58 | 84.69% | +| **Tier1** | 4978 | 19.72% | **0.75** | 13.90% | +| **FullOpts** | 300 | 1.19% | 0.08 | 1.39% | +| **Contains OSR** | 2 | 0.01% | 0.00 | 0.02% | +| **Total jitted** | 25248 | 100.00% | 5.41 | 100.00% | + +`ReadyToRun=1`: + +| Metric | Number of methods | Share, % | Total size, Mb | Share, % | +|------------------|-------------------|----------|----------------|----------| +| **Tier0** | 19968 | 79.09% | 4.58 | 84.69% | +| **Tier1** | 4978 | 19.72% | **0.75** | 13.90% | +| **FullOpts** | 300 | 1.19% | 0.08 | 1.39% | +| **Contains OSR** | 2 | 0.01% | 0.00 | 0.02% | +| **Total jitted** | 25248 | 100.00% | 5.41 | 100.00% | + +In case of AvaloniaILSpy, Instrumented tiers add around 1Mb (stubs included) to the working set and around 5k of new jit compilations. + +# Start time and performance impact + +## TechEmpower + +Overall, it is expected from Instrumented tiers to improve startup speed when Dynamic PGO is enabled and improve performance (e.g. Latency/Throughput) for prejitted code. A good example demonstrating both is the following TechEmpower benchmark (plaintext-plaintext): + +![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext.png) + +Legend: +* Red - `DOTNET_TieredPGO=0`, `DOTNET_ReadyToRun=1` +* Black - `DOTNET_TieredPGO=1`, `DOTNET_ReadyToRun=1` +* Yellow - `DOTNET_TieredPGO=1`, `DOTNET_ReadyToRun=0` + +Yellow line provides the highest level of performance (RPS) by sacrificing start up speed (and, hence, time it takes to process the first request). It happens because the benchmark is quite simple and most of its code is already prejitted so we can only instrument it when we completely drop R2R and compile everything from scratch. It also explains why the black line (when we enable Dynamic PGO but still rely on R2R) didn't really show a lot of improvements. With the separate instrumentation tier for hot R2R we achieve "Yellow"-level of performance while maintaining the same start up speed as it was before. Also, for the mode where we have to compile a lot of code to Tier0, switching to "instrument only hot Tier0 code" strategy shows ~8% time-to-first-request reduction across all TE benchmarks. + +![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext-opt.png) + +## AvaloniaILSpy + +For this experiment we modified the source code of the app to send an event once view is completely loaded to measure start time: + +| Mode | Start time | +|----------------------------|------------| +| R2R=0 | 2.03s | +| R2R=0, PGO=1 | 2.26s | +| R2R=0, PGO=1, Instr. Tiers | 2.03s | -Pros & cons of using optimizations inside the instrumented tiers -Pros: - * Lower overhead from instrumentation - * We definitely don't want to use unoptimized instrumented tier for R2R - it will produce a lot of new first-time compilations for non-inlined methods - and performance impact might be noticeable (when we switch from fast R2R to extremely slow Tier0+instrumentation) +As we can see, Instrumentation tiers help to mitigate the start time regression from Dynamic PGO. -Cons: - * Currently, we don't instrument inlinees -> we'll probably miss some oportunities and produce less accurate profile leading to a less optimized final tier - * Non-optimized instrumented tier is faster to prepare for use \ No newline at end of file +(_Predicted results according to local runs_) From 7fd07491aeb84a4dd64c9a8417e69f23698962ba Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Tue, 18 Oct 2022 17:06:34 +0200 Subject: [PATCH 47/53] Update DynamicPgo-InstrumentedTiers.md --- docs/design/features/DynamicPgo-InstrumentedTiers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index d73852c60ac0d3..c520ebbcd2a8d1 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -630,11 +630,11 @@ In this app Tier1 code occupies 8.22mb in the loader heap (we can add a few mega | Metric | Number of methods | Share, % | Total size, Mb | Share, % | |------------------|-------------------|----------|----------------|----------| -| **Tier0** | 19968 | 79.09% | 4.58 | 84.69% | -| **Tier1** | 4978 | 19.72% | **0.75** | 13.90% | -| **FullOpts** | 300 | 1.19% | 0.08 | 1.39% | -| **Contains OSR** | 2 | 0.01% | 0.00 | 0.02% | -| **Total jitted** | 25248 | 100.00% | 5.41 | 100.00% | +| **Tier0** | 4713 | 62.45% | 0.84 | 58.34% | +| **Tier1** | 2516 | 33.34% | 0.56 | 38.75% | +| **FullOpts** | 318 | 4.21% | 0.04 | 2.92% | +| **Contains OSR** | 0 | 0.00% | 0.00 | 0.00% | +| **Total jitted** | 7547 | 100.00% | 1.44 | 100.00% | In case of AvaloniaILSpy, Instrumented tiers add around 1Mb (stubs included) to the working set and around 5k of new jit compilations. From c4395f84f54fa7b7014c64287f6f6becee3e761c Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Oct 2022 00:50:18 +0200 Subject: [PATCH 48/53] Address feedback --- src/coreclr/jit/fginline.cpp | 25 +++++-------------------- src/coreclr/jit/importercalls.cpp | 9 --------- src/coreclr/jit/jitee.h | 5 ----- src/coreclr/vm/tieredcompilation.cpp | 6 ++++-- 4 files changed, 9 insertions(+), 36 deletions(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 9131c7eb6f3558..46f95c036cd4e4 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -529,7 +529,8 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitordspTreeID(tree), block->bbNum); - noway_assert((block->bbNext->countOfInEdges() > 0) && (block->bbJumpDest->countOfInEdges() > 0)); + + noway_assert(!m_compiler->fgComputePredsDone); // We have a constant operand, and should have the all clear to optimize. // Update side effects on the tree, assert there aren't any, and bash to nop. @@ -538,36 +539,20 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitorgtBashToNOP(); m_madeChanges = true; - BasicBlock* bNotTaken = nullptr; - - if (condTree->AsIntCon()->gtIconVal != 0) + if (!condTree->IsIntegralConst(0)) { block->bbJumpKind = BBJ_ALWAYS; - bNotTaken = block->bbNext; } else { block->bbJumpKind = BBJ_NONE; - bNotTaken = block->bbJumpDest; - } - - m_compiler->fgRemoveRefPred(bNotTaken, block); - - // If that was the last ref, a subsequent flow-opt pass - // will clean up the now-unreachable bNotTaken, and any - // other transitively unreachable blocks. - if (bNotTaken->bbRefs == 0) - { - JITDUMP("... it looks like " FMT_BB " is now unreachable!\n", bNotTaken->bbNum); } } } else { - const var_types retType = tree->TypeGet(); - GenTree* foldedTree = m_compiler->gtFoldExpr(tree); - *pTree = foldedTree; - m_madeChanges = true; + *pTree = m_compiler->gtFoldExpr(tree); + m_madeChanges = true; } } }; diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index cbcac15b5ea0aa..e27ddb557c32e9 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -218,15 +218,6 @@ var_types Compiler::impImportCall(OPCODE opcode, { return impImportJitTestLabelMark(sig->numArgs); } - - // static ulong JitHelpers_JitFlags() => 0; - // can be defined anywhere and will be replaced by Debug-version of RyuJIT - if ((mflags & CORINFO_FLG_STATIC) && (sig->numArgs == 0) && (sig->retType == CorInfoType::CORINFO_TYPE_ULONG) && - (strcmp("JitHelpers_JitFlags", eeGetMethodName(methHnd, nullptr)) == 0)) - { - call = gtNewLconNode((__int64)opts.jitFlags->GetRawFlags()); - goto DONE_CALL; - } #endif // DEBUG const bool isIntrinsic = (mflags & CORINFO_FLG_INTRINSIC) != 0; diff --git a/src/coreclr/jit/jitee.h b/src/coreclr/jit/jitee.h index ad5a7121e35000..36b03d87a8c511 100644 --- a/src/coreclr/jit/jitee.h +++ b/src/coreclr/jit/jitee.h @@ -157,11 +157,6 @@ class JitFlags return m_jitFlags == 0; } - unsigned __int64 GetRawFlags() const - { - return m_jitFlags; - } - void SetFromFlags(CORJIT_FLAGS flags) { // We don't want to have to check every one, so we assume it is exactly the same values as the JitFlag diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 9ed15f4707048d..3a8fb3ef61e612 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -284,8 +284,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( #ifdef FEATURE_PGO if (g_pConfig->TieredPGO()) { - if (currentNativeCodeVersion.IsDefaultVersion() && - currentNativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && + if (currentNativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0 && g_pConfig->TieredPGO_InstrumentOnlyHotCode()) { if (ExecutionManager::IsReadyToRunCode(currentNativeCodeVersion.GetNativeCode())) @@ -302,6 +301,9 @@ void TieredCompilationManager::AsyncPromoteToTier1( // 2) Better profile since we'll be able to instrument inlinees // 3) Unoptimized instrumented tier is faster to produce and wire up nextTier = NativeCodeVersion::OptimizationTierInstrumented; + + // NOTE: we might consider using OptimizationTierInstrumentedOptimized if the previous Tier0 + // made it to Tier1-OSR. } } } From a1839966883e51a266a0a2b3a65d5808ec3c7f3e Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 22 Oct 2022 15:31:18 +0200 Subject: [PATCH 49/53] Address Andy's feedback --- .../features/DynamicPgo-InstrumentedTiers.md | 109 ++++++++---------- src/coreclr/debug/daccess/request.cpp | 8 +- src/coreclr/inc/dacprivate.h | 4 +- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/fgprofile.cpp | 6 +- src/coreclr/jit/importercalls.cpp | 8 +- src/coreclr/vm/codeversion.h | 4 +- src/coreclr/vm/eeconfig.cpp | 3 + src/coreclr/vm/prestub.cpp | 6 +- src/coreclr/vm/tieredcompilation.cpp | 18 +-- 10 files changed, 75 insertions(+), 93 deletions(-) diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index c520ebbcd2a8d1..65411c177b656e 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -1,17 +1,17 @@ # Instrumented Tiers -[#70941](https://github.com/dotnet/runtime/pull/70941) introduced separate tiers to instrument hot code mainly to address the following problems: +[#70941](https://github.com/dotnet/runtime/pull/70941) introduced separate tiers to focus on instrumenting only the hot code. It's done to address the following problems: 1) R2R code should still benefit from Dynamic PGO despite being not instrumented in the first place -2) Instrumentation in Tier0 should not affect startup +2) Overhead from the instrumentation in Tier0 should not slow startup -To address both of the problems the following workflow (when TieredPGO is enabled) was introduced: +To address these problems the following workflow was introduced: ```mermaid flowchart prestub(.NET Function) -->|Compilation| hasAO{"Marked with
[AggressiveOpts]?"} - hasAO-->|Yes|tier1ao["JIT to Tier1

(that attribute is extremely
rarely a good idea)"] + hasAO-->|Yes|tier1ao["JIT to Tier1

(no dynamic profile data)"] hasAO-->|No|hasR2R - hasR2R{"Is prejitted (R2R)
and ReadyToRun==1"?} -->|No| tier000 + hasR2R{"Is prejitted (R2R)?"} -->|No| tier000 tier000["JIT to Tier0

(not optimized, not instrumented,
with patchpoints)"]-->|Running...|ishot555 ishot555{"Is hot?
(called >30 times)"} @@ -23,17 +23,17 @@ flowchart ishot1{"Is hot?
(called >30 times)"}-.->|No,
keep running...|ishot1 ishot1--->|"Yes"|tier1inst - tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 + tier0["JIT to Tier0Instrumented

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 tier1pgo2["JIT to Tier1

(optimized with profile data)"] - tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
no patchpoints)"] + tier1inst["JIT to Tier1Instrumented

(optimized, instrumented,
no patchpoints)"] tier1inst-->|Running...|ishot5 ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 ishot5-.->|No,
keep running...|ishot5 ``` (_VSCode doesn't support mermaid diagrams out of the box, consider installing external add-ins_) -It's easier to explain this on a concrete example: +Now, any code is eligible for Dynamic PGO if it's hot enough. It's easier to explain this on a concrete example: ```csharp class Program : IDisposable @@ -67,19 +67,17 @@ class Program : IDisposable The method we'll be looking at is `HotLoop`. The method itself has a hot loop (to show how this work interacts with OSR) but the whole method is expected to be promoted to Tier1 too since it's invoked also in a loop (cold loop). The method also has a virtual call to showcase GDV. -# Case 1: Program is prejitted (R2R) +# Case 1: `HotLoop` is prejitted (R2R) -Let's see what happens with this program when it's prejitted: +Let's see what happens when the method we're inspecting has an AOT version on start: -1) When we start the app, vm picks up R2R'd version of `HotLoop` that looks like this: +1) When we start the app, VM picks up R2R'd version of `HotLoop` that looks like this: ```asm ; Assembly listing for method Program:HotLoop(System.IDisposable) ; Emitting BLENDED_CODE for X64 CPU with AVX - Windows ; ReadyToRun compilation ; optimized code -; rsp based frame -; fully interruptible ; No PGO data G_M43040_IG01: ;; offset=0000H 57 push rdi @@ -110,21 +108,18 @@ G_M43040_IG06: ;; offset=0027H 5F pop rdi C3 ret ;; size=7 bbWeight=1 PerfScore 2.25 - ; Total bytes of code 46 ``` -As we can see from the codegen: it's not instrumented (we never instrument R2R'd code - it would increas binary size by quite a lot), it doesn't have patchpoints for OSR (since it's already optimized) and is optimized. Technically, t can be optimized with a Static PGO but, presumably, it's a rare case in the real world so we left that virtual call here non-devirtualized. +As we can see from the codegen: it's not instrumented (we never instrument R2R'd code - it would increase the binary size by quite a lot), it doesn't have patchpoints for OSR (since it's already optimized) and is optimized. Technically, it can be optimized with a Static PGO but, presumably, it's a rare case in the real world due to complexity, so we left that virtual call here non-devirtualized. -2) HotLoop is invoked >30 times meaning it's likely a hot method so VM "promotes" it to InstrumentedTierOptimized: +2) HotLoop is invoked >30 times meaning it's likely a hot method so VM "promotes" it to Tier1Instrumented: ```asm ; Assembly listing for method Program:HotLoop(System.IDisposable) ; Emitting BLENDED_CODE for X64 CPU with AVX - Windows ; Tier-1 compilation ; optimized code ; instrumented for collecting profile data -; rsp based frame -; fully interruptible ; No PGO data G_M43040_IG01: ;; offset=0000H 57 push rdi @@ -176,15 +171,13 @@ We had to instrument **optimized** code here to mitigate two issues: As a downside - the profile is less accurate and it doesn't instrument inlinees. -3) Th new code version of `HotLoop` is also invoked >30 times leading to a final promotion to Tier1: +3) The new code version of `HotLoop` is also invoked >30 times leading to the final promotion to Tier1: ```asm ; Assembly listing for method Program:HotLoop(System.IDisposable) ; Emitting BLENDED_CODE for X64 CPU with AVX - Windows ; Tier-1 compilation ; optimized code ; optimized using profile data -; rsp based frame -; fully interruptible ; with Dynamic PGO: edge weights are invalid, and fgCalledCount is 48 ; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data G_M43040_IG01: ;; offset=0000H @@ -236,10 +229,9 @@ G_M43040_IG08: ;; offset=0062H 49BB10007E61FD7F0000 mov r11, 0x7FFD617E0010 ; code for System.IDisposable:Dispose 41FF13 call [r11]System.IDisposable:Dispose():this EBDD jmp SHORT G_M43040_IG06 - ; Total bytes of code 116 ``` -The codegen looks a bit bulky but if we look closer we'll see that we clonned the loop to have a fast version with a devirtualized call inside (see `G_M43040_IG03`) without guards inside. To summarize what happened with `HotLoop` we can take a look at this part of the diagram: +The codegen looks a bit bulky but if we look closer we'll see that we clonned the loop to have a fast version with a devirtualized call inside (see `G_M43040_IG03`) with guards hoisted out of that loop. To summarize what happened with `HotLoop` we can take a look at this part of the diagram: ```mermaid flowchart hasR2R("...") -->|Yes| R2R @@ -247,16 +239,16 @@ flowchart ishot1{"Is hot?
(called >30 times)"}-.->|No,
keep running...|ishot1 ishot1--->|"Yes"|tier1inst tier1pgo2["JIT to Tier1

(optimized with profile data)"] - tier1inst["JIT to InstrumentedTierOptimized

(optimized, instrumented,
no patchpoints)"] + tier1inst["JIT to Tier1Instrumented

(optimized, instrumented,
no patchpoints)"] tier1inst-->|Running...|ishot5 ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 ishot5-.->|No,
keep running...|ishot5 ``` -# Case 2: Program is not prejitted +# Case 2: `HotLoop` is not initially prejitted -This case is a bit more complicated since it involves OSR. +This case is a bit more complicated since it involves OSR for this case. 1) Since no R2R version exists for `HotLoop` VM has to ask JIT to compile a Tier0 version of it as fast as it can: ```asm @@ -264,8 +256,6 @@ This case is a bit more complicated since it involves OSR. ; Emitting BLENDED_CODE for X64 CPU with AVX - Windows ; Tier-0 compilation ; MinOpts code -; rbp based frame -; fully interruptible G_M43040_IG01: ;; offset=0000H 55 push rbp 4883EC70 sub rsp, 112 @@ -316,17 +306,15 @@ G_M43040_IG08: ;; offset=0066H ; Total bytes of code 108 ``` -The codegen is unoptimized, with patchpoints for OSR and without instrumentation (to avoid spending time on it for methods which will never make it to tier1) +The codegen is unoptimized, with patchpoints for OSR and without instrumentation (to avoid spending time on it for methods which will never make it to Tier1 - as the practice shows: only 10-20% of methods make it to Tier1) -2) Its loop body triggers OSR: +2) Its loop body triggers OSR after `DOTNET_TC_OnStackReplacement_InitialCounter` iterations (see jitconfigvalue.h): ```asm ; Assembly listing for method Program:HotLoop(System.IDisposable) ; Emitting BLENDED_CODE for X64 CPU with AVX - Windows ; Tier-1 compilation ; OSR variant for entry point 0x11 ; optimized code -; rsp based frame -; fully interruptible ; No PGO data G_M43040_IG01: ;; offset=0000H 4883EC38 sub rsp, 56 @@ -363,17 +351,15 @@ G_M43040_IG06: ;; offset=0047H ; Total bytes of code 82 ``` -Now the loop is faster because of optimizations but is still not instrumented/devirtualized. +Now the loop is faster because of optimizations but is still not instrumented/devirtualized. In theory, we could start instrumenting at least the loop body at this stage, but it's left as is for now, see notes below. -3) `HotLoop` itself is invoked > 30 times, that triggers promotion to InstrumentedTier: +3) `HotLoop` itself is invoked > 30 times, that triggers promotion to Tier0Instrumented: ```asm ; Assembly listing for method Program:HotLoop(System.IDisposable) ; Emitting BLENDED_CODE for X64 CPU with AVX - Windows ; Tier-0 compilation ; MinOpts code ; instrumented for collecting profile data -; rbp based frame -; fully interruptible G_M43040_IG01: ;; offset=0000H 55 push rbp 4881EC80000000 sub rsp, 128 @@ -440,12 +426,12 @@ G_M43040_IG08: ;; offset=00C1H ; Total bytes of code 202 ``` Now the whole method is compiled to Tier0 with instrumentation and patchpoints. No optimizations. -We decided to promote hot Tier0 to InstrumentedTier without optimizations for the following reasons: -1) We won't notice a big performance regression from going from Tier0 to InstrumentedTier -2) InstrumentedTier is faster to compile +We decided to promote hot Tier0 to Tier0Instrumented without optimizations for the following reasons: +1) We won't notice a big performance regression from going from Tier0 to Tier0Instrumented +2) Tier0Instrumented is faster to compile 3) Its profile is more accurate -Although, in this specific case we could consider using InstrumentedTierOptimized since we had a faster loop in previous code version due to Tier1-OSR but since OSR events are rare and we don't want to produce a less accurate profile that we had before https://github.com/dotnet/runtime/pull/70941 it's left as is. We might re-consider this if we improve instrumentation for the optimized code to produce more accurate profile including inlinees. +Although, in this specific case we could consider using Tier1Instrumented since we had a faster loop in the previous code version due to Tier1-OSR, but since OSR events are rare and we don't want to produce a less accurate profile that we had before https://github.com/dotnet/runtime/pull/70941 it's left as is. We might re-consider this when we improve instrumentation for the optimized code to produce a more accurate profile including inlinees. 4) The loop of `HotLoop` triggered OSR once again: ```asm @@ -455,8 +441,6 @@ Although, in this specific case we could consider using InstrumentedTierOptimize ; OSR variant for entry point 0x11 ; optimized code ; optimized using profile data -; rsp based frame -; fully interruptible ; with Dynamic PGO: edge weights are invalid, and fgCalledCount is 9999 ; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data G_M43040_IG01: ;; offset=0000H @@ -521,8 +505,6 @@ We ended up with a very fast version of the method with optimal loop `G_M43040_I ; Tier-1 compilation ; optimized code ; optimized using profile data -; rsp based frame -; fully interruptible ; with Dynamic PGO: edge weights are invalid, and fgCalledCount is 48 ; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data G_M43040_IG01: ;; offset=0000H @@ -584,22 +566,22 @@ flowchart ishot555{"Is hot?
(called >30 times)"} ishot555-.->|No,
keep running...|ishot555 ishot555-->|Yes|tier0 - tier0["JIT to InstrumentedTier

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 + tier0["JIT to Tier0Instrumented

(not optimized, instrumented,
with patchpoints)"]-->|Running...|ishot5 tier1pgo2["JIT to Tier1

(optimized with profile data)"] ishot5{"Is hot?
(called >30 times)"}-->|Yes|tier1pgo2 ishot5-.->|No,
keep running...|ishot5 ``` -It's worth noting that we analyzed the worst (in case of working set) case with OSR, normally (in 99% of cases) we end up only with three code versions for hot code: -1) Tier0 -2) Instrumented Tier0 -3) Tier1 +It's worth noting that we analyzed the worst (in case of working set) case with OSR, normally (in 99.8% of cases) we end up only with three code versions for hot code: +1) Tier0/R2R +2) Instrumented Tier (with or without optimizations) +3) Tier1 optimized with profile -# Impact on Working set +# Working Set Impact -The general rule of thumb that only 10-20% of methods make it to Tier1 and about to 40-60% of all methods are less than 8 bytes of IL (e.g., getters/setters) so we're effectively double the size of Tier1 with this approach (including call counting stubs, etc.) How bad it is compared to overall working set in various apps? let's consider these two examples: +The general rule of thumb that only 10-20% of methods make it to Tier1 and about to 40-60% of all methods are less than 8 bytes of IL (e.g., getters/setters) so we're effectively double the size of Tier1 with this approach (including call counting stubs, etc.). How bad it can be compared to overall working set in various apps? let's consider these two examples: -## 1) A 1st party service (build size is >100Gb) +## 1) A large web app (internal Microsoft service) | Metric | Number of methods | Share, % | Total size, Mb | Share, % | |------------------|-------------------|----------|----------------|----------| @@ -610,11 +592,11 @@ The general rule of thumb that only 10-20% of methods make it to Tier1 and about | **Total jitted** | 195188 | 100.00% | 71.60 | 100.00% | -![IL Histogram 1](DynamicPgo-InstrumentedTiers-ilsize-histogram1.png) +![IL Histogram 1](DynamicPgo-Tier0Instrumenteds-ilsize-histogram1.png) -In this app Tier1 code occupies 8.22mb in the loader heap (we can add a few megabytes on top of it for call counting stubs, jump-stubs, etc.) meaning that instrumentation tier is expected to add a similar amount (~13Mb). The total working set of the service is 7.5Gb so instrumentation tier contributes less than 0.2% of that. We're adding +30k new jit compilations which we can fully compensate with https://github.com/dotnet/runtime/issues/76402 work to avoid potential problems connected with too big queues of methods pending call counting installation/promotions to tier1. +In this app Tier1 code occupies 8.22mb in the loader heap (we can add a few megabytes on top of it for call counting stubs, jump-stubs, etc.) meaning that instrumentated tier is expected to add a similar amount (~13Mb). The total working set of the service is 10Gb so instrumentated tiers contribute ~0.1% of that. We're adding +30k new jit compilations which we can fully compensate with https://github.com/dotnet/runtime/issues/76402 work to avoid potential problems connected with too big queues of methods pending call counting installation/promotions to tier1. -## 2) A desktop application [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy) +## 2) A desktop OSS application [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy) `ReadyToRun=0`: @@ -636,28 +618,29 @@ In this app Tier1 code occupies 8.22mb in the loader heap (we can add a few mega | **Contains OSR** | 0 | 0.00% | 0.00 | 0.00% | | **Total jitted** | 7547 | 100.00% | 1.44 | 100.00% | -In case of AvaloniaILSpy, Instrumented tiers add around 1Mb (stubs included) to the working set and around 5k of new jit compilations. +In case of AvaloniaILSpy, instrumented tiers add around 1Mb (stubs included) to the total working set and around 5k of new jit compilations. # Start time and performance impact ## TechEmpower -Overall, it is expected from Instrumented tiers to improve startup speed when Dynamic PGO is enabled and improve performance (e.g. Latency/Throughput) for prejitted code. A good example demonstrating both is the following TechEmpower benchmark (plaintext-plaintext): +Overall, it is expected from instrumented tiers to improve startup speed when Dynamic PGO is enabled and improve performance (e.g. Latency/Throughput) for prejitted code. A good example demonstrating both is the following TechEmpower benchmark (plaintext-plaintext): -![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext.png) +![Plaintext](DynamicPgo-Tier0Instrumenteds-Plaintext.png) Legend: * Red - `DOTNET_TieredPGO=0`, `DOTNET_ReadyToRun=1` * Black - `DOTNET_TieredPGO=1`, `DOTNET_ReadyToRun=1` * Yellow - `DOTNET_TieredPGO=1`, `DOTNET_ReadyToRun=0` -Yellow line provides the highest level of performance (RPS) by sacrificing start up speed (and, hence, time it takes to process the first request). It happens because the benchmark is quite simple and most of its code is already prejitted so we can only instrument it when we completely drop R2R and compile everything from scratch. It also explains why the black line (when we enable Dynamic PGO but still rely on R2R) didn't really show a lot of improvements. With the separate instrumentation tier for hot R2R we achieve "Yellow"-level of performance while maintaining the same start up speed as it was before. Also, for the mode where we have to compile a lot of code to Tier0, switching to "instrument only hot Tier0 code" strategy shows ~8% time-to-first-request reduction across all TE benchmarks. +Yellow line provides the highest level of performance (RPS) by sacrificing start up speed (and, hence, time it takes to process the first request). It happens because the benchmark is quite simple and most of its code is already prejitted so we can only instrument it when we completely drop R2R and compile everything from scratch. It also explains why the black line (when we enable Dynamic PGO but still rely on R2R) didn't really show a lot of improvements. With the separate instrumentated tiers for hot R2R we achieve "Yellow"-level of performance while maintaining the same start up speed as it was before. Also, for the mode where we have to compile a lot of code to Tier0, switching to "instrument only hot Tier0 code" strategy shows ~8% time-to-first-request reduction across all TE benchmarks. -![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext-opt.png) +![Plaintext](DynamicPgo-Tier0Instrumenteds-Plaintext-opt.png) +(_Predicted results according to local runs_) ## AvaloniaILSpy -For this experiment we modified the source code of the app to send an event once view is completely loaded to measure start time: +For this experiment we modified the source code of the app to send an event once view is completely loaded to measure the real start time: | Mode | Start time | |----------------------------|------------| @@ -665,6 +648,4 @@ For this experiment we modified the source code of the app to send an event once | R2R=0, PGO=1 | 2.26s | | R2R=0, PGO=1, Instr. Tiers | 2.03s | -As we can see, Instrumentation tiers help to mitigate the start time regression from Dynamic PGO. - -(_Predicted results according to local runs_) +As we can see, instrumentated tiers help to mitigate the start time regression from Dynamic PGO. diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 20afb956bc7cb9..7f48fc6819c82a 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -1112,11 +1112,11 @@ HRESULT ClrDataAccess::GetTieredVersions( case NativeCodeVersion::OptimizationTierOptimized: nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_Optimized; break; - case NativeCodeVersion::OptimizationTierInstrumented: - nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_InstrumentedTier; + case NativeCodeVersion::OptimizationTier0Instrumented: + nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_QuickJittedInstrumented; break; - case NativeCodeVersion::OptimizationTierInstrumentedOptimized: - nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_InstrumentedTierOptimized; + case NativeCodeVersion::OptimizationTier1Instrumented: + nativeCodeAddrs[count].OptimizationTier = DacpTieredVersionData::OptimizationTier_OptimizedTier1Instrumented; break; } } diff --git a/src/coreclr/inc/dacprivate.h b/src/coreclr/inc/dacprivate.h index 7c41b55dbd7609..5d920fd0da4905 100644 --- a/src/coreclr/inc/dacprivate.h +++ b/src/coreclr/inc/dacprivate.h @@ -610,8 +610,8 @@ struct MSLAYOUT DacpTieredVersionData OptimizationTier_OptimizedTier1, OptimizationTier_ReadyToRun, OptimizationTier_OptimizedTier1OSR, - OptimizationTier_InstrumentedTier, - OptimizationTier_InstrumentedTierOptimized, + OptimizationTier_QuickJittedInstrumented, + OptimizationTier_OptimizedTier1Instrumented, }; CLRDATA_ADDRESS NativeCodeAddr; diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index e4813050fabc8b..1ad5cb944ad8f4 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -4107,7 +4107,7 @@ const char* Compiler::compGetTieringName(bool wantShortName) const } else { - return "Tier1"; + return instrumenting ? "Instrumented Tier1" : "Tier1"; } } else if (opts.OptimizationEnabled()) diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 488112cf78844f..19bdccad8a2685 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -1887,8 +1887,8 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() (JitConfig.TC_PartialCompilation() > 0); const bool prejit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); const bool tier0WithPatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && mayHavePatchpoints; - const bool instrOpt = opts.IsInstrumentedOptimized(); - const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !instrOpt; + const bool osrMethod = opts.IsInstrumentedOptimized() && opts.IsOSR(); + const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !osrMethod; if (useEdgeProfiles) { @@ -1899,7 +1899,7 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() JITDUMP("Using block profiling, because %s\n", (JitConfig.JitEdgeProfiling() == 0) ? "edge profiles disabled" - : prejit ? "prejitting" : instrOpt ? "optimized instr" : "tier0 with patchpoints"); + : prejit ? "prejitting" : osrMethod ? "OSR" : "tier0 with patchpoints"); fgCountInstrumentor = new (this, CMK_Pgo) BlockCountInstrumentor(this); } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 4986c92ae2a43c..de5482d622e207 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1310,16 +1310,14 @@ var_types Compiler::impImportCall(OPCODE opcode, const bool mustImportEntryBlock = gtIsRecursiveCall(methHnd) || actualCall->IsInlineCandidate() || actualCall->IsGuardedDevirtualizationCandidate(); - BasicBlock* entryBb = opts.IsOSR() ? fgEntryBB : fgFirstBB; - // Only schedule importation if we're not currently importing. // - if ((opts.IsInstrumentedOptimized() || opts.IsOSR()) && mustImportEntryBlock && (compCurBB != entryBb)) + if ((opts.IsInstrumentedOptimized() && opts.IsOSR()) && mustImportEntryBlock && (compCurBB != fgEntryBB)) { JITDUMP("\ninlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB " for importation\n", - dspTreeID(call), entryBb->bbNum); - impImportBlockPending(entryBb); + dspTreeID(call), fgEntryBB->bbNum); + impImportBlockPending(fgEntryBB); } } } diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 7edacf57c087f9..66de4ba27257a4 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -78,8 +78,8 @@ class NativeCodeVersion OptimizationTier1, OptimizationTier1OSR, OptimizationTierOptimized, // may do less optimizations than tier 1 - OptimizationTierInstrumented, - OptimizationTierInstrumentedOptimized, + OptimizationTier0Instrumented, + OptimizationTier1Instrumented, }; #ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 84ba02b5b2469a..f438a31838fa14 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -783,6 +783,9 @@ HRESULT EEConfig::sync() #if defined(FEATURE_PGO) fTieredPGO = Configuration::GetKnobBooleanValue(W("System.Runtime.TieredPGO"), CLRConfig::EXTERNAL_TieredPGO); + + // Also, consider DynamicPGO enabled if WritePGOData is set + fTieredPGO |= CLRConfig::GetConfigValue(CLRConfig::INTERNAL_WritePGOData) != 0; tieredPGO_InstrumentOnlyHotCode = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredPGO_InstrumentOnlyHotCode) == 1; // We need quick jit for TieredPGO diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index a4eb6c19961ef3..4b4373ac40e818 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1225,10 +1225,10 @@ PrepareCodeConfig::JitOptimizationTier PrepareCodeConfig::GetJitOptimizationTier case NativeCodeVersion::OptimizationTierOptimized: return JitOptimizationTier::Optimized; - case NativeCodeVersion::OptimizationTierInstrumented: + case NativeCodeVersion::OptimizationTier0Instrumented: return JitOptimizationTier::InstrumentedTier; - case NativeCodeVersion::OptimizationTierInstrumentedOptimized: + case NativeCodeVersion::OptimizationTier1Instrumented: return JitOptimizationTier::InstrumentedTierOptimized; default: @@ -1304,7 +1304,7 @@ bool PrepareCodeConfig::FinalizeOptimizationTierForTier0LoadOrJit() NativeCodeVersion::OptimizationTier previousOptimizationTier = GetCodeVersion().GetOptimizationTier(); _ASSERTE( previousOptimizationTier == NativeCodeVersion::OptimizationTier0 || - previousOptimizationTier == NativeCodeVersion::OptimizationTierInstrumented || + previousOptimizationTier == NativeCodeVersion::OptimizationTier0Instrumented || previousOptimizationTier == NativeCodeVersion::OptimizationTierOptimized); #endif // _DEBUG diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 3a8fb3ef61e612..dcff50459c8b4a 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -117,13 +117,13 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza // Initial tier for R2R is always just OptimizationTier0 // For ILOnly it depends on TieredPGO_InstrumentOnlyHotCode: // 1 - OptimizationTier0 as we don't want to instrument the initial version (will only instrument hot Tier0) - // 2 - OptimizationTierInstrumented - instrument all ILOnly code + // 2 - OptimizationTier0Instrumented - instrument all ILOnly code if (g_pConfig->TieredPGO_InstrumentOnlyHotCode() || ExecutionManager::IsReadyToRunCode(pMethodDesc->GetNativeCode())) { return NativeCodeVersion::OptimizationTier0; } - return NativeCodeVersion::OptimizationTierInstrumented; + return NativeCodeVersion::OptimizationTier0Instrumented; } #endif @@ -292,7 +292,7 @@ void TieredCompilationManager::AsyncPromoteToTier1( // We definitely don't want to use unoptimized instrumentation tier for hot R2R: // 1) It will produce a lot of new compilations for small methods which were inlined in R2R // 2) Noticeable performance regression from fast R2R to slow instrumented Tier0 - nextTier = NativeCodeVersion::OptimizationTierInstrumentedOptimized; + nextTier = NativeCodeVersion::OptimizationTier1Instrumented; } else { @@ -300,9 +300,9 @@ void TieredCompilationManager::AsyncPromoteToTier1( // 1) No new compilations since previous tier already triggered them // 2) Better profile since we'll be able to instrument inlinees // 3) Unoptimized instrumented tier is faster to produce and wire up - nextTier = NativeCodeVersion::OptimizationTierInstrumented; + nextTier = NativeCodeVersion::OptimizationTier0Instrumented; - // NOTE: we might consider using OptimizationTierInstrumentedOptimized if the previous Tier0 + // NOTE: we might consider using OptimizationTier1Instrumented if the previous Tier0 // made it to Tier1-OSR. } } @@ -1066,14 +1066,14 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) if (g_pConfig->TieredCompilation_QuickJit()) { - if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumented) + if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTier0Instrumented) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); return flags; } - if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTierInstrumentedOptimized) + if (currentTier == NativeCodeVersion::OptimizationTier::OptimizationTier1Instrumented) { flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); @@ -1103,13 +1103,13 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(PrepareCodeConfig *config) switch (nativeCodeVersion.GetOptimizationTier()) { - case NativeCodeVersion::OptimizationTierInstrumented: + case NativeCodeVersion::OptimizationTier0Instrumented: _ASSERT(g_pConfig->TieredCompilation_QuickJit()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); break; - case NativeCodeVersion::OptimizationTierInstrumentedOptimized: + case NativeCodeVersion::OptimizationTier1Instrumented: _ASSERT(g_pConfig->TieredCompilation_QuickJit()); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR); flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); From ff471e15af6cd8a4fb12b3606933583cc24c8e29 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 22 Oct 2022 20:54:35 +0200 Subject: [PATCH 50/53] Disable edge-profiling with a comment --- src/coreclr/jit/fgprofile.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 19bdccad8a2685..5c2b9fa3b96901 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -1887,8 +1887,11 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() (JitConfig.TC_PartialCompilation() > 0); const bool prejit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); const bool tier0WithPatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && mayHavePatchpoints; - const bool osrMethod = opts.IsInstrumentedOptimized() && opts.IsOSR(); - const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !osrMethod; + const bool isOptimized = opts.IsInstrumentedOptimized(); + const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !isOptimized; + + // TODO-TP: Don't give up on edge profiling for optimized code, currently it has issues + // such as unexpected trees near tail calls if (useEdgeProfiles) { @@ -1899,7 +1902,7 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod() JITDUMP("Using block profiling, because %s\n", (JitConfig.JitEdgeProfiling() == 0) ? "edge profiles disabled" - : prejit ? "prejitting" : osrMethod ? "OSR" : "tier0 with patchpoints"); + : prejit ? "prejitting" : isOptimized ? "tier1 instrumented" : "tier0 with patchpoints"); fgCountInstrumentor = new (this, CMK_Pgo) BlockCountInstrumentor(this); } From b202b9f1f689a807af6a85d955ae6e4cc5fd5962 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 22 Oct 2022 22:43:34 +0200 Subject: [PATCH 51/53] fix assert --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index de5482d622e207..5c90d1efc81520 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1312,7 +1312,7 @@ var_types Compiler::impImportCall(OPCODE opcode, // Only schedule importation if we're not currently importing. // - if ((opts.IsInstrumentedOptimized() && opts.IsOSR()) && mustImportEntryBlock && (compCurBB != fgEntryBB)) + if (opts.IsOSR() && mustImportEntryBlock && (compCurBB != fgEntryBB)) { JITDUMP("\ninlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB " for importation\n", From 64eb6df6f2938af88bd9a2873ef3215cee0d8a8a Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Mon, 24 Oct 2022 19:09:44 +0200 Subject: [PATCH 52/53] Update docs/design/features/DynamicPgo-InstrumentedTiers.md Co-authored-by: Andy Ayers --- docs/design/features/DynamicPgo-InstrumentedTiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index 65411c177b656e..b48547bea55b9d 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -231,7 +231,7 @@ G_M43040_IG08: ;; offset=0062H EBDD jmp SHORT G_M43040_IG06 ; Total bytes of code 116 ``` -The codegen looks a bit bulky but if we look closer we'll see that we clonned the loop to have a fast version with a devirtualized call inside (see `G_M43040_IG03`) with guards hoisted out of that loop. To summarize what happened with `HotLoop` we can take a look at this part of the diagram: +The codegen looks a bit bulky but if we look closer we'll see that we cloned the loop to have a fast version with a devirtualized call inside (see `G_M43040_IG03`) with guards hoisted out of that loop. To summarize what happened with `HotLoop` we can take a look at this part of the diagram: ```mermaid flowchart hasR2R("...") -->|Yes| R2R From 07339dc644b70c7c32190380f50018a9fd03f562 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 25 Oct 2022 22:32:47 +0200 Subject: [PATCH 53/53] Fix "mb" in the doc, add one more example to the "perf impact" section --- ...amicPgo-InstrumentedTiers-msft-service.png | Bin 0 -> 25240 bytes .../features/DynamicPgo-InstrumentedTiers.md | 36 ++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 docs/design/features/DynamicPgo-InstrumentedTiers-msft-service.png diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers-msft-service.png b/docs/design/features/DynamicPgo-InstrumentedTiers-msft-service.png new file mode 100644 index 0000000000000000000000000000000000000000..be6e94e8d826f495b7771d5b74ae82ec56580c6f GIT binary patch literal 25240 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 literal 0 HcmV?d00001 diff --git a/docs/design/features/DynamicPgo-InstrumentedTiers.md b/docs/design/features/DynamicPgo-InstrumentedTiers.md index b48547bea55b9d..b7786f37d6575a 100644 --- a/docs/design/features/DynamicPgo-InstrumentedTiers.md +++ b/docs/design/features/DynamicPgo-InstrumentedTiers.md @@ -583,7 +583,7 @@ The general rule of thumb that only 10-20% of methods make it to Tier1 and about ## 1) A large web app (internal Microsoft service) -| Metric | Number of methods | Share, % | Total size, Mb | Share, % | +| Metric | Number of methods | Share, % | Total size, MB | Share, % | |------------------|-------------------|----------|----------------|----------| | **Tier0** | 115862 | 59.36% | 60.06 | 83.89% | | **Tier1** | 30942 | 15.85% | 8.22 | 11.48% | @@ -592,33 +592,35 @@ The general rule of thumb that only 10-20% of methods make it to Tier1 and about | **Total jitted** | 195188 | 100.00% | 71.60 | 100.00% | -![IL Histogram 1](DynamicPgo-Tier0Instrumenteds-ilsize-histogram1.png) +![IL Histogram 1](DynamicPgo-InstrumentedTiers-ilsize-histogram1.png) -In this app Tier1 code occupies 8.22mb in the loader heap (we can add a few megabytes on top of it for call counting stubs, jump-stubs, etc.) meaning that instrumentated tier is expected to add a similar amount (~13Mb). The total working set of the service is 10Gb so instrumentated tiers contribute ~0.1% of that. We're adding +30k new jit compilations which we can fully compensate with https://github.com/dotnet/runtime/issues/76402 work to avoid potential problems connected with too big queues of methods pending call counting installation/promotions to tier1. +In this app Tier1 code occupies 8.22MB in the loader heap (we can add a few megabytes on top of it for call counting stubs, jump-stubs, etc.) meaning that instrumentated tier is expected to add a similar amount (~13MB). The total working set of the service is 10GB so instrumentated tiers contribute ~0.1% of that. We're adding +30k new jit compilations which we can fully compensate with https://github.com/dotnet/runtime/issues/76402 work to avoid potential problems connected with too big queues of methods pending call counting installation/promotions to tier1. ## 2) A desktop OSS application [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy) `ReadyToRun=0`: -| Metric | Number of methods | Share, % | Total size, Mb | Share, % | +| Metric | Number of methods | % | Total size, MB | % | |------------------|-------------------|----------|----------------|----------| | **Tier0** | 19968 | 79.09% | 4.58 | 84.69% | | **Tier1** | 4978 | 19.72% | **0.75** | 13.90% | | **FullOpts** | 300 | 1.19% | 0.08 | 1.39% | -| **Contains OSR** | 2 | 0.01% | 0.00 | 0.02% | -| **Total jitted** | 25248 | 100.00% | 5.41 | 100.00% | +| **OSR** | 2 | 0.01% | 0.00 | 0.02% | +| | | | | | +| **Total** | 25248 | 100.00% | 5.41 | 100.00% | `ReadyToRun=1`: -| Metric | Number of methods | Share, % | Total size, Mb | Share, % | +| Metric | Number of methods | % | Total size, MB | % | |------------------|-------------------|----------|----------------|----------| | **Tier0** | 4713 | 62.45% | 0.84 | 58.34% | | **Tier1** | 2516 | 33.34% | 0.56 | 38.75% | | **FullOpts** | 318 | 4.21% | 0.04 | 2.92% | -| **Contains OSR** | 0 | 0.00% | 0.00 | 0.00% | -| **Total jitted** | 7547 | 100.00% | 1.44 | 100.00% | +| **OSR** | 0 | 0.00% | 0.00 | 0.00% | +| | | | | | +| **Total** | 7547 | 100.00% | 1.44 | 100.00% | -In case of AvaloniaILSpy, instrumented tiers add around 1Mb (stubs included) to the total working set and around 5k of new jit compilations. +In case of AvaloniaILSpy, instrumented tiers add around 1MB (stubs included) to the total working set and around 5k of new jit compilations. # Start time and performance impact @@ -626,7 +628,7 @@ In case of AvaloniaILSpy, instrumented tiers add around 1Mb (stubs included) to Overall, it is expected from instrumented tiers to improve startup speed when Dynamic PGO is enabled and improve performance (e.g. Latency/Throughput) for prejitted code. A good example demonstrating both is the following TechEmpower benchmark (plaintext-plaintext): -![Plaintext](DynamicPgo-Tier0Instrumenteds-Plaintext.png) +![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext.png) Legend: * Red - `DOTNET_TieredPGO=0`, `DOTNET_ReadyToRun=1` @@ -635,7 +637,7 @@ Legend: Yellow line provides the highest level of performance (RPS) by sacrificing start up speed (and, hence, time it takes to process the first request). It happens because the benchmark is quite simple and most of its code is already prejitted so we can only instrument it when we completely drop R2R and compile everything from scratch. It also explains why the black line (when we enable Dynamic PGO but still rely on R2R) didn't really show a lot of improvements. With the separate instrumentated tiers for hot R2R we achieve "Yellow"-level of performance while maintaining the same start up speed as it was before. Also, for the mode where we have to compile a lot of code to Tier0, switching to "instrument only hot Tier0 code" strategy shows ~8% time-to-first-request reduction across all TE benchmarks. -![Plaintext](DynamicPgo-Tier0Instrumenteds-Plaintext-opt.png) +![Plaintext](DynamicPgo-InstrumentedTiers-Plaintext-opt.png) (_Predicted results according to local runs_) ## AvaloniaILSpy @@ -649,3 +651,13 @@ For this experiment we modified the source code of the app to send an event once | R2R=0, PGO=1, Instr. Tiers | 2.03s | As we can see, instrumentated tiers help to mitigate the start time regression from Dynamic PGO. + +## Microsoft internal service + +Throughput of the service after startup: + +![Plaintext](DynamicPgo-InstrumentedTiers-msft-service.png) + +X axis - time in seconds after start, Y axis - Throughput in MB/s. + +Here Dynamic PGO without instrumented tiers (red line) is not able to show benefits because the service is prejitted thus prejitted code doesn't benefit from Dynamic PGO. Instrumented tiers help with that by instrumenting hot R2R code to achieve the best performance, hence, the throughput is higher (green line). \ No newline at end of file

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