起因

在.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性能:

  1. 减少内存分配,就能减少GC回收的次数

  2. 减少GC线程挂起的时间.让工作线程一直在执行任务(说白点就是让工作线程一直处于干活的状态)

在.Net 5 GC改进:

  1. 在Server GC中增加均衡/平衡机制(Balance),给每个GC线程一样多的工作量(理论上),每个GC线程执行的时间也是一样的.避免某个GC线程一直在工作,其他GC线程没有任务可执行.从而缩短GC线程挂起的时间. 有专门说均衡机制的文章https://devblogs.microsoft.com/dotnet/balancing-work-on-gc-threads/文章

  2. 减少 第0代(gen0)和第1代(gen1)回收次数

  3. 减少GC扫描静态数据和减少使用并发锁

  4. 从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代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Running;namespace dotnet_perf
{public class DoubleSorting : Sorting<double>{protected override double GetNext() => _random.Next();}public class Int32Sorting : Sorting<int>{protected override int GetNext() => _random.Next();}public class StringSorting : Sorting<string>{protected override string GetNext(){var dest = new char[_random.Next(1, 5)];for (int i = 0; i < dest.Length; i++) dest[i] = (char)('a' + _random.Next(26));return new string(dest);}}public abstract class Sorting<T>{protected Random _random;private T[] _orig, _array;[Params(10)]public int Size { get; set; }protected abstract T GetNext();[GlobalSetup]public void Setup(){_random = new Random(42);_orig = Enumerable.Range(0, Size).Select(_ => GetNext()).ToArray();_array = (T[])_orig.Clone();Array.Sort(_array);}[Benchmark]public void Random(){_orig.AsSpan().CopyTo(_array);Array.Sort(_array);}}
}

JIT改进

JIT(即时编译器,也有人称实时编译器).作用就是C#/Vb.Net代码(编译后生成IL代码,CPU是不认识什么是IL代码的),在运行的时候,JIT生成汇编代码(或者叫机器指令),再有CPU去执行.

JIT这里有两个作用:

  1. 安全检查,说C#/VB.Net是安全的语言,第一是编译的时候,对代码进行安全检查.第二是在程序运行的时候,JIT也会进行安全检查.

  2. 生成汇编代码.

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

; dotnet_perf.TestJit.Ctor()
;         public int Ctor() => new Span<B>(_array).Length;
;                              ^^^^^^^^^^^^^^^^^^^^^^^^^^push      rdipush      rsisub       rsp,28mov       rsi,[rcx+8]test      rsi,rsijne       short M00_L00xor       eax,eaxjmp       short M00_L01
M00_L00:mov       rcx,rsicall      00007FF884C41F50mov       rdi,raxmov       rcx,7FF82531DEAAcall      CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEcmp       rdi,raxjne       short M00_L02mov       eax,[rsi+8]
M00_L01:add       rsp,28pop       rsipop       rdiret
M00_L02:call      System.ThrowHelper.ThrowArrayTypeMismatchException()int       3
; Total bytes of code 66

.NET Core 5.0.0 (CoreCLR 5.0.20.47505, CoreFX 5.0.20.47505), X64 RyuJIT

; dotnet_perf.TestJit.Ctor()
;         public int Ctor() => new Span<B>(_array).Length;
;                              ^^^^^^^^^^^^^^^^^^^^^^^^^^mov       rax,[rcx+8]test      rax,raxjne       short M00_L00xor       eax,eaxjmp       short M00_L01
M00_L00:mov       eax,[rax+8]
M00_L01:ret
; Total bytes of code 17

从上方的汇编代码对比,发现.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 189

T2函数生成汇编代码:

; 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中通过合并程序集,减少开销.

  • 更快的数学库(算法).

  1. 改进NaN检查.生成更小更快的代码.

  2. SSE和AMD64 (Intrinsics为内部函数)

  3. 改进哈希值

  • 更快的加密,如RSA.

  • 更快的P/Invoke操作,Windows和Linux

  • 更快的reflection emit

  • 更快的I/O操作,

  • 更少的内存分配.

    1. 减少一些字符串内存分配

    2. 减少一些装箱操作

    3. 删除一些临时内存分配

