.NET8极致性能优化Zeroing

点击上方蓝字 江湖评谈关注我们

fbb85d88c62bd390a4e5f84767008efa.png

前言

除非你使用了SkipLocalsInit这种特性,一般的stackalloc分配的任何栈空间都需要JIT进行清零的操作,Zeroing(归零)优化的是JIT里面生成的清零代码。本篇继续来看下这些极致的优化技术。

示例

来看看一个简单的例子:

public void Constant256() => Use(stackalloc byte[256]);
public void Constant1024() => Use(stackalloc byte[1024]);
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Use(Span<byte> span) { }

详细

.NET7里面Constant256和Constant1024的ASM如下:

; Tests.Constant256()
       push      rbp
       sub       rsp,40
       lea       rbp,[rsp+20]
       xor       eax,eax
       mov       [rbp+10],rax
       mov       [rbp+18],rax
       mov       rax,0A77E4BDA96AD
       mov       [rbp+8],rax
       add       rsp,20
       mov       ecx,10
M00_L00:
       push      0
       push      0
       dec       rcx
       jne       short M00_L00
       sub       rsp,20
       lea       rcx,[rsp+20]
       mov       [rbp+10],rcx
       mov       dword ptr [rbp+18],100
       lea       rcx,[rbp+10]
       call      qword ptr [7FFF3DD37900]; Tests.Use(System.Span`1<Byte>)
       mov       rcx,0A77E4BDA96AD
       cmp       [rbp+8],rcx
       je        short M00_L01
       call      CORINFO_HELP_FAIL_FAST
M00_L01:
       nop
       lea       rsp,[rbp+20]
       pop       rbp
       ret
; Total bytes of code 110


; Tests.Constant1024()
       push      rbp
       sub       rsp,40
       lea       rbp,[rsp+20]
       xor       eax,eax
       mov       [rbp+10],rax
       mov       [rbp+18],rax
       mov       rax,606DD723A061
       mov       [rbp+8],rax
       add       rsp,20
       mov       ecx,40
M00_L00:
       push      0
       push      0
       dec       rcx
       jne       short M00_L00
       sub       rsp,20
       lea       rcx,[rsp+20]
       mov       [rbp+10],rcx
       mov       dword ptr [rbp+18],400
       lea       rcx,[rbp+10]
       call      qword ptr [7FFF3DD47900]; Tests.Use(System.Span`1<Byte>)
       mov       rcx,606DD723A061
       cmp       [rbp+8],rcx
       je        short M00_L01
       call      CORINFO_HELP_FAIL_FAST
M00_L01:
       nop
       lea       rsp,[rbp+20]
       pop       rbp
       ret
; Total bytes of code 110

我们可以看到,在中间,JIT编写了一个清零循环,每次迭代通过将两个8字节的0推入堆栈来清零16字节:

M00_L00:
       push      0
       push      0
       dec       rcx
       jne       short M00_L00

这种循环式的清零,对于性能并不友好,所以.NET8的Constant256如下:

; Tests.Constant256()
       push      rbp
       sub       rsp,40
       vzeroupper
       lea       rbp,[rsp+20]
       xor       eax,eax
       mov       [rbp+10],rax
       mov       [rbp+18],rax
       mov       rax,6281D64D33C3
       mov       [rbp+8],rax
       test      [rsp],esp
       sub       rsp,100
       lea       rcx,[rsp+20]
       vxorps    ymm0,ymm0,ymm0
       vmovdqu   ymmword ptr [rcx],ymm0
       vmovdqu   ymmword ptr [rcx+20],ymm0
       vmovdqu   ymmword ptr [rcx+40],ymm0
       vmovdqu   ymmword ptr [rcx+60],ymm0
       vmovdqu   ymmword ptr [rcx+80],ymm0
       vmovdqu   ymmword ptr [rcx+0A0],ymm0
       vmovdqu   ymmword ptr [rcx+0C0],ymm0
       vmovdqu   ymmword ptr [rcx+0E0],ymm0
       mov       [rbp+10],rcx
       mov       dword ptr [rbp+18],100
       lea       rcx,[rbp+10]
       call      qword ptr [7FFEB7D3F498]; Tests.Use(System.Span`1<Byte>)
       mov       rcx,6281D64D33C3
       cmp       [rbp+8],rcx
       je        short M00_L00
       call      CORINFO_HELP_FAIL_FAST
M00_L00:
       nop
       lea       rsp,[rbp+20]
       pop       rbp
       ret
; Total bytes of code 156

.NET8里面通过ymm0寄存器一次性移动32个字节来进行清零,大大提高性能。

Constant1024在.NET8里面如下:

; Tests.Constant1024()
       push      rbp
       sub       rsp,40
       lea       rbp,[rsp+20]
       xor       eax,eax
       mov       [rbp+10],rax
       mov       [rbp+18],rax
       mov       rax,0CAF12189F783
       mov       [rbp],rax
       test      [rsp],esp
       sub       rsp,400
       lea       rcx,[rsp+20]
       mov       [rbp+8],rcx
       xor       edx,edx
       mov       r8d,400
       call      CORINFO_HELP_MEMSET
       mov       rcx,[rbp+8]
       mov       [rbp+10],rcx
       mov       dword ptr [rbp+18],400
       lea       rcx,[rbp+10]
       call      qword ptr [7FFEB7D5F498]; Tests.Use(System.Span`1<Byte>)
       mov       rcx,0CAF12189F783
       cmp       [rbp],rcx
       je        short M00_L00
       call      CORINFO_HELP_FAIL_FAST
M00_L00:
       nop
       lea       rsp,[rbp+20]
       pop       rbp
       ret
; Total bytes of code 119

也没有用循环清零的方式,而是 通过CORINFO_HELP_MEMSET帮助函数来进行清零

以上两种.NET8里面提高性能的方式如何呢?我们测试如下结果:

MethodRuntimeMeanRatio
Constant256.NET 7.07.927 ns1.00
Constant256.NET 8.03.181 ns0.40
Constant1024.NET 7.030.523 ns1.00
Constant1024.NET 8.08.850 ns0.29

可以看到.NET8相对于.NET7提升了两到三倍的性能

是不是到这里就优化的可以了呢?并不是,我们还可以继续优化,通过AVX512的zmm0寄存器一次性清64个字节的零。

; Tests.Constant256()
       push      rbp
       sub       rsp,40
       vzeroupper
       lea       rbp,[rsp+20]
       xor       eax,eax
       mov       [rbp+10],rax
       mov       [rbp+18],rax
       mov       rax,992482B435F7
       mov       [rbp+8],rax
       test      [rsp],esp
       sub       rsp,100
       lea       rcx,[rsp+20]
       vxorps    ymm0,ymm0,ymm0
       vmovdqu32 [rcx],zmm0
       vmovdqu32 [rcx+40],zmm0
       vmovdqu32 [rcx+80],zmm0
       vmovdqu32 [rcx+0C0],zmm0
       mov       [rbp+10],rcx
       mov       dword ptr [rbp+18],100
       lea       rcx,[rbp+10]
       call      qword ptr [7FFCE555F4B0]; Tests.Use(System.Span`1<Byte>)
       mov       rcx,992482B435F7
       cmp       [rbp+8],rcx
       je        short M00_L00
       call      CORINFO_HELP_FAIL_FAST
M00_L00:
       nop
       lea       rsp,[rbp+20]
       pop       rbp
       ret
; Total bytes of code 132
; Tests.Use(System.Span`1<Byte>)
       ret
; Total bytes of code 1

现在,请注意,我们不再看到使用ymm0的八个vmovdqu指令,而是看到使用zmm0的四个vmovdqu32指令,因为每个移动指令能够清零两倍的内容,每个指令一次处理64个字节。性能进一步提升了。

往期精彩回顾:

.NET8 JIT核心:分层编译的原理

新版.Net性能有没有达到C++90%?

面试官问.Net对象赋值为null,就会被GC回收吗?

.Net析构函数再论(源码剖析)

CLR Via C#还值得一读吗

.Net面试越来越卷了,未来何处何从

关于作者

从0教你学习.Net核心CLR/JIT技术

骚操作:托管DLL四位.Net创始人名字首字母

.Net JIT的骚操作DNGuard HVM原理简析

aabed3a7772b9b868661611ad69b7dc2.jpeg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值