前言

.Net7里面对于基础类型的优化,是必不可少的。因为这些基础类型基本上都会经常用到,本篇除了基础类型的优化介绍之外,还有一个循环克隆的优化特性,也一并看下。

概括

1.基础类型优化

基础类型的优化不会有些不会涉及ASM,主要是记忆。

一:double.Parse和float.Parse,把某数值转换成double或者float类型,这两个Parse进行了优化。

二:bool.TryParse和bool.TryFormat也进行了性能优化。

假如说有以下代码:

destination[0] = 'T';
destination[1] = 'r';
destination[2] = 'u';
destination[3] = 'e';

四次写操作,写入到destination数组。这个可以一次性写入(单个ulong)来进行性能优化,基准不测:

BinaryPrimitives.WriteUInt64LittleEndian(MemoryMarshal.AsBytes(destination), 0x65007500720054); // "True"
0x65007500720054是内存地址,里面存放了四个char的值。 private bool _value = true;
private char[] _chars = new char[] { 'T', 'r', 'u', 'e' }; [Benchmark] public bool ParseTrue() => bool.TryParse(_chars, out _);
[Benchmark] public bool FormatTrue() => _value.TryFormat(_chars, out _);

三:Enum枚举也进行了性能优化

这里主要是二进制算法和线性算法的综合应用,因为当我们执行枚举的一些方法,比如Enum.IsDefined、Enum.GetName或Enum.ToString的时候。它会搜索一些值,这些值也是存储在数组中的,会使用Array.BinarySearch二进制来搜索。涉及到复杂的算法的时候Array.BinarySearch二进制搜索是可以的,但是如果比较简单的算法则用它相当于杀鸡用牛刀,这里就引入了线性搜索:SpanHelpers.IndexOf。那么何时用线性何时用二进制搜索呢?对于小于或等于32个定义值的枚举用线性,大于的用二进制。

代码如下,benchmark这里就不打印出来了

private DayOfWeek[] _days = Enum.GetValues<DayOfWeek>();
[Benchmark]
public bool AllDefined()
{
foreach (DayOfWeek day in _days)
{
if (!Enum.IsDefined(day))
{
return false;
}
} return true;
}

Enums在与Nullable和EqualityComparer.Default的配合下也得到了性能提升,因为EqualityComparer.Default缓存了一个从所有对Default的访问中返回的EqualityComparer实例,EqualityComparer.Default.Equals根据它这个里面的T值来确保了nullable enums被映射到(现有的)Nullable的专门比较器上,并简单地调整了其定义以确保它能与enums很好地配合。

代码如下,Benchmark不测

private DayOfWeek?[] _enums = Enum.GetValues<DayOfWeek>().Select(e => (DayOfWeek?)e).ToArray();
[Benchmark]
[Arguments(DayOfWeek.Saturday)]
public int FindEnum(DayOfWeek value) => IndexOf(_enums, value);
private static int IndexOf<T>(T[] values, T value)
{
for (int i = 0; i < values.Length; i++)
{
if (EqualityComparer<T>.Default.Equals(values[i], value))//这里的T值是IndexOf传过来的,进行的一个性能优化
{
return i;
}
} return -1;
}

四:Guid的优化

Guid实现将数据分成4个32位的值,并进行4个int的比较。如果当前的硬件支持128位SIMD,实现就会将两个Guid的数据加载为两个向量,并简单地进行一次比较。benchmark不测

private Guid _guid1 = Guid.Parse("0aa2511d-251a-4764-b374-4b5e259b6d9a");
private Guid _guid2 = Guid.Parse("0aa2511d-251a-4764-b374-4b5e259b6d9a");
[Benchmark]
public bool GuidEquals() => _guid1 == _guid2;

五:DateTime.Equals的优化

