.Net 5性能改进
起因
在.Net?Core跳過4.0,避免和先.Net?Framework 4.0同名,版本號變為5.0,同時也不在叫.Net?Core改為.Net 5(統一的叫法),先看看官方對.Net版本規劃.
本文主要是根據https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/?翻譯而來.不完全翻譯.順序也有所調整.
從CPU平臺看.Net 5改進
在.Net 5?開始使用Arm64指令集進行性能優化,這對國產飛騰和華為鯤鵬服務器,在性能上是有很大的提升.在有就是國產龍芯處理器開始在.Net?Core 3.1進行支持,不知道在.Net 5正式發布前.龍芯指令集的代碼會不會合并到.Net 5代碼的主干中.(編者朱:這個可能性是沒有了,.NET 6是很有可能的)
從功能上看.Net 5改進
GC
GC對性能的影響還是很大的.是因為GC回收資源的時候會掛起工作線程,只留GC線程清理資源和回收內存,造成程序有短暫的停頓.
如何提高GC性能:
減少內存分配,就能減少GC回收的次數
減少GC線程掛起的時間.讓工作線程一直在執行任務(說白點就是讓工作線程一直處于干活的狀態)
在.Net 5?GC改進:
在Server GC中增加均衡/平衡機制(Balance),給每個GC線程一樣多的工作量(理論上),每個GC線程執行的時間也是一樣的.避免某個GC線程一直在工作,其他GC線程沒有任務可執行.從而縮短GC線程掛起的時間.?有專門說均衡機制的文章https://devblogs.microsoft.com/dotnet/balancing-work-on-gc-threads/文章?
減少?第0代(gen0)和第1代(gen1)回收次數
減少GC掃描靜態數據和減少使用并發鎖
從CoreCLR(c/c++代碼)?部分代碼(如Array.Sort)移植到System.Private.Corelib(C#代碼),這樣的好處,就是代碼復用(CoreCLR和Mono共用一個實現),c#代碼是安全的(相對于c語言,如數組越界等),可以更好的優化C#代碼.
關于GC示例1代碼:
using System; using System.Diagnostics; using System.Threading;class Program {public static void Main(){new Thread(() =>{var a = new int[20];while (true) Array.Sort(a);}) { IsBackground = true }.Start();var sw = new Stopwatch();while (true){sw.Restart();for (int i = 0; i < 10; i++){GC.Collect();Thread.Sleep(15);}Console.WriteLine(sw.Elapsed.TotalSeconds);}} }關于GC示例2代碼:
JIT改進
JIT(即時編譯器,也有人稱實時編譯器).作用就是C#/Vb.Net代碼(編譯后生成IL代碼,CPU是不認識什么是IL代碼的),在運行的時候,JIT生成匯編代碼(或者叫機器指令),再有CPU去執行.
JIT這里有兩個作用:
安全檢查,說C#/VB.Net是安全的語言,第一是編譯的時候,對代碼進行安全檢查.第二是在程序運行的時候,JIT也會進行安全檢查.
生成匯編代碼.
JIT對程序的性能也有很大的比重.所以要求JIT生成性能更高,代碼更少的指令(通常情況下匯編指令越少,性能越高,但不是絕對的,比如使用CPU自帶的指令).
C#和Java跨平臺是都有中間語言的存在(.Net的IL和Java的ByteCode),這里的平臺指CPU架構,CPU架構分為CISC(復雜指令集,代表為X86)和RISC(精簡指令集,代表為ARM和國產龍芯),在JIT將中間語言生成對應的平臺的指令.
示例1:
using System; using BenchmarkDotNet.Attributes;namespace dotnet_perf {public class TestJit{private B[] _array = new B[42];[Benchmark]public int Ctor() => new Span<B>(_array).Length;}class A{}sealed class B : A{} }匯編代碼對比:
.NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT
.NET Core 5.0.0 (CoreCLR 5.0.20.47505, CoreFX 5.0.20.47505), X64 RyuJIT
從上方的匯編代碼對比,發現.Net 5生成的匯編代碼更少,從執行時間來看,.Net 5生成的代碼性能更高.
Intrinsics(內部函數,也有稱內聯函數,這里翻譯為指令)
Intrinsics為什么這里要翻譯為指令,是因為Intrinsics函數都是在指令集,如X86的AVX/SSE等.
說起這個Intrinsics就得說SIMD(Single Instruction Multiple Data,即單指令流多數據流).
代碼:
using System.Numerics; using BenchmarkDotNet.Attributes;namespace App_Pef5 {[DisassemblyDiagnoser(printSource: true)]//[RyuJitX64Job]public class Intrinsics{[Benchmark]public void T1(){double[] op1 = new double[] { 1.0, 2.0, 3.0, 4.0 };double[] op2 = new double[] { 1.0, 2.0, 3.0, 4.0 };double[] result = new double[4];for (int i = 0; i < 10000; i++){var v1 = new Vector<double>(op1, 0);var v2 = new Vector<double>(op2, 0);var v3 = Vector.Add(v1, v2);v3.TryCopyTo(result);}}[Benchmark]public void T2(){double[] op1 = new double[] { 1.0, 2.0, 3.0, 4.0 };double[] op2 = new double[] { 1.0, 2.0, 3.0, 4.0 };double[] result = new double[4];for (int j = 0; j < 10000; j++){for (int i = 0; i < op1.Length; i++){result[i] = op1[i] + op2[i];}}}} }T1函數生成匯編代碼:
; App_Pef5.Intrinsics.T1()push rdipush rsisub rsp,28vzeroupper ; double[] op1 = new double[] { 1.0, 2.0, 3.0, 4.0 }; ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^mov rcx,offset MT_System.Double[]mov edx,4call CORINFO_HELP_NEWARR_1_VCmov rsi,raxmov rcx,14C58332BE0vmovdqu xmm0,xmmword ptr [rcx]vmovdqu xmmword ptr [rsi+10],xmm0vmovdqu xmm0,xmmword ptr [rcx+10]vmovdqu xmmword ptr [rsi+20],xmm0 ; double[] op2 = new double[] { 1.0, 2.0, 3.0, 4.0 }; ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^mov rcx,offset MT_System.Double[]mov edx,4call CORINFO_HELP_NEWARR_1_VCmov rdi,raxmov rcx,14C58332BE0vmovdqu xmm0,xmmword ptr [rcx]vmovdqu xmmword ptr [rdi+10],xmm0vmovdqu xmm0,xmmword ptr [rcx+10]vmovdqu xmmword ptr [rdi+20],xmm0 ; double[] result = new double[4]; ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^mov rcx,offset MT_System.Double[]mov edx,4call CORINFO_HELP_NEWARR_1_VC ; for (int i = 0; i < 10000; i++) ; ^^^^^^^^^xor edx,edx ; var v1 = new Vector<double>(op1, 0); ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ M00_L00:vmovupd ymm0,[rsi+10] ; var v2 = new Vector<double>(op2, 0); ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^vmovupd ymm1,[rdi+10]vaddpd ymm0,ymm0,ymm1 ; v3.TryCopyTo(result); ; ^^^^^^^^^^^^^^^^^^^^^lea rcx,[rax+10]mov r8d,4cmp r8d,4jb short M00_L01vmovupd [rcx],ymm0 M00_L01:inc edxcmp edx,2710jl short M00_L00vzeroupperadd rsp,28pop rsipop rdiret ; Total bytes of code 189T2函數生成匯編代碼:
; App_Pef5.Intrinsics.T2()push rdipush rsisub rsp,28vzeroupper ; double[] op1 = new double[] { 1.0, 2.0, 3.0, 4.0 }; ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^mov rcx,offset MT_System.Double[]mov edx,4call CORINFO_HELP_NEWARR_1_VCmov rsi,raxmov rcx,212ECBD2BE0vmovdqu xmm0,xmmword ptr [rcx]vmovdqu xmmword ptr [rsi+10],xmm0vmovdqu xmm0,xmmword ptr [rcx+10]vmovdqu xmmword ptr [rsi+20],xmm0 ; double[] op2 = new double[] { 1.0, 2.0, 3.0, 4.0 }; ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^mov rcx,offset MT_System.Double[]mov edx,4call CORINFO_HELP_NEWARR_1_VCmov rdi,raxmov rcx,212ECBD2BE0vmovdqu xmm0,xmmword ptr [rcx]vmovdqu xmmword ptr [rdi+10],xmm0vmovdqu xmm0,xmmword ptr [rcx+10]vmovdqu xmmword ptr [rdi+20],xmm0 ; double[] result = new double[4]; ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^mov rcx,offset MT_System.Double[]mov edx,4call CORINFO_HELP_NEWARR_1_VC ; for (int j = 0; j < 10000; j++) ; ^^^^^^^^^xor edx,edx ; for (int i = 0; i < op1.Length; i++) ; ^^^^^^^^^ M00_L00:xor ecx,ecx ; result[i] = op1[i] + op2[i]; ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ M00_L01:movsxd r8,ecxvmovsd xmm0,qword ptr [rsi+r8*8+10]vaddsd xmm0,xmm0,qword ptr [rdi+r8*8+10]vmovsd qword ptr [rax+r8*8+10],xmm0inc ecxcmp ecx,4jl short M00_L01inc edxcmp edx,2710jl short M00_L00add rsp,28pop rsipop rdiret ; Total bytes of code 185使用intrinsics指令,單次并不會帶來性能的提升,需要在多次使用的時候,才能帶來更好的性能,因為上面的代碼,是我首次使用intrinsics,后面在去了解C/C++中是如何使用的.在去整體對比性能.
從細節上看有哪些改進
更快的加載程序集,在.Net?Core時,程序集被拆分的很多且很小的,加載很多很小的是會增加開銷,在.Net 5中通過合并程序集,減少開銷.
更快的數學庫(算法).
改進NaN檢查.生成更小更快的代碼.
SSE和AMD64 (Intrinsics為內部函數)?
改進哈希值
更快的加密,如RSA.
更快的P/Invoke操作,Windows和Linux
更快的reflection emit
更快的I/O操作,
更少的內存分配.
減少一些字符串內存分配
減少一些裝箱操作
刪除一些臨時內存分配
總結
以上是生活随笔為你收集整理的.Net 5性能改进的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: InfluxDB 2.0 之Flux
- 下一篇: Java面试必问JVM调优,那.NET5