前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >.NET9 AOT的性能优化

.NET9 AOT的性能优化

作者头像
JusterZhu
发布2025-01-23 21:04:29
发布2025-01-23 21:04:29
8900
代码可运行
举报
文章被收录于专栏:JusterZhuJusterZhu
运行总次数:0
代码可运行

前言

.NET9里面重要的一个优化是对于AOT预编译的内联优化,这种优化较高的提升了AOT运行的性能。本篇看下这种优化技术。

AOT优化概述

优化从来都不是简单的去掉几行代码或者改动几个机器码就行了,需要统筹考虑,以AOT优化来参考说明。

.NET9里面AOT的优化主要聚焦于内联上面。内联优化虽然提高了AOT后程序运行的速度,但会膨胀二进制可执行文件的体积。这个体积太大,程序开发者肯定难以忍受。所以这中间需要做个平衡性的处理,也即是在体积能够膨胀适度的情况下,最大限度化的提升程序运行性能。

以防部分小伙伴不知道内联是什么,这里简单的说明下。当一个函数(设若函数ABC)频繁的调用另外一个函数(设若函数DEF),调用超过了一定的次数,编译器会发现这种调用热点(次数),会把DEF函数的代码放入到ABC函数里面去,让这两个函数形成一个函数ABC,且删除原有的DEF函数。

这里为了较明白的说清楚内联,部分编译器的操作如下

内联之前:

代码语言:javascript
代码运行次数:0
复制
void DEF()
{
   int x=0x10;
}
void ABC()
{
     DEF();
}

内联之后:

代码语言:javascript
代码运行次数:0
复制
void ABC()
{
     int x=0x10;
}

很明显内联之后,编译器直接删除掉DEF函数,把DEF代码移动到了ABC函数里面。

这是一个非常简单的内联,因为少了一个函数,它非常明显的优化是避免帧寄存器(RBP)和栈寄存器(RSP)频繁的出入栈和保存导致的额外开销操作,用以提高性能。实际上的更复杂,举个例子比如在一些编译器中,发现DEF函数里面的int变量x并没有做任何事情,激进下的优化直接把变量x也给删除了。

回到正题,上面略微了解下优化的关键点。注意,本篇的AOT的内联优化是直接在编译阶段,无论是否有热点都会一次性的优化到可执行文件二进制的结果。我们下面继续看AOT的内联优化操作。

AOT优化内联点

AOT内联的优化主要有以下几个方面,其一:值类型(只读结构体)的内联。其二:部分泛型的内联。其三:代码少且使用频繁的属性内联。

以下所有演示代码的机器码是AOT后的结果。

1.值类型内联

只读结构体的内联优化,下面代码:

代码语言:javascript
代码运行次数:0
复制
 readonly struct Test  //readonly结构所有成员都是只读的
 {
      public int X { get; }
      public int Y { get; }

      public Test(int x, int y)
      {
          X = x;
          Y = y;
      }

      public int JISuan() => X * X + Y * Y; // 这里内联
}
static void Main()
{
      Test test = new Test(1, 2);
      int jisuan = test.JISuan(); // 直接内联计算
}

AOT之后如下代码变成了一个机器指令,不仅进行了内联,且直接计算出了结果赋值给了jisuan这个变量。非常精简,性能自然不用多说,杠杠的。

代码语言:javascript
代码运行次数:0
复制
代码:int jisuan = test.JISuan();  变成了如下机器码:

优化后的ASM码:mov ecx,5

注意看,以上代码为什么不把只读结构及其字段,函数在for循环里的内联,只内联部分。如果全部内联ILC(AOT编译器)的压力确实小了,因为它不需要对代码进行判断,直接内联即可。这个问题,涉及到本篇开头提到的在性能和体积之间的平衡。只内联运行的部分,而无关紧要的则直接剔除,既保证了速度又保证了体积的适度。

2.部分泛型的内联

List泛型内联的操作

