.NET8极致性能优化CHRL
前言
.NET8在.NET7的基礎(chǔ)上進行了進一步的優(yōu)化,比如CHRL(全稱:CORINFO_HELP_RNGCHKFAIL)優(yōu)化技術(shù),CORINFO_HELP_RNGCHKFAIL是邊界檢查,在.NET7里面它已經(jīng)進行了部分優(yōu)化,但是.NET8里面它繼續(xù)優(yōu)化,類似人工智能,.NET8能意識到某些性能問題,從而進行優(yōu)化。本篇來看下
概述
JIT會對數(shù)組,字符串的范圍邊界進行檢查。比如數(shù)組的索引是否在數(shù)組長度范圍內(nèi),不能超過。所以JIT就會產(chǎn)生邊界檢查的步驟。
public class Tests
{
private byte[] _array = new byte[8];
private int _index = 4;
public void Get() => Get(_array, _index);
[MethodImpl(MethodImplOptions.NoInlining)]
private static byte Get(byte[] array, int index) => array[index];
}
Get函數(shù).NET7的ASM如下:
; Tests.Get(Byte[], Int32)
sub rsp,28
cmp edx,[rcx+8]
jae short M01_L00
mov eax,edx
movzx eax,byte ptr [rcx+rax+10]
add rsp,28
ret
M01_L00:
call CORINFO_HELP_RNGCHKFAIL
int 3
cmp指令把數(shù)組的MT(方法表)偏移8位置的數(shù)組長度與當前的數(shù)組索引對比,兩者如果索引大于(后者)或等于(jae)數(shù)組長度(前者)的時候。就會跳轉(zhuǎn)到CORINFO_HELP_RNGCHKFAIL進行邊界檢查,可能會引發(fā)超出引范圍的異常IndexOutOfRangeException。但是實際上這段這段代碼的訪問只需要兩個mov,一個是數(shù)組的索引,一個是(MT(方法表)+0x10+索引)取其值返回即可。所以這個地方有清晰可見的優(yōu)化的地方。
.NET8學習了一些范圍邊界的智能化優(yōu)化,也就說,有的地方不需要邊界檢查,從而把邊界檢查優(yōu)化掉,用以提高代碼的性能。下面例子:
private readonly int[] _array = new int[7];
public int GetBucket() => GetBucket(_array, 42);
private static int GetBucket(int[] buckets, int hashcode) =>
buckets[(uint)hashcode % buckets.Length];
.NET7它的ASM如下:
; Tests.GetBucket()
sub rsp,28
mov rcx,[rcx+8]
mov eax,2A
mov edx,[rcx+8]
mov r8d,edx
xor edx,edx
idiv r8
cmp rdx,r8
jae short M00_L00
mov eax,[rcx+rdx*4+10]
add rsp,28
ret
M00_L00:
call CORINFO_HELP_RNGCHKFAIL
int 3
它依然進行了邊界檢查,然.NET8的JIT能自動識別到(uint)hashcode%buckets.Length這個索引不可能超過數(shù)組的長度也就是buckets.Length。所以.NET8可以省略掉邊界檢查,如下.NET8 ASM
; Tests.GetBucket()
mov rcx,[rcx+8]
mov eax,2A
mov r8d,[rcx+8]
xor edx,edx
div r8
mov eax,[rcx+rdx*4+10]
ret
再看下另外一個例子:
public class Tests
{
private readonly string _s = "\"Hello, World!\"";
public bool IsQuoted() => IsQuoted(_s);
private static bool IsQuoted(string s) =>
s.Length >= 2 && s[0] == '"' && s[^1] == '"';
}
IsQuoted檢查字符串是否至少有兩個字符,并且字符串開頭和結(jié)尾均以引號結(jié)束,s[^1]表示s[s.Length - 1]也就是字符串的長度。.NET7 ASM如下:
; Tests.IsQuoted(System.String)
sub rsp,28
mov eax,[rcx+8]
cmp eax,2
jl short M01_L00
cmp word ptr [rcx+0C],22
jne short M01_L00
lea edx,[rax-1]
cmp edx,eax
jae short M01_L01
mov eax,edx
cmp word ptr [rcx+rax*2+0C],22
sete al
movzx eax,al
add rsp,28
ret
M01_L00:
xor eax,eax
add rsp,28
ret
M01_L01:
call CORINFO_HELP_RNGCHKFAIL
int 3
注意看.NET7的騷操,它實際上進行了邊界檢查,但是只檢查了一個,因為它只有一個jae指令跳轉(zhuǎn)。這是為什么呢?JIT已經(jīng)知道不需要對s[0]進行邊界檢查,因為s.Length >= 2已經(jīng)檢查過了,只要是小于2的索引(因為索引是無符號,沒有負數(shù))都不需要檢查。但是依然對s[s.Length - 1]進行了邊界檢查,所以.NET7雖然也是騷操,但是它這個騷操不夠徹底。
我們來看下徹底騷操的.NET8
; Tests.IsQuoted(System.String)
mov eax,[rcx+8]
cmp eax,2
jl short M01_L00
cmp word ptr [rcx+0C],22
jne short M01_L00
dec eax
cmp word ptr [rcx+rax*2+0C],22
sete al
movzx eax,al
ret
M01_L00:
xor eax,eax
ret
完全沒有了邊界檢查,JIT不僅意識到s[0]是安全的,因為檢查過了s.Length >= 2。因為檢查過了s.Length >= 2,還意識到s.length> s.Length-1 >=1。所以不需要邊界檢查,全給它優(yōu)化掉了。
可以看到.NET8的性能優(yōu)化的極致有多厲害,它基本上榨干了JIT的引擎,讓其進行最大智能化程度的優(yōu)化。
點擊下加入技術(shù)討論群:
歡迎加入.NET技術(shù)交流群
結(jié)尾
作者:江湖評談
歡迎關(guān)注公眾號:jianghupt,文章首發(fā),以及更多高階內(nèi)容分享。
總結(jié)
以上是生活随笔為你收集整理的.NET8极致性能优化CHRL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊分布式 SQL 数据库Doris(九
- 下一篇: Java属性loadFromXML()方