@@ -7,15 +7,25 @@ namespace BenchmarkDotNet.Portability
77{ 
88    // Implementation is based on article https://medium.com/@meriffa/net-core-concepts-tiered-compilation-10f7da3a29c7 
99    // documentation https://learn.microsoft.com/en-us/dotnet/core/runtime-config/compilation 
10-     // and source https://github.com/dotnet/runtime/blob/71c30b405516b1fe774a1bfdbc43cd804468568f/src/coreclr/vm/eeconfig.cpp 
10+     // and source https://github.com/dotnet/runtime/blob/3fb6fbb3efaa7f2ae5e76bf235615d7f70005201/src/coreclr/vm/eeconfig.cpp 
11+     // https://github.com/dotnet/runtime/blob/3fb6fbb3efaa7f2ae5e76bf235615d7f70005201/src/coreclr/jit/jitconfigvalues.h 
1112    internal  static class  JitInfo 
1213    { 
13-         public  const  string  MinOptsEnv  =  "JITMinOpts" ; 
14-         public  const  string  TieredCompilationEnv  =  "TieredCompilation" ; 
15-         public  const  string  DynamicPGOEnv  =  "TieredPGO" ; 
16-         public  const  string  AggressiveTieringEnv  =  "TC_AggressiveTiering" ; 
17-         public  const  string  CallCountThresholdEnv  =  "TC_CallCountThreshold" ; 
18-         public  const  string  CallCountingDelayMsEnv  =  "TC_CallCountingDelayMs" ; 
14+         public  const  string  EnvCallCountingDelayMs  =  "TC_CallCountingDelayMs" ; 
15+ 
16+         private  const  string  EnvMinOpts  =  "JITMinOpts" ; 
17+         private  const  string  EnvTieredCompilation  =  "TieredCompilation" ; 
18+         private  const  string  EnvQuickJit  =  "TC_QuickJit" ; 
19+         private  const  string  EnvPGO  =  "TieredPGO" ; 
20+         private  const  string  EnvCallCountThreshold  =  "TC_CallCountThreshold" ; 
21+         private  const  string  EnvAggressiveTiering  =  "TC_AggressiveTiering" ; 
22+         private  const  string  EnvOSR  =  "TC_OnStackReplacement" ; 
23+ 
24+         private  const  string  KnobTieredCompilation  =  "System.Runtime.TieredCompilation" ; 
25+         private  const  string  KnobQuickJit  =  "System.Runtime.TieredCompilation.QuickJit" ; 
26+         private  const  string  KnobPGO  =  "System.Runtime.TieredPGO" ; 
27+         private  const  string  KnobCallCountThreshold  =  "System.Runtime.TieredCompilation.CallCountThreshold" ; 
28+         private  const  string  KnobCallCountingDelayMs  =  "System.Runtime.TieredCompilation.CallCountingDelayMs" ; 
1929
2030        // .Net 5 and older uses COMPlus_ prefix, 
2131        // .Net 6+ uses DOTNET_ prefix, but still supports legacy COMPlus_. 
@@ -40,34 +50,71 @@ private static bool IsKnobDisabled(string name)
4050        private  static bool  TryParseKnob ( string  name ,  out  int  value ) 
4151            =>  int . TryParse ( AppContext . GetData ( name )  as  string ,  out  value ) ; 
4252
53+         private  static bool  IsEnabled ( string  envName ,  string  knobName ) 
54+             =>  IsEnvVarEnabled ( envName )  ||  IsKnobEnabled ( knobName ) ; 
55+ 
56+         private  static bool  IsDisabled ( string  envName ,  string  knobName ) 
57+             =>  IsEnvVarDisabled ( envName )  ||  IsKnobDisabled ( knobName ) ; 
58+ 
4359        /// <summary> 
4460        /// Is tiered JIT enabled? 
4561        /// </summary> 
4662        public  static readonly  bool  IsTiered  = 
4763            IsNetCore 
4864            // JITMinOpts disables tiered compilation (all methods are effectively tier0 instead of tier1). 
49-             &&  ! IsEnvVarEnabled ( MinOptsEnv ) 
65+             &&  ! IsEnvVarEnabled ( EnvMinOpts ) 
5066            &&  ( ( CoreRuntime . TryGetVersion ( out  var  version )  &&  version . Major  >=  3 ) 
5167                // Enabled by default in netcoreapp3.0+, check if it's disabled. 
52-                 ?  ! IsEnvVarDisabled ( TieredCompilationEnv )   &&   ! IsKnobDisabled ( "System.Runtime.TieredCompilation" ) 
68+                 ?  ! IsDisabled ( EnvTieredCompilation ,   KnobTieredCompilation ) 
5369                // Disabled by default in netcoreapp2.X, check if it's enabled. 
54-                 :  IsEnvVarEnabled ( TieredCompilationEnv )   ||   IsKnobEnabled ( "System.Runtime.TieredCompilation" ) ) ; 
70+                 :  IsEnabled ( EnvTieredCompilation ,   KnobTieredCompilation ) ) ; 
5571
5672        /// <summary> 
57-         /// Is tiered JIT enabled with dynamic profile-guided optimization (tier0 instrumented)?  
73+         /// The maximum numbers of jit tiers that a method may be promoted through. This is the maximum number of jit tiers - 1.  
5874        /// </summary> 
59-         public  static readonly  bool  IsDPGO  = 
60-             IsTiered 
61-             // Added experimentally in .Net 6 
62-             &&  Environment . Version . Major  >=  6 
63-             // Disabled if QuickJit is disabled in .Net 7+. 
64-             &&  ( Environment . Version . Major  <  7  ||  ( ! IsEnvVarDisabled ( "TC_QuickJit" )  &&  ! IsKnobDisabled ( "System.Runtime.TieredCompilation.QuickJit" ) ) ) 
65-             &&  ( Environment . Version . Major  >=  8 
66-                 // Enabled by default in .Net 8, check if it's disabled 
67-                 ?  ! IsEnvVarDisabled ( DynamicPGOEnv )  &&  ! IsKnobDisabled ( "System.Runtime.TieredPGO" ) 
68-                 // Disabled by default in earlier versions, check if it's enabled. 
69-                 :  IsEnvVarEnabled ( DynamicPGOEnv )  ||  IsKnobEnabled ( "System.Runtime.TieredPGO" ) ) ; 
75+         public  static readonly  int  MaxTierPromotions  =  GetMaxTierPromotions ( ) ; 
7076
77+         private  static int  GetMaxTierPromotions ( ) 
78+         { 
79+             if  ( ! IsTiered ) 
80+             { 
81+                 return  0 ; 
82+             } 
83+             // Tier1 
84+             int  maxPromotions  =  1 ; 
85+             if  ( GetIsDPGO ( ) ) 
86+             { 
87+                 // Tier0 instrumented 
88+                 ++ maxPromotions ; 
89+             } 
90+             if  ( GetIsOSR ( ) ) 
91+             { 
92+                 // On-stack-replacement *shouldn't* interfere with promotion velocity, but there is a bug where OSR may cause a method to be tier0 instrumented twice. 
93+                 // https://github.com/dotnet/runtime/issues/117787#issuecomment-3090771091 
94+                 ++ maxPromotions ; 
95+             } 
96+             return  maxPromotions ; 
97+ 
98+             static bool  GetIsDPGO ( )  => 
99+                 // Added experimentally in .Net 6. 
100+                 Environment . Version . Major  >=  6 
101+                 // Disabled if QuickJit is disabled in .Net 7+. 
102+                 &&  ( Environment . Version . Major  <  7  ||  ! IsDisabled ( EnvQuickJit ,  KnobQuickJit ) ) 
103+                 &&  ( Environment . Version . Major  >=  8 
104+                     // Enabled by default in .Net 8, check if it's disabled. 
105+                     ?  ! IsDisabled ( EnvPGO ,  KnobPGO ) 
106+                     // Disabled by default in earlier versions, check if it's enabled. 
107+                     :  IsEnabled ( EnvPGO ,  KnobPGO ) ) ; 
108+ 
109+             static bool  GetIsOSR ( )  => 
110+                 // Added experimentally in .Net 5. 
111+                 Environment . Version . Major  >=  5 
112+                 &&  ( Environment . Version . Major  >=  7 
113+                     // Enabled by default in .Net 7, check if it's disabled. 
114+                     ?  ! IsEnvVarDisabled ( EnvOSR ) 
115+                     // Disabled by default in earlier versions, check if it's enabled. 
116+                     :  IsEnvVarEnabled ( EnvOSR ) ) ; 
117+         } 
71118
72119        /// <summary> 
73120        /// The number of times a method must be called before it will be eligible for the next JIT tier. 
@@ -81,16 +128,16 @@ private static int GetTieredCallCountThreshold()
81128                return  0 ; 
82129            } 
83130            // AggressiveTiering was added in .Net 5. 
84-             if  ( Environment . Version . Major  >=  5  &&  IsEnvVarEnabled ( AggressiveTieringEnv ) ) 
131+             if  ( Environment . Version . Major  >=  5  &&  IsEnvVarEnabled ( EnvAggressiveTiering ) ) 
85132            { 
86133                return  1 ; 
87134            } 
88-             if  ( TryParseEnvVar ( CallCountThresholdEnv ,  out  int  callCountThreshold ) ) 
135+             if  ( TryParseEnvVar ( EnvCallCountThreshold ,  out  int  callCountThreshold ) ) 
89136            { 
90137                return  callCountThreshold ; 
91138            } 
92139            // CallCountThreshold was added as a knob in .Net 8. 
93-             if  ( Environment . Version . Major  >=  8  &&  TryParseKnob ( "System.Runtime.TieredCompilation.CallCountThreshold" ,  out  callCountThreshold ) ) 
140+             if  ( Environment . Version . Major  >=  8  &&  TryParseKnob ( KnobCallCountThreshold ,  out  callCountThreshold ) ) 
94141            { 
95142                return  callCountThreshold ; 
96143            } 
@@ -110,16 +157,16 @@ private static TimeSpan GetTieredDelay()
110157                return  TimeSpan . Zero ; 
111158            } 
112159            // AggressiveTiering was added in .Net 5. 
113-             if  ( Environment . Version . Major  >=  5  &&  IsEnvVarEnabled ( AggressiveTieringEnv ) ) 
160+             if  ( Environment . Version . Major  >=  5  &&  IsEnvVarEnabled ( EnvAggressiveTiering ) ) 
114161            { 
115162                return  TimeSpan . Zero ; 
116163            } 
117-             if  ( TryParseEnvVar ( CallCountingDelayMsEnv ,  out  int  callCountDelay ) ) 
164+             if  ( TryParseEnvVar ( EnvCallCountingDelayMs ,  out  int  callCountDelay ) ) 
118165            { 
119166                return  TimeSpan . FromMilliseconds ( callCountDelay ) ; 
120167            } 
121168            // CallCountingDelayMs was added as a knob in .Net 8. 
122-             if  ( Environment . Version . Major  >=  8  &&  TryParseKnob ( "System.Runtime.TieredCompilation.CallCountingDelayMs" ,  out  callCountDelay ) ) 
169+             if  ( Environment . Version . Major  >=  8  &&  TryParseKnob ( KnobCallCountingDelayMs ,  out  callCountDelay ) ) 
123170            { 
124171                return  TimeSpan . FromMilliseconds ( callCountDelay ) ; 
125172            } 
0 commit comments