DateTime.Equals,DateTime是用一个单一的ulong _dateData字段实现的。其中大部分位存储了从1/1/0001 12:00am开始的ticks偏移量,每个tick是100纳秒,并且前两个位描述了DateTimeKind。因此,公共的Ticks属性返回_dateData的值,但前两位被屏蔽掉了,例如:_dateData & 0x3FFFFFFFFFFFFFFF。然后,平等运算符只是将一个DateTime的Ticks与其他DateTime的Ticks进行比较,这样我们就可以有效地得到(dt1._dateData & 0x3FFFFFFFFFFF)==(dt2._dateData & 0x3FFFFFFFFFFF)。然而,作为一个微观的优化,可以更有效地表达为((dt1._dateData ^ dt2._dateData) << 2) == 0。

这里其实是一个细微的优化,但是依然可见优化力度。

.Net6
; Program.DateTimeEquals()
mov rax,[rcx+8]
mov rdx,[rcx+10]
mov rcx,0FFFFFFFFFFFF
and rax,rcx
and rdx,rcx
cmp rax,rdx
sete al
movzx eax,al
ret
; Total bytes of code 34
而在.NET 7上则产生。
; Program.DateTimeEquals()
mov rax,[rcx+8]
mov rdx,[rcx+10]
xor rax,rdx
shl rax,2
sete al
movzx eax,al
ret
; Total bytes of code 22 所以我们得到的不是mov、and、and、cmp,而是xor和shl。

其它还有一些DateTime.Day、DateTime.DayOfYear、DateTime.DayOfYear改进性能。

六:数学API的优化

七:System.Formats.Tar压缩文件库的优化

2.循环克隆优化

循环克隆实际上是通过提前判断是否超出数组边界来进行的一个优化,如果没哟超过数组边界,则快速路就,超过了就慢速路径进行数组边界检查。

private int[] _values = Enumerable.Range(0, 1000).ToArray();
[Benchmark]
[Arguments(0, 0, 1000)]
public int LastIndexOf(int arg, int offset, int count)
{
int[] values = _values;
for (int i = offset + count - 1; i >= offset; i--)
if (values[i] == arg)
return i;
return 0;
}

.Net7 ASM

; Program.LastIndexOf(Int32, Int32, Int32)
sub rsp,28
mov rax,[rcx+8]
lea ecx,[r8+r9+0FFFF]
cmp ecx,r8d
jl short M00_L02
test rax,rax
je short M00_L01
test ecx,ecx
jl short M00_L01
test r8d,r8d
jl short M00_L01
cmp [rax+8],ecx
jle short M00_L01
M00_L00:
mov r9d,ecx
cmp [rax+r9*4+10],edx
je short M00_L03
dec ecx
cmp ecx,r8d
jge short M00_L00
jmp short M00_L02
M00_L01:
cmp ecx,[rax+8]
jae short M00_L04
mov r9d,ecx
cmp [rax+r9*4+10],edx
je short M00_L03
dec ecx
cmp ecx,r8d
jge short M00_L01
M00_L02:
xor eax,eax
add rsp,28
ret
M00_L03:
mov eax,ecx
add rsp,28
ret
M00_L04:
call CORINFO_HELP_RNGCHKFAIL
int 3
; Total bytes of code 98

M00_L00快速路径,M00_L01慢速路径,在M00_L00前面进行了一个判断,如果没有超出数组边界以及其它判断,那么就M00_L01不进行,否则M00_L02进行边界检查。

另外还有一个概念是循环提升,这个就另说了。

结尾

作者:江湖评谈

参照:[微软官方博客]

文章首发于公众号【江湖评谈】,欢迎大家关注。