代码语言:javascript
代码运行次数:0
复制
var list = new List<int>();
for (int i = 0; i < 10; i++)
{
    list.Add(i); // 对 List<T>.Add 的调用在 AOT 下可内联
}

for循环部分list.Add(i),常规的运行是先进行i变量自增,判断i变量是否小于10,如果是则调用list.Add函数把i变量放入到list里面去。而内联优化之后代码如下,可以看到是非常精炼的部分。完全摒弃了Add函数的调用,把Add函数的代码放入到for循环进行了内联。

代码语言:javascript
代码运行次数:0
复制
loop:
     mov  dword ptr [rdi+esi*4+10h],esi  //把变量i放入到list
     inc  esi           //索引i进行了自增 
     cmp  esi,0Ah       //如果索引i小于10
     jl   loop          //继续跳转到loop自增i变量加入到到list  

另外一个例子就是Span泛型的的操作:

代码语言:javascript
代码运行次数:0
复制
 int[] array = { 1, 2, 3, 4, 5 };
 var span = new Span<int>(array);
 var slice = span.Slice(1, 3); // Span<T>.Slice 内联优化

内联后的代码,通过xmm寄存器操作,取出数组前四个元素到xmm里,后面再进行了一次取元素,最后定位到array数组元素1所在的地址

代码语言:javascript
代码运行次数:0
复制
lea         rcx,[AOT_Private]    //array数组地址  
movups      xmm0,xmmword ptr [rcx]   //取array数组前四个元素到xmm寄存器,因xmm一次性最多16字节,所以下面还需要取一次array元素 
movups      xmmword ptr [rax+10h],xmm0   //把前四个元素放入到内存
mov         edx,dword ptr [rcx+10h]   //取array数组的第五个元素
mov         dword ptr [rax+20h],edx  //放到内存
add         rax,10h      //array数组地址
lea         rbx,[rax+4]  //取array数组的第一个元素的地址

以上简单来说就是进行了如下优化,直接内联了Slice函数,非常明显的看到了优化的凝练:

代码语言:javascript
代码运行次数:0
复制
代码:var slice = span.Slice(1, 3);

内联优化后的ASM代码:lea rbx,[rax+4]  //rbx即指向span第一个元素地址

3,属性内联

代码语言:javascript
代码运行次数:0
复制
 class JiShu
 {
    private int _jishu;
    public int Jishu 
    {
        get => _jishu; // 属性的 get 方法
        set => _jishu = value; // 属性的 set 方法
    }
    public void Add() => _jishu++; // 调用 get 和 set
}
static void Main()
{
  var jishu= new JiShu();
  for (int i = 0; i < 5; i++)
  {
      jishu.Add(); // get 和 set 方法在此被频繁调用
  }
}

因为频繁对属性进行操作,jishu.Add()函数被for循环内联后的代码如下,摒弃了jishu.Add()函数,把其代码放入到for循环进行了内联。循环计数,非常简洁

代码语言:javascript
代码运行次数:0
复制
loop:
   mov  eax,dword ptr [rsp+30h] //获取_jishu的值
   inc  eax                     //每循环一次自增1
   mov  dword ptr [rsp+30h],eax //把值赋给其地址
   dec  ecx                     //把索引自减1,注意索引是从5减到0为止
   jne  loop                    //如果索引不等于0,继续循环

结尾

AOT优化通过只读结构体,部分泛型,以及属性这三种语法的内联优化,进行了AOT性能的提升。优化之后的代码,凸显了可见性的精简和凝练。

这依然只是部分优化,可以预见后续的.NET10,11,12等等在AOT上有更大性能的提升。

以上就是本篇内容,欢迎点赞,关注。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JusterZhu 微信公众号,前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • AOT优化概述
  • AOT优化内联点
  • 结尾
  • AOT优化通过只读结构体,部分泛型,以及属性这三种语法的内联优化,进行了AOT性能的提升。优化之后的代码,凸显了可见性的精简和凝练。
  • 这依然只是部分优化,可以预见后续的.NET10,11,12等等在AOT上有更大性能的提升。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档