前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET9 Pre3 CLR的优化细节

.NET9 Pre3 CLR的优化细节

作者头像
江湖评谈
发布2024-04-18 20:10:38
550
发布2024-04-18 20:10:38
举报
文章被收录于专栏:天下风云天下风云

前言

上一篇:.NET9 Pre3 CLR的改进,讲了下.NET9 Pre3的CLR改进,第二个改进点即是内联。本篇详细看下,略有繁芜。

内联

Pre3里面,哪些代码可以内联优化呢?诸如以下这种:

代码语言:javascript
复制
typeof(T) == obj.GetType()
typeof(T) == typeof(T)
typeof(T) == null
obj1.GetType() == obj2.GetType()

比如其中的typeof,如果出现以下代码:

代码语言:javascript
复制
public static bool Callee<T>() => typeof(T) == typeof(int);

.NET9 Pre3之前呢?typeof函数它实际上是调用了System.Type.GetTypeFromHandle获取到类型,通过System.Type.op_Equality进行类型比较。则比较麻烦。

这里的优化是在JIT层面进行的,如果JIT检测到GetTypeFromHandle和op_Equality标记了Intrincis,则在JIT构建IR的时候,就把这个结果判断出来。

代码语言:javascript
复制
Importing BB01 (PC=000) of 'ConsoleApp3.Program:Callee[int]():ubyte'
    [ 0]   0 (0x000) ldtoken
    [ 1]   5 (0x005) call 0A00000E
In Compiler::impImportCall: opcode is call, kind=0, callRetType is ref, structSize is 0
Named Intrinsic System.Type.GetTypeFromHandle: Recognized

    [ 1]  10 (0x00a) ldtoken
    [ 2]  15 (0x00f) call 0A00000E
In Compiler::impImportCall: opcode is call, kind=0, callRetType is ref, structSize is 0
Named Intrinsic System.Type.GetTypeFromHandle: Recognized

    [ 2]  20 (0x014) call 0A00000F
In Compiler::impImportCall: opcode is call, kind=0, callRetType is ubyte, structSize is 0
Named Intrinsic System.Type.op_Equality: Recognized
Importing Type.op_*Equality intrinsic

Folding call to Type:op_Equality to a simple compare via EQ
Optimizing compare of types-from-handles to instead compare handles
Asking runtime to compare 00007FF89EF9FC30 (System.Int32) and 00007FF89EF9FC30 (System.Int32) for equality
Runtime reports comparison is known at jit time: 0

    [ 1]  25 (0x019) ret

STMT00000 ( 0x000[E-] ... ??? )
               [000008] -----------                         *  RETURN    int
               [000007] -----------                         \--*  CNS_INT   int    1

上面很清晰的看到在JIT Compile的时候就已经判断出了返回的值是0(false)。JIT判断代码:

代码语言:javascript
复制
const bool typesAreEqual = (s == TypeCompareState::Must);
const bool operatorIsEQ  = (oper == GT_EQ);
const int  compareResult = operatorIsEQ ^ typesAreEqual ? 0 : 1;
JITDUMP("Runtime reports comparison is known at jit time: %u\n", compareResult);
GenTree* result = gtNewIconNode(compareResult);                
return result;

减少IR的代码,即如果泛型T与int同类型,则返回true,否则false。而不需要等到JIT生成机器码的时候,生成繁杂的GetTypeFromHandle和op_Equality机器码。

代码语言:javascript
复制
push    rbp
mov     rbp,rsp
mov     eax,1 //类型相同为1,不同为0
pop     rbp
ret

未优化前的:

代码语言:javascript
复制
ConsoleApp3.dll!ConsoleApp3.Program.Callee<T>():
//此处省略,便于观看
00007FF8BCAE22C3 FF 15 07 2C F4 FF    call        qword ptr [System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)+08h (07FF8BCA24ED0h)]  
00007FF8BCAE22C9 48 89 45 E8          mov         qword ptr [rbp-18h],rax  
00007FF8BCAE22CD 48 B9 88 11 A5 BC F8 7F 00 00 mov         rcx,7FF8BCA51188h  
00007FF8BCAE22D7 E8 24 14 AE 5F       call        00007FF91C5C3700  
00007FF8BCAE22DC 48 89 45 E0          mov         qword ptr [rbp-20h],rax  
00007FF8BCAE22E0 48 8B 4D E0          mov         rcx,qword ptr [rbp-20h]  
00007FF8BCAE22E4 FF 15 E6 2B F4 FF    call        qword ptr [System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)+08h (07FF8BCA24ED0h)]  
00007FF8BCAE22EA 48 89 45 D8          mov         qword ptr [rbp-28h],rax  
00007FF8BCAE22EE 48 8B 4D E8          mov         rcx,qword ptr [rbp-18h]  
00007FF8BCAE22F2 48 8B 55 D8          mov         rdx,qword ptr [rbp-28h]  
00007FF8BCAE22F6 FF 15 84 3C F4 FF    call        qword ptr [System.Type.op_Equality(System.Type, System.Type)+08h (07FF8BCA25F80h)]  
//此处省略,便于观看
00007FF8BCAE230B C3                   ret

未优化前,即使是删除了部分代码,依然是比较繁杂。

代码

代码(注意观察需要在Debug模式下开启Debuggable特性,也可以C# Code Relase放到CLR里面),参考:.NET9极致性能CLR操控MSIL(分层编译)

代码语言:javascript
复制
可以加特性[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
或者dotnet build -c relase 
namespace ConsoleApp3
{
    public class Program
    {
        public static bool Test<T>() => Callee<T>();
        public static bool Callee<T>() => typeof(T) == typeof(int);
        static void Main(string[] args)
        {
            Test<int>();
            Console.ReadLine();
        }
    }
}

原理

  • 通过pMethod->GetMethodSignature().GetToken()获取方法Token
  • 获取到System.Runtime.CompilerServices.IntrinsicAttribute这个特性
  • 通过Token获取到CustomAttribute的IL表里面的Parent字段值,如果相等则表示Intrincis被设置了。此时就可以进行内联优化了。GetTypeFromHandle和op_Equality函数皆是如此。
本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-04-15,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 江湖评谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 内联
  • 代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com