.Net7基础类型的优化和循环克隆优化的更多相关文章

  1. 关于对For循环嵌套优化的问题

    1.案例描述 由于一次Java面试的笔试题,当时没有写出很好的解决方案,特此专门撰写一篇博客来加以记录,方便日后的查看 面试题目如下:从性能上优化如下代码并说明优化理由? for (int i = 0 ...

  2. Android(java)学习笔记195:三重for循环的优化(Java面试题)

    1.首先我们看一段代码: for(int i=0;i<1000;i++){ for(int j=0;j<100;j++){ for(int k=0;k<10;k++){ testFu ...

  3. java总结之基础类型与常量池

    1.基础类型有byte short int long char boolean float double八种. 其中byte short int long char 的包装类型是存放在常量池(用来维护 ...

  4. Android(java)学习笔记138:三重for循环的优化(Java面试题)

    1. 首先我们看一段代码: for(int i=0;i<1000;i++){ for(int j=0;j<100;j++){ for(int k=0;k<10;k++){ testF ...

  5. 关于优化for循环的注意的事项

    for循环注意事项: 1.for循环内部尽量少做数据库查询之类的IO代价大的操作 2.尽量控制for循环的次数,不多做无用功 3.能一次加载在内存中的,就不要通过循环来多次查询数据库,除非数据量过大. ...

  6. Redis基础类型常用操作命令

    Redis基础类型常用操作命令 概念:Redis是用C语言开发的一个开源的高性能键值对数据库. 特征: 数据间没有必然的联系 内部采用单线程机制进行工作 高性能 多数据类型支持 字符串类型 Strin ...

  7. 【CUDA 基础】3.5 展开循环

    title: [CUDA 基础]3.5 展开循环 categories: - CUDA - Freshman tags: - 展开归约 - 归约 - 模板函数 toc: true date: 2018 ...

  8. Python的基础类型(int,bool,str):

    Python的基础类型(int,bool,str): 1.int -------> 整形:主要用力进行数字计算 2.string ------>字符串:可以保存少量数据并进行相关的操作 3 ...

  9. salesforce 零基础学习(五十八)通过sObject的field返回其对应的基础类型

    项目中有时候会要求通过sObject的Field的type类型返回其对应的基本类型,然后对其进行相关的处理,创建sObject的field可以选择的type类型是固定多的. 上述类型可以转换成几种基本 ...

  10. TypeScript 素描-基础类型

    博文读自 TypeScript 官方文档而来,不具有学习性,仅是本人学习时记录以供日后翻阅 ,有学习TypeScript的朋友还请去看更为详细的官方文档 TypeScript官网文档中的基础类型, T ...

随机推荐

  1. Let和Const区别,详细版本

    let:声明的是变量1.不存在变量提升 // var 的情况 console.log(foo); // 输出undefined var foo = 2; // let 的情况 console.log( ...

  2. 存储论——经济订货批量的R实现

    存储论又称库存理论,是运筹学中发展较早的分支.早在 1915 年,哈李斯(F.Harris)针对银行货币的储备问题进行了详细的研究,建立了一个确定性的存贮费用模型,并求得了最佳批量公式.1934 年威 ...

  3. python入门教程之十七进程、线程和协程

    进程 要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识. Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次 ...

  4. python实现微信自动发消息功能

    import timeimport uiautomation as autofrom uiautomation.uiautomation import Bitmapimport win32clipbo ...

  5. 四月十一号Java基础知识

    1.下列格式调用JAVA语言定义的方法:字符串变量名.方法名():2.由键盘输入多个数据普通格式一:Scanner reader= new Scanner(System.in): int number ...

  6. MySQL 读书笔记(一)

    1 MySQL 表 1.1 索引组织表 在 InnoDB 存储引擎中,表都是根据主键顺序存放的,这种存储方式称为索引组织表. InnoDB存储引擎中,每张 MySQL表 都有一个唯一主键,如果创建表时 ...

  7. Yii2批量插入数据

    方法一 yii2一次插入多行数据  /** * @inheritdoc 批量添加 * @params $add array 添加数据 */ public function add_all($add) ...

  8. C++11强制类型转换

    C++ 强制类型转换有四种关键字:static_cast.const_cast.reinterpret_cast和dynamic_cast.它们用于不同的情况和目的,比C语言的强制类型转换更清晰和安全 ...

  9. Python中的print()语句

    Python中print()语句的相关使用 介绍 print()函数可以将输出的信息打印出来,即发送给标准输出流.Python中可以直接使用print()函数,将信息展示在控制台 基本使用方法 输出数 ...

  10. Pandas的使用

    在数据分析工作中,Pandas 的使用频率是很高的,一方面是因为 Pandas 提供的基础数据结构 DataFrame 与 json 的契合度很高,转换起来很方便.另一方面,如果日常的数据清理工作不是 ...