.NET Core 3 Tiered Compilation


Tiered Compilation allows the .NET runtime to substitute different assembly code method implementations for the same method during the lifetime of an application to achieve higher performance. It currently achieves this in the following two ways.

Startup

  • Whenever code needs to be jitted, the runtime first generates a low-quality code body, then replaces it with a higher code quality version later if the method appears hot.
  • The lower quality initial codegen saves JIT time, and these savings typically dominate the additional cost to run the lower quality code for a short time.

Steady-State

  • If code loaded from ReadyToRun images appears hot, the runtime replaces it with jitted code, which is typically higher quality.
  • At runtime, the JIT can observe the exact dependencies that are loaded and CPU instruction support which allows it to generate superior code.
  • In the future, it may also utilize profile-guided feedback, but it does not currently do so.

The tiered compilation (TC) is on by default with .NET Core 3.0. This feature enables the runtime to adaptively use the just-in-time (JIT) compiler to achieve better performance.

The main benefit of tiered compilation is to provide two ways of jitting methods.

  • Lower-quality-but-faster tier
  • A higher-quality-but-slower tier.

The quality refers to how well the method is optimized. TC helps to improve an application's performance as it goes through various stages of execution, from startup through the steady-state.

TC Disabled

When the tiered compilation is disabled, every method is compiled in a single way that is biased to steady-state performance over startup performance.

TC Enabled

When the tiered compilation is enabled, the following behavior applies for method compilation when an app starts.

  • If the method has ahead-of-time-compiled code or ReadyToRun, the pregenerated code is used.
  • Otherwise, the method is jitted. Typically, these methods are generics over value types.
  • Quick JIT produces lower-quality or less optimized code more quickly.
  • In .NET Core 3.0, Quick JIT is enabled by default for methods that do not contain loops and is preferred during startup.
  • The fully optimizing JIT produces higher-quality or more optimized code more slowly.
  • For methods where Quick JIT would not be used, for example, if the method is attributed with MethodImplOptions.AggressiveOptimization, the fully optimizing JIT is used.

For frequently called methods, the just-in-time compiler eventually creates fully optimized code in the background. The optimized code then replaces the pre-compiled code for that method.

Code generated by Quick JIT may run slower, allocate more memory, or use more stack space. If there are issues, you can disable Quick JIT using this MSBuild property in the project file:

<PropertyGroup>
  <TieredCompilationQuickJit>false</TieredCompilationQuickJit>
</PropertyGroup>

To disable TC completely, use this MSBuild property in your project file:

<PropertyGroup>
  <TieredCompilation>false</TieredCompilation>
</PropertyGroup>

If you change these settings in the project file, you may need to perform a clean build for the new settings to be reflected (delete the obj and bin directories and rebuild).