.NET9 PreView6 RyuJIT代码布局改进(HIR)

文摘   2024-07-11 10:14   湖北  

点击上方蓝字 江湖评谈设为关注/星标




前言

7月9日微软发布了.NET9 PreView6,本篇看下这个版本的CLR(JIT)第一个改进:代码布局改进。

问题

代码的布局改进,是在RyuJIT的HIR(Hight IR)部分,它实际上是对BB(Basick Block)块的顺序改进。

微软早在.NET6就引入了动态PGO的实验,后续的7,8等版本也进行了BB块的顺序改进,但是BB块的顺序如同顽疾始终受限于代码流程图的的控制,这点一直没变。为了改变这个现状,在过去的几个月围绕重构了 RyuJIT 的流程图数据结构,以消除围绕区块排序的各种限制。

非官方解决

一般来说,代码的流水线指令按照顺序执行。比如执行完前一行代码,后一行继续执行。这样会产生两个概念前任(preds)和继任(succs)但是有的代码,比如if分支这种类型的代码,它的后一行代码不一定是它需要执行的代码,通过if判断跳转,可能跳转到很多行之后去执行。而优化的地点就在这里。

看一个例子(注意这个例子取自官网,但是多了一行代码:Console.ReadLine()所以分析过程会不太一样

var random = new Random();int number = random.Next(100);
if (number < 99){   Console.WriteLine("We're in BB02");}else{   Console.WriteLine("We're in BB03");}Console.WriteLine("We're in BB04 -- the successor of BB02 and BB03");Console.ReadLine();

.NET9 RyuJIT HIR会把以上代码生成五个BB块。C#伪代码如下:

// bb01 JIT植入的调试代码:CORINFO_HELP_DBG_IS_JUST_MY_CODE// bb02var random = new Random();int number = random.Next(100);// bb03if (number < 99){   Console.WriteLine("We're in BB02");}// bb04else{   Console.WriteLine("We're in BB03");}// bb05Console.WriteLine("We're in BB04 -- the successor of BB02 and BB03");Console.ReadLine();

BB01是JIT植入的调试代码,它的preds和succs如下,这里可以忽略。

------------ BB01 [0000] [???..???) -> BB02(1) (always), preds={} succs={BB02}

BB02即是初始化随机数,可以看到它有两个分支。

------------ BB02 [0001] [000..019) -> BB04(0.5),BB03(0.5) (cond), preds={BB01} succs={BB03,BB04}

BB03

------------ BB03 [0002] [019..028) -> BB05(1) (always), preds={BB02} succs={BB05}

BB04

------------ BB04 [0003] [028..035) -> BB05(1) (always), preds={BB02} succs={BB05}

BB05

------------ BB05 [0004] [035..047) (return), preds={BB03,BB04} succs={}

没有优化的代码的流水线执行路径如下:

BB01->BB02->BB03-BB04->BB05

但加入了if分支,则变成了如下,其中变化的BB03/4是if判断里面的内容

BB01->BB02->BB03->BB05BB01->BB02->BB04->BB05

优化点就在这里,JIT会对执行的热点(执行次数较多的代码块)进行排序,假如BB03的热点大于BB04,则可以如下流水执行。注意它这里BB04移动到BB05后面去了,是因为4根本没有执行,所以它的热点在某时刻基本为零。

BB01->BB02->BB03->BB05->-BB04

反之呢?因为是随机数,如果BB04的热度超过了BB03,则可如下动态优化

BB01->BB02->BB04->BB05->-BB03

BB块

看下热点BB块3/4的实际情况

1.BB03

------------ BB03 [0002] [019..028) -> BB05(1) (always), preds={BB02} succs={BB05}
***** BB03 [0002]STMT00018 ( 0x019[E-] ... 0x019 ) [000036] -----+----- * NO_OP void
***** BB03 [0002]STMT00019 ( 0x01A[E-] ... 0x024 ) [000038] --CXG+----- * CALL void System.Console:WriteLine(System.String) [000058] H----+----- arg0 in rcx \--* CNS_INT(h) ref 'We're in BB02'
***** BB03 [0002]STMT00020 ( 0x024[E-] ... ??? ) [000039] -----+----- * NO_OP void
***** BB03 [0002]STMT00021 ( 0x025[E-] ... 0x025 ) [000040] -----+----- * NO_OP void
***** BB03 [0002]STMT00022 ( 0x026[E-] ... 0x026 ) [000041] -----+----- * NOP void

2.BB04

------------ BB04 [0003] [028..035) -> BB05(1) (always), preds={BB02} succs={BB05}
***** BB04 [0003]STMT00009 ( 0x028[E-] ... 0x028 ) [000023] -----+----- * NO_OP void
***** BB04 [0003]STMT00010 ( 0x029[E-] ... 0x033 ) [000025] --CXG+----- * CALL void System.Console:WriteLine(System.String) [000059] H----+----- arg0 in rcx \--* CNS_INT(h) ref 'We're in BB03'
***** BB04 [0003]STMT00011 ( 0x033[E-] ... ??? ) [000026] -----+----- * NO_OP void
***** BB04 [0003]STMT00012 ( 0x034[E-] ... 0x034 ) [000027] -----+----- * NO_OP void

结尾

.NET9 PreView6的这次优化,实际上是对一些优化方方面的硬骨头进行了敲碎,然后重新组织,配合PGO则是性能的另一大提升。当然它的优化远不止于此,其它的诸如,ARM64 代码生成代码布局循环优化减少地址暴露AVX10v1 支持硬件内部代码生成用于浮点和 SIMD 运算的恒定折叠等,后续继续关注下。

往期精彩回顾

NET9 PreView5张量和AI算力


江湖评谈
记录,分享,自由。
 最新文章