Hey,
Just a small question regarding the behavior of RuntimeHelpers.PrepareMethod in the presence of tiered compilation: Does it assume max optimizations or it generates only the thunk jump code?
cc @kouvel
Currently it prepares the tier 0 version of the code by either loading pregenerated code or by jitting it at tier 0. Tier 1 jitting usually happens in a background thread, so it would not occur in a constrained execution region. If the intention is to optimize the method as quickly as possible, it may be a better option to attribute the method with [MethodImpl(MethodImplOptions.AggressiveOptimization)], which currently would skip tier 0 for the method, then with PrepareMethod it would currently prepare the optimized version of the code. In the future though, the attribute may not immediately generate fully optimized code but it would try to optimize it as quickly as possible.
Hm.. [MethodImpl(MethodImplOptions.AggressiveOptimization)] + PrepareMethod doesn't work for me - COMPlus_JitDisasm still shows tier0 asm.
It seems to be working for me:
```c#
private static void Main(string[] args)
{
var bindingFlags = BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Static;
RuntimeHelpers.PrepareMethod(typeof(Program).GetMethod("Foo", bindingFlags).MethodHandle);
Foo();
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private static void Foo()
{
Foo2();
}
private static void Foo2()
{
}
```asm
; Assembly listing for method Program:Foo()
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; Tier-1 compilation
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
;# V00 OutArgs [V00 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00] "OutgoingArgSpace"
;
; Lcl frame size = 0
G_M16031_IG01:
G_M16031_IG02:
C3 ret
; Total bytes of code 1, prolog size 0 for method Program:Foo()
; ============================================================
After commenting out the AggressiveOptimization attribute:
; Assembly listing for method Program:Foo()
; Emitting BLENDED_CODE for X64 CPU with AVX - Windows
; Tier-0 compilation
; compiler->opts.MinOpts() is true
; rbp based frame
; partially interruptible
; Final local variable assignments
;
; V00 OutArgs [V00 ] ( 1, 1 ) lclBlk (32) [rsp+0x00] "OutgoingArgSpace"
;
; Lcl frame size = 32
G_M16030_IG01:
55 push rbp
4883EC20 sub rsp, 32
488D6C2420 lea rbp, [rsp+20H]
G_M16030_IG02:
E88956FFFF call Program:Foo2()
90 nop
G_M16030_IG03:
488D6500 lea rsp, [rbp]
5D pop rbp
C3 ret
; Total bytes of code 22, prolog size 10 for method Program:Foo()
; ============================================================
@EgorBo, can you post a sample?
@kouvel hm.. can't reproduce it anymore.
In my code I had something like
typeof(Foo).GetMethods().ToList().ForEach(m => RuntimeHelpers.PrepareMethod(m.MethodHandle));
and still had tier0 output but now everything works fine... maybe I did something wrong. Sorry for bothering you.
_(I am creating an add-in for VS2019 to automate it: https://github.com/EgorBo/Disasmo)_
That add-in is pretty cool, I would use that a lot, thanks!
Although it's not an issue now, AggressiveOptimization is not guaranteed to always generate optimized code immediately, that may change in the future. There may not even be one version of best optimized code, for instance AggressiveOptimization may start with an instrumented version of code and cause the method to then transition to a version of code that is optimized based on profile data. @EgorBo I imagine such a change would break your scenario what you may want is one (unprofiled) version of optimized code, perhaps a stable way of getting that is to disable tiered compilation and get the disasm output out-of-proc with env var COMPlus_TieredCompilation=0. CC @noahfalk @AndyAyersMS
Most helpful comment
Currently it prepares the tier 0 version of the code by either loading pregenerated code or by jitting it at tier 0. Tier 1 jitting usually happens in a background thread, so it would not occur in a constrained execution region. If the intention is to optimize the method as quickly as possible, it may be a better option to attribute the method with
[MethodImpl(MethodImplOptions.AggressiveOptimization)], which currently would skip tier 0 for the method, then withPrepareMethodit would currently prepare the optimized version of the code. In the future though, the attribute may not immediately generate fully optimized code but it would try to optimize it as quickly as possible.