.Net 5性能改进相关推荐

  1. .NET 3.5 中WCF客户端代理性能改进以及最佳实践

    介绍 在.NET 3.0 SP1(与.NET 3.5一起发布) 中,WCF客户端创建有一个重要的性能改进.对BasicHttpBinding 来说,性能已经接近于创建ASMX代理. ASMX 代理 v ...

  2. PowerShell Core 6.2 发布,侧重于性能改进

    百度智能云 云生态狂欢季 热门云产品1折起>>>   PowerShell Core 6.2 GA 已发布,PowerShell Core 是 PowerShell 的开源版本,适用 ...

  3. Windows 7 SP1确实将有性能改进

    虽然微软一再刻意淡化Windows 7 SP1的重要性,但这都阻挡不了我们对新系统第一个服务包的期待,而最新消息显示它确实也会带来一定程度的性能改进.微软此前曾表示,Windows 7 SP1只是一次 ...

  4. Qt 5.12 LTS(长期维护版本)中Qt Quick的性能改进

    我们一直致力于提高Qt的性能和优化其内存消耗.Qt 5.12的一个重点关注是在于减少QML引擎的内存消耗和优化JavaScript性能. 与上一个长期支持版Qt 5.6 LTS相比,Qt 5.9 LT ...

  5. Entity Framework Core 6.0 预览4 性能改进

    起因 微软在Build2021开发者大会上,发布Entity Framework Core 6.0(简称EFCore 6)预览第四版,号称是性能版本,性能提升主要对于Entity Framework  ...

  6. .NET 5 中的正则引擎性能改进(翻译)

    前言 System.Text.RegularExpressions 命名空间已经在 .NET 中使用了多年,一直追溯到 .NET Framework 1.1.它在 .NET 实施本身的数百个位置中使用 ...

  7. java8hashmap_Java 8中的HashMap性能改进

    java8hashmap HashMap<K, V>是每个Java程序中快速,通用且无处不在的数据结构. 首先是一些基础知识. 您可能知道,它使用键的hashCode()和equals() ...

  8. Java 8中的HashMap性能改进

    HashMap<K, V>是每个Java程序中快速,通用且无处不在的数据结构. 首先是一些基础知识. 您可能知道,它使用键的hashCode()和equals()方法在存储桶之间拆分值. ...

  9. 【译】.NET 7 中的性能改进(十一)

    ▲ 点击上方"DotNet NB"关注公众号 回复"1"获取开发者路线图 学习分享 丨作者 / 郑 子 铭 这是DotNet NB 公众号的第215篇原创文章 ...

  10. .NET获取枚举DescriptionAttribute描述信息性能改进的多种方法

    原文:.NET获取枚举DescriptionAttribute描述信息性能改进的多种方法 一. DescriptionAttribute的普通使用方式 1.1 使用示例 DescriptionAttr ...

最新文章

  1. 【机器学习】KNN算法代码练习
  2. 导出Excel神器最终版
  3. 修改Tomcat编码方式的两种方法
  4. python银行家算法_Linux 死锁概念与银行家算法python 实现
  5. c语言贪心算法合并箭,贪心算法:用最少数量的箭引爆气球
  6. 编辑器、编译器和IDE的区别
  7. linux环境配置以及远程登录linux
  8. Linux安装SSH
  9. 流量控制系统pid整定方法仿真
  10. Teamviewer 更改ID
  11. 论学习过程中“结构化”的思维必要作用
  12. java线程之可重入锁
  13. Python爬虫下一代网络请求库httpx和parsel解析库测评
  14. adb指令禁用软件_MIUI系统adb指令禁用系统应用
  15. 鸿蒙系统三大主要功能,全球第三大手机系统「鸿蒙」上线,这19款手机能抢先用...
  16. 外卖跑腿小程序APP定制开发扫码点餐配送
  17. Ansys Speos | 联合 optiSLang 背光板设计优化方案
  18. 【小技巧】如何安装下载MATLAB最新的工具箱
  19. 优化%和*操作的效率(星网锐捷笔试题)
  20. Nuxtjs上使用wow.js+animate.css实现滚动加载动画

热门文章

  1. el表达式 if 和 if else 的写法
  2. shell脚本--cut命令
  3. WPF里面的常用笔刷
  4. Ext4.2文件目录及页面默认导入文件
  5. SQL Azure Reporting CTP
  6. twitter推文不收录_如何使用Twitter书签保存推文供以后使用
  7. 亚马逊的vps多少钱一个月_如何查看您在亚马逊上花了多少钱
  8. mac屏幕截图_如何在Mac上拍摄屏幕截图
  9. JAVA-MyBatis ORM
  10. 如何使用VIM